Resumption Handshake (0-RTT)
PALISADE v1.2 — Informative Appendix
This document describes the resumption handshake flow for PALISADE v1.2, which enables 0-RTT early data transmission with strict security restrictions.
This diagram is informative and should be read together with the normative text of the main specification (Section 12). If a discrepancy appears, the normative text wins.
1. Resumption Handshake Flow (v1.2)
The resumption handshake allows clients to resume a previous session using a resumption ticket, enabling 0-RTT early data transmission.
Client Server
------ ------
Retrieve ResumeTicket (from previous session)
Generate new ephemeral KEM keypair (K'_c)
resume_nonce = random(32)
ZeroRTTRequest (wire format):
resume_ticket (serialized)
new_ephemeral_KEM_pub (K'_c)
resume_nonce (32 bytes)
padding ------->
[Early Data] (optional, if permitted)
epoch_id = 0xFFFFFFFF
seq >= 1
Only KEEPALIVE0 or RESUME_INTENT frames
Encrypted with early data keys -------> (buffered)
Phase 1: Ticket Validation
- Verify ticket signature
- Decrypt TicketInner
- Check ticket not expired
- Atomically validate and mark used
- Extract PSK from ticket
Phase 2: Key Exchange
- Generate server_nonce'
- Generate K'_s (ephemeral KEM)
- ss'_c = Encap(K'_c) → CT'_c
- ss'_s = Encap(K'_s) → CT'_s
Phase 3: Early Data Validation
- Check max_early_data_bytes limit
- Validate early data frames (allowlist)
- Set accept_0rtt flag
Phase 4: Transcript & Signature
- Encode canonical transcript
(ClientHelloResume || ServerHelloResume)
- Sign transcript_hash
- Derive resumption keys
<------- ServerHelloResume:
version (0x12)
accept_0rtt (0x00 or 0x01)
server_nonce' (32 bytes)
K'_s (ephemeral KEM public key)
CT'_c (KEM ciphertext for client)
CT'_s (KEM ciphertext for server)
server_certificate
server_signature
padding
ss'_c = Decap(CT'_c)
ss'_s = Decap(CT'_s)
Encode canonical transcript
Verify ServerSig(transcript_hash)
Derive resumption keys via key schedule:
- Uses PSK from ticket
- Binds to transcript_hash
- Derives epoch 0 keys (C2S, S2C)
[Encrypted Data] <------> [Encrypted Data]
(epoch_id = 0, normal traffic)
=== ESTABLISHED ===Round Trips: 0-RTT (zero round trip time) for early data, 1-RTT for handshake completion
Key Points:
- Client uses a previously obtained
ResumeTicket - Fresh KEM exchange ensures forward secrecy (keys not reused)
- Ticket validation and "mark used" MUST be atomic to prevent replay
- Early data is optional and subject to strict restrictions
- Resumption key schedule uses PSK from ticket, not full handshake secrets
2. Message Structures (v1.2)
| Message | Field | Type | Notes |
|---|---|---|---|
| ZeroRTTRequest | resume_ticket | variable | Serialized ResumeTicket (opaque to client) |
| new_ephemeral_KEM_pub | variable | Fresh KEM public key (K'_c) | |
| resume_nonce | 32 bytes | Cryptographically random | |
| padding | variable | Variable-length padding | |
| ServerHelloResume | version | uint8 | 0x12 for PALISADE v1.2 |
| accept_0rtt | uint8 | 0x00 = rejected, 0x01 = accepted | |
| server_nonce' | 32 bytes | Cryptographically random | |
| K'_s | variable | Server's ephemeral KEM public key | |
| CT'_c | variable | KEM encapsulation to K'_c | |
| CT'_s | variable | KEM encapsulation to K'_s | |
| server_certificate | variable | Server's post-quantum public key | |
| server_signature | variable | Signature over canonical transcript |
3. Early Data Restrictions (Section 12.8)
Early data in PALISADE v1.2 is subject to strict security restrictions to prevent replay attacks and state corruption.
Epoch and Sequence Rules
- Early data MUST use
epoch_id = 0xFFFFFFFF(reserved) - Early data MUST use
seq >= 1 - Early data MUST use
key_phase=0(key_phase=1 is invalid) - Early data sequence numbers are independent from application traffic
Permitted Early Data Frames
Early data MUST contain only the following frame types (strict allowlist):
- KEEPALIVE0 (0x04): Zero-payload keepalive for connection liveness
- RESUME_INTENT (0x05): Signals resumption attempt without state changes
All other frame types in early data MUST be silently ignored.
Prohibited Effects
Early data MUST NOT cause:
- Epoch advancement or key derivation
- Primary path binding updates (migration)
- Replay window modifications for non-early epochs
- Durable protocol state transitions
- Non-idempotent or security-sensitive operations
Server Enforcement
- Server MUST enforce ticket-defined
max_early_data_byteslimit - Server MUST ignore client-offered early data limits (prevents replay-amplification)
- If early data is rejected, server MUST discard it without side effects
- Early data is replayable and MUST be treated as advisory only
4. Ticket Validation (Section 12.5)
The server MUST perform atomic ticket validation to prevent replay attacks:
| Validation Step | Requirement | Rationale |
|---|---|---|
| Signature Verification | Verify ticket server signature | Ensures ticket authenticity |
| Decryption | Decrypt TicketInner using server secret | Access ticket contents |
| Expiration Check | Verify ticket not expired | Enforces validity window |
| Single-Use Check | Atomically validate and mark used | Prevents replay (MUST be atomic) |
| Early Data Policy | Check server policy and ticket limits | Enforces early data restrictions |
Critical: Ticket validation and "mark used" MUST be atomic. Non-atomic implementations allow race conditions where two threads both validate a ticket before either marks it used, resulting in early data replay.
5. Resumption Key Derivation
Resumption uses a separate key schedule that binds to the resumption transcript:
Resumption Key Schedule Input:
PSK = extracted from ResumeTicket
ss'_c = Decap(CT'_c) (KEM shared secret)
resume_nonce = client nonce (32 bytes)
server_nonce' = server nonce (32 bytes)
transcript_hash = SHA3-256(canonical_ClientHelloResume || canonical_ServerHelloResume)
Key Derivation (HKDF):
resumption_secret = HKDF-Extract(salt=PSK, IKM=ss'_c)
master_secret = HKDF-Extract(salt=resumption_secret, IKM=transcript_hash)
Epoch 0 Keys:
epoch_0_keys = HKDF-Expand(master_secret, "palisade epoch 0", 64)
C2S_key = epoch_0_keys[0:32] (client-to-server)
S2C_key = epoch_0_keys[32:64] (server-to-client)
C2S_IV = HKDF-Expand(master_secret, "palisade iv c2s 0", 12)
S2C_IV = HKDF-Expand(master_secret, "palisade iv s2c 0", 12)
Note: Keys are cryptographically independent from original session keys
(beyond PSK input). Forward secrecy is maintained.PALISADE Protocol Specification Draft 00
INFORMATIONAL