Message Flow Diagrams
PALISADE v1.2 — Informative Appendix
This section provides ASCII art diagrams illustrating the message flows for the initial handshake and resumption. These diagrams show the high-level message exchange and cryptographic operations.
These diagrams are informative and should be read together with the normative text of the main specification (Section 7). If a discrepancy appears, the normative text wins.
1. Initial Handshake (1-RTT)
The full handshake establishes a new PALISADE session with mutual authentication and post-quantum key establishment.
Client Server
------ ------
Generate ephemeral KEM keypair (K_c)
ClientNonce = random(32)
ClientHello:
version (0x12)
supported_kems[] (list)
supported_sigs[] (list)
supported_aeads[] (list)
client_nonce (32 bytes)
K_c (ephemeral KEM public key)
client_certificate (mandatory)
[client_signature] (if mutual auth)
[extensions] ------->
Verify ClientHello
Select algorithms:
kem_choice, sig_choice, aead_choice
Generate K_s, ServerNonce
ss_c = Encap(K_c) → CT_c
ss_s = Encap(K_s) → CT_s
Derive shared secrets
Encode canonical transcript
Sign transcript_hash
<------- ServerHello:
version (0x12)
kem_choice
sig_choice
aead_choice
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
[extensions]
ss_c = Decap(CT_c)
ss_s = Decap(CT_s)
Encode canonical transcript
Verify ServerSig(transcript_hash)
Derive keys via key schedule:
- early_secret
- handshake_secret
- master_secret
- epoch 0 keys (C2S, S2C)
[Encrypted Data] <------> [Encrypted Data]
=== ESTABLISHED ===Round Trips: 1-RTT (one round trip time)
Key Points:
- ClientHello includes lists of supported algorithms (not single values)
- ServerHello contains single choices from the client's lists
- Version is a single byte (0x12 for PALISADE v1.2)
- Client certificate is mandatory in full handshake
- Transcript encoding excludes signatures (canonical form)
- Both endpoints derive the same keys from shared secrets
2. Resumption Handshake (0-RTT)
The resumption handshake allows clients to resume a previous session using a resumption ticket, enabling 0-RTT early data.
Client Server
------ ------
Retrieve ResumeTicket (from previous session)
Generate new ephemeral KEM keypair (K'_c)
resume_nonce = random(32)
ZeroRTTRequest:
resume_ticket
K'_c (new ephemeral KEM public key)
resume_nonce (32 bytes)
[early_data] (0-RTT data, optional) ------->
Validate ResumeTicket
Extract PSK from ticket
Generate server_nonce'
ss'_c = Encap(K'_c) → CT'_c
Encode canonical transcript
Sign transcript_hash
Derive keys using resumption
key schedule (PSK-based)
<------- ServerHelloResume:
version (0x12)
server_nonce' (32 bytes)
CT'_c (KEM ciphertext)
server_certificate
server_signature
ss'_c = Decap(CT'_c)
Encode canonical transcript
Verify ServerSig(transcript_hash)
Derive keys via resumption key schedule:
- Uses PSK from ticket
- Binds to transcript_hash
- Derives epoch 0 keys
[Encrypted Data] <------> [Encrypted Data]
=== ESTABLISHED ===Round Trips: 0-RTT (zero round trip time) for early data, 1-RTT for full resumption
Key Points:
- Client uses a previously obtained
ResumeTicket - Early data (0-RTT) can be sent with ZeroRTTRequest
- Server MUST enforce ticket-defined
max_early_data_byteslimit - Resumption key schedule uses PSK from ticket, not full handshake secrets
- Early data uses epoch ID
0xFFFFFFFFandseq >= 1 - Early data is replayable and MUST only be used for idempotent operations
3. Message Field Details (v1.2)
| Message | Field | Type | Notes |
|---|---|---|---|
| ClientHello | version | uint8 | 0x12 for PALISADE v1.2 |
| supported_kems[] | list<uint16> | Ordered list of KEM algorithm IDs | |
| supported_sigs[] | list<uint16> | Ordered list of signature algorithm IDs | |
| supported_aeads[] | list<uint16> | Ordered list of AEAD algorithm IDs | |
| client_nonce | 32 bytes | Cryptographically random | |
| K_c | variable | Client's ephemeral KEM public key | |
| ServerHello | version | uint8 | 0x12 for PALISADE v1.2 |
| kem_choice | uint16 | Selected from client's supported_kems[] | |
| sig_choice | uint16 | Selected from client's supported_sigs[] | |
| aead_choice | uint16 | Selected from client's supported_aeads[] | |
| 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 |
4. Key Derivation Flow
After the handshake completes, both endpoints derive the same keys using the key schedule:
Shared Secrets: ss_c = Decap(CT_c) (client decapsulates) ss_s = Decap(CT_s) (server decapsulates) Key Schedule Input: IKM = ss_c || ss_s || client_nonce || server_nonce (96 bytes total for ML-KEM-768) Key Derivation (HKDF): early_secret = HKDF-Extract(salt=0, IKM) handshake_secret = HKDF-Extract(salt=early_secret, IKM) master_secret = HKDF-Extract(salt=handshake_secret, IKM) 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) RID Derivation (for session identification): steering_prefix = HKDF-Expand(client_secret, "palisade rid steering", 8) privacy_suffix = HKDF-Expand(client_secret, salt || "palisade rid privacy", 14) RID = steering_prefix || rotation_epoch || privacy_suffix (24 bytes)
PALISADE Protocol Specification Draft 00
INFORMATIONAL