advanced 30 min

The peer handshake

How two nodes prove identity and bind a session cryptographically during the peer handshake.

Prerequisites

Complete these before starting this module:

What you'll learn

  • Explain the shared-value derivation from the TLS session.
  • Understand how nodes prove key ownership and prevent MITM / replay.
  • Locate `makeSharedValue` / `buildHandshake` / `verifyHandshake`.
Complete this module by self-assessment and a quiz. Jump to assessment

Introduction

≈30 min · Advanced · builds on Transaction signing & verification

Before two nodes trust each other, they have to prove who they are, cryptographically. In this module you'll learn how the peer handshake derives a shared value from the TLS session to bind the connection, how each node proves it owns its key, and how that stops man-in-the-middle and replay attacks cold. It's the moment a stranger becomes a trusted peer.

The Challenge

In brief: two strangers must prove their identities and confirm they share one secure channel.

What We Need to Prove

The challenge: Node A and Node B each claim a public key and demand the other prove possession of the matching secret key; both must prove identity (I own this key), liveness (this is happening now, not a replay), and session binding (this proof is for this connection only)

Attack Scenarios to Prevent

1. Man-in-the-Middle (MITM)

The man-in-the-middle attack: Evil sits between Node A and Node B, intercepting and relaying messages, so A thinks it is talking to B and B thinks it is talking to A

2. Replay Attack

Evil records handshake messages from previous session
Replays them to impersonate Node A

3. Self-Connection

Node A tries to connect to itself through network loop
Could cause infinite recursion/waste resources

4. Network Mismatch

Mainnet node connects to testnet node
Could cause confusion/invalid transactions

The Solution: Cryptographic Handshake

High-Level Flow

The solution in five steps: establish SSL/TLS, extract the session's shared value, sign it with each node's secret key, exchange the signatures in HTTP headers with network-ID and self-connection checks, and the connection is authenticated

The Shared Value: Session Binding

In brief: a value derived from the TLS session that ties the handshake to this exact connection.

Why We Need It

Signatures alone aren't enough:

// ❌ INSECURE: Sign static message
auto sig = sign(pk, sk, "I am Node A");
// Problem: Can be replayed in future connections!

We need something unique to THIS specific connection:

// ✅ SECURE: Sign session-specific value
auto sharedValue = deriveFromSSL(session);
auto sig = sign(pk, sk, sharedValue);
// Can only be used for THIS session

Implementation

Hashing the SSL Finished Messages

What are "Finished" messages?

In the SSL/TLS handshake, both parties send a "Finished" message that contains:

  • A hash of all previous handshake messages
  • A MAC (Message Authentication Code) proving they know the session keys

These messages are:

  • Unique per session: Different for every SSL connection
  • Unpredictable: Depend on random values exchanged during handshake
  • Authenticated: Part of SSL's own security

Properties of the Shared Value

The shared value's five properties: session-specific, unpredictable before the handshake completes, mutual (both nodes contribute via XOR), independently verifiable, and bound to this exact SSL session

Key idea. Binding the proof to the TLS session is the trick that defeats man-in-the-middle: a stolen handshake is worthless on any other connection.

Building the Handshake

In brief: what each side sends to prove it owns its node key.

Header Fields Explained

Network-ID:

// Mainnet: 0
// Testnet: 1
// Devnet:  2, etc.

// Prevents nodes from different networks connecting

Network-Time:

// Current time in milliseconds since epoch
// Helps detect replayed handshakes (timestamps too old)
// Not strictly enforced (clocks may be slightly off)

Public-Key:

// Node's public key in Base58 format
// Example: nHUpcmNsxAw47yt2ADDoNoQrzLyTJPgnyq5o3xTmMcgV8X3iVVa7
// Used to verify the signature

Session-Signature:

// Signature of the shared value
// Proves: "I have the secret key for this public key"
//     AND "I'm participating in THIS specific SSL session"

Instance-Cookie:

// Random value generated on node startup
// If we receive our own cookie back → we're connecting to ourselves!

Server-Domain (optional):

// Domain name like "ripple.com"
// Can be verified against validator list
// Helps with node identification

Verifying the Handshake

Complete Handshake Flow

The complete handshake sequence: TCP connection, SSL/TLS handshake with Finished messages, both compute shared = sha512Half(finishedA XOR finishedB), Node A sends the HTTP Upgrade request with Public-Key, Session-Signature, Network-ID and Instance-Cookie headers, Node B verifies and responds with its own headers, Node A verifies back, and the XRPL protocol begins

Security Properties

In brief: what the handshake guarantees: mutual authentication, session binding, no replay, no MITM.

1. Mutual Authentication

Both nodes prove they possess their private keys:

Node A proves: "I have SK_A"
Node B proves: "I have SK_B"

2. Session Binding

Signatures are specific to this connection:

Signature valid ONLY for THIS SSL session
Cannot be replayed in different session

3. Replay Prevention

sharedValue = derived from THIS session's SSL handshake
Old signatures from previous sessions won't verify

4. MITM Prevention

Attacker cannot forge signatures without private keys
SSL provides encryption, handshake provides authentication

5. Self-Connection Prevention

if (theirCookie == myCookie) {
    // We're talking to ourselves!
    reject();
}

6. Network Segregation

if (theirNetwork != myNetwork) {
    // Different networks (mainnet vs testnet)
    reject();
}

Attack Analysis

Can an attacker impersonate Node A?

No:

Attacker needs to:
1. Know Node A's secret key (impossible - properly secured)
2. Sign the shared value (requires secret key)
Without SK_A, cannot create valid signature

Can an attacker replay old handshakes?

No:

Shared value is different for each SSL session
Old signature: sign(SK, oldSharedValue)
New session:   verify(PK, newSharedValue, oldSignature)
Result: Verification fails (different shared values)

Can an attacker perform MITM?

Very difficult:

SSL provides:
- Encryption (attacker can't read/modify)
- Certificate validation (can detect impersonation)

Application handshake provides:
- Signature verification (requires private keys)
- Session binding (tied to SSL session)

Attacker would need to:
1. Break SSL (extremely difficult)
2. AND forge signatures (impossible without keys)

Implementation Best Practices

DO:

DON'T:

// ❌ Don't skip signature verification
if (config.TRUSTED_NODE) {
    // Skip verification - WRONG!
}

// ❌ Don't ignore network ID
// connect();  // Oops, might be wrong network

// ❌ Don't allow self-connections
// They waste resources and can cause issues

Performance Considerations

// Handshake happens once per connection
// Not a performance bottleneck

Typical handshake time:
- SSL/TLS handshake:      50-100ms
- Shared value computation: <1ms
- Signature creation:      <1ms
- Signature verification:  <1ms
Total:                    ~50-100ms

// Amortized over connection lifetime (hours/days)
// Cost is negligible

Summary

This module covered how two strangers become trusted peers. The handshake derives a shared value from the TLS session, binding the proof to that exact connection, and each node proves it owns its node key. Together these give mutual authentication and defeat man-in-the-middle and replay attacks: a stolen handshake is worthless on any other connection.

To remember:

  • Goal: prove node-key ownership AND bind the proof to this exact TLS session
  • Shared value = a hash derived from the TLS Finished messages (makeSharedValue)
  • Each side signs the shared value with its node key: buildHandshake / verifyHandshake
  • This defeats MITM and replay: a captured handshake is worthless on any other connection
  • Self-connection is detected and dropped
  • It happens during the HTTP Upgrade to the peer protocol
  • Code: src/xrpld/overlay/detail/Handshake.cpp
  • Watch out: the identity proved here is the NODE key (n...), not a validator's master key; do not conflate the two

Next up. The crypto all works; now learn where it quietly breaks. Next, the tour every reviewer needs: crypto pitfalls and performance.

Assignments

0 of 2 complete

Unlocks

Finishing this module opens up:

XRPL Academy © 2026