E2E ENCRYPTED P2P

E2E Encrypted
P2P Protocol

End-to-end encrypted node communications using X25519 key exchange and ChaCha20-Poly1305 AEAD. Every peer connection is authenticated and encrypted by default. Port 19333, magic 0x534F5354 ("SOST").

1. Cryptographic Stack

All peer-to-peer encryption uses modern, well-audited primitives. Each connection generates fresh ephemeral keys — no long-term key material is reused across sessions.

X25519 Key Exchange

256-bit ephemeral Curve25519 keypairs generated per connection. Each side produces a 32-byte public key and derives a 32-byte shared secret via Diffie-Hellman. The ephemeral nature ensures forward secrecy — compromising one session reveals nothing about past or future sessions.

ChaCha20-Poly1305 AEAD

Authenticated Encryption with Associated Data. 256-bit key, 96-bit nonce (64-bit counter + 32-bit zero padding), 16-byte authentication tag per message. Provides both confidentiality and integrity — any tampering is detected and the message is rejected.

HKDF-SHA256 Key Derivation

The shared secret from X25519 is never used directly. HKDF-SHA256 derives separate send and receive keys using distinct labels:

send_key = HKDF-SHA256(shared_secret, label="sost-p2p-key-a")
recv_key = HKDF-SHA256(shared_secret, label="sost-p2p-key-b")

The initiator uses key-a to send and key-b to receive; the responder reverses this. Each direction has an independent 64-bit nonce counter starting at 0, incremented per message.

2. Encryption Modes

Node operators can configure their encryption policy via the --encryption flag. Three modes are supported:

ModeFlagBehavior
off--encryption offNo encryption. Messages sent in plaintext. EKEY exchange is skipped. Useful for debugging or trusted LAN environments only.
on (default)--encryption onEncryption enabled. Node sends EKEY and encrypts if the peer reciprocates. Falls back to plaintext if the peer does not support encryption. Recommended for most deployments.
required--encryption requiredEncryption mandatory. Node disconnects any peer that does not complete the EKEY handshake. Maximum security — no plaintext connections allowed.
Default: Encryption is ON by default. All official SOST nodes ship with encryption enabled. The off mode exists for development and diagnostics only.

3. Handshake Flow

The connection handshake establishes encryption before any protocol data is exchanged. The EKEY exchange happens first, then VERS/VACK, then all subsequent messages are encrypted.

    Initiator                          Responder
    ─────────                          ─────────
        │                                  │
        │──── EKEY (pubkey_a, 32B) ───────>│
        │                                  │
        │<─── EKEY (pubkey_b, 32B) ────────│
        │                                  │
        │  [Both derive shared_secret via X25519]
        │  [HKDF-SHA256 → send_key, recv_key]
        │                                  │
        │════════ ENCRYPTED CHANNEL ═══════│
        │                                  │
        │──── VERS (version, height) ─────>│
        │                                  │
        │<─── VACK (version, height) ──────│
        │                                  │
        │══ HANDSHAKE COMPLETE ════════════│
        │                                  │
        │  [GETB, BLCK, TXXX, PING/PONG]  │
        │  [All messages encrypted]        │
        │                                  │

Timeout: If EKEY is not received within 10 seconds of connection, the peer is disconnected. If VERS/VACK is not completed within 30 seconds, the peer is disconnected and scored for misbehavior.

4. Protocol Commands

All commands use the same 12-byte frame header. Command names are 4-byte ASCII identifiers padded with null bytes if shorter.

CommandDirectionPayloadPurpose
EKEYBoth32-byte X25519 public keyExchange ephemeral encryption keys. Must be the first message on a new connection.
ENCRBoth1-byte mode flagDeclare encryption capability and mode. Sent alongside or after EKEY.
VERSInitiator → ResponderProtocol version (u32), best height (u64), genesis hash (32B)Version handshake. Initiator announces its chain state.
VACKResponder → InitiatorProtocol version (u32), best height (u64), genesis hash (32B)Version acknowledgement. Responder confirms compatibility.
GETBEitherStart height (u64), count (u16)Request a batch of blocks starting from the given height. Max 100 blocks per request.
BLCKEitherSerialized block dataDeliver a single block in response to GETB, or relay a newly mined block.
DONEEitherNone (0 bytes)Signal end of a GETB batch. Sender has no more blocks to send for this request.
TXXXEitherSerialized transactionRelay an unconfirmed transaction for mempool inclusion.
PINGEither8-byte nonceKeepalive probe. Peer must respond with PONG containing the same nonce.
PONGEither8-byte nonce (echo)Keepalive response. Nonce must match the corresponding PING.

5. Block Sync Flow

When a node discovers a peer with a higher best height, it initiates block synchronization. Blocks are requested and delivered in batches of up to 100.

    Node (behind)                      Peer (ahead)
    ─────────────                      ────────────
        │                                  │
        │  [VERS/VACK: peer height > ours]│
        │                                  │
        │── GETB (height=1000, n=100) ────>│
        │                                  │
        │<──── BLCK (block 1000) ──────────│
        │<──── BLCK (block 1001) ──────────│
        │<──── BLCK (block 1002) ──────────│
        │          ...                     │
        │<──── BLCK (block 1099) ──────────│
        │<──── DONE ──────────────────────│
        │                                  │
        │  [Validate & connect blocks]    │
        │                                  │
        │── GETB (height=1100, n=100) ────>│
        │                                  │
        │<──── BLCK (block 1100) ──────────│
        │          ...                     │
        │<──── BLCK (block 1155) ──────────│
        │<──── DONE ──────────────────────│
        │                                  │
        │  [Sync complete: heights match]  │
        │                                  │

Sync Parameters

ParameterValueDescription
Batch size100 blocks maxMaximum blocks per GETB request
Timeout30 secondsPer-batch timeout. If no BLCK/DONE received within 30s, retry or disconnect.
Retries3Maximum retry attempts per batch before disconnecting the peer.
Rate (sync)5,000 blocks/minElevated rate limit during active sync to allow fast catch-up.
Rate (steady)50 blocks/minNormal rate limit after sync is complete.

6. Encrypted Frame Format

Once the EKEY handshake is complete, all messages are wrapped in an encrypted frame. The frame header is sent in plaintext (so the receiver knows how much ciphertext to read), but the payload is encrypted.

┌──────────────────────────────────────────────────────────┐
│                    ENCRYPTED FRAME                        │
├──────────┬──────────┬─────────────┬─────────────┬────────┤
│  magic   │   cmd    │ payload_len │ ciphertext  │  tag   │
│  4 bytes │ 4 bytes  │   4 bytes   │   N bytes   │ 16 B   │
├──────────┼──────────┼─────────────┼─────────────┼────────┤
│ 0x534F5354│ "BLCK"  │  len(ct)    │ ChaCha20    │ Poly   │
│  "SOST"  │ "GETB"  │  + 16       │  encrypted  │ 1305   │
│          │ "TXXX"  │             │  payload    │  MAC   │
│          │  etc.   │             │             │        │
└──────────┴──────────┴─────────────┴─────────────┴────────┘

Header (12 bytes, plaintext):
  magic:       0x534F5354 ("SOST") — identifies SOST protocol frames
  cmd:         4-byte ASCII command identifier
  payload_len: total encrypted payload size including 16-byte auth tag

Encrypted payload:
  ciphertext:  ChaCha20 encrypted message body (N bytes)
  tag:         Poly1305 16-byte authentication tag

The payload_len field includes the 16-byte Poly1305 tag, so actual plaintext length = payload_len - 16. Maximum payload_len = 4,194,304 bytes (4 MB). Any frame exceeding this is rejected and the peer is banned.

Nonce Management

Each direction maintains an independent 64-bit nonce counter, starting at 0 and incremented after each message. The nonce is not transmitted — both sides track it implicitly. If counters desync (decryption fails), the connection is terminated immediately.

8. DoS Protection

The P2P layer implements multiple defensive mechanisms to prevent denial-of-service attacks, resource exhaustion, and misbehaving peers.

Rate Limiting

ContextRateDescription
Steady state50 blocks/minNormal operation after initial sync. Prevents block flooding.
Active sync5,000 blocks/minElevated limit during initial block download to allow fast catch-up.
Max message size4 MBAny frame with payload_len > 4,194,304 bytes is rejected immediately.

Peer Limits

ParameterValueDescription
Max inbound32Maximum simultaneous inbound peer connections.
Per-IP limit2Maximum connections from a single IP address.
IP cooldown30 secondsMinimum time between connection attempts from the same IP after disconnect.

Ban Scoring

Each peer accumulates misbehavior points for protocol violations. When a peer reaches the ban threshold, it is disconnected and its IP is blocked for the ban duration.

ParameterValue
Ban threshold100 points
Ban duration24 hours

Misbehavior examples:

Defense in depth: Rate limiting, peer limits, ban scoring, and encryption together provide layered protection. An attacker must overcome all layers simultaneously, making resource exhaustion attacks impractical against well-connected SOST nodes.

E2E Encryption (Phase XIII+XIV)

Deal channels now use true end-to-end encryption. The relay is blind transport — it cannot read message content.

Cryptographic Stack

Identity & SigningED25519 (permanent, signs everything)
Key AgreementX25519 (per-deal ephemeral key exchange)
Key DerivationHKDF-SHA256 with directional labels
Payload EncryptionChaCha20-Poly1305 AEAD
Replay ProtectionSequence numbers + sliding window + nonce dedup

Prekey Architecture

Signed PrekeyX25519, rotated every 7 days, signed by ED25519 identity key
One-Time PrekeysConsumed once per session, replenished in batches of 10
Async BootstrapSender initiates encrypted channel while recipient is offline
Offline DeliveryStore-and-forward queue, 7-day TTL, delivery receipts

What the Relay Can and Cannot Do

CANStore, forward, route encrypted envelopes
CANVerify header signatures for routing authentication
CANQueue messages for offline recipients
CANNOTRead message payload content
CANNOTFabricate valid encrypted messages
CANNOTReuse one-time prekeys without detection
Not yet implemented: Double ratchet (continuous forward secrecy), post-compromise security, deniability. These are planned for a future phase. Current test coverage: 231 TypeScript tests, 0 failures.
🎮