IslandAccord & The Key Hierarchy
How C6P v1 establishes authenticated sessions through IslandAccord handshake and derives hierarchical keys for perfect forward secrecy.
What is IslandAccord v1?
An authenticated prekey handshake protocol providing mutual authentication and session establishment.
IslandAccord v1 is C6P's canonical authenticated prekey handshake. It combines X25519 Diffie-Hellman key agreement with Ed25519 signatures to provide:
Server Blindness
Server never learns session secrets — all cryptography happens client-side
Initiator Authentication
Responder cryptographically verifies initiator identity via transcript-bound Ed25519 signature
Explicit Key Confirmation
Both parties prove they derived identical secrets before session activation
Downgrade Resistance
Protocol version and cipher suite are transcript-bound and signature-bound
IslandAccord v1 is PRODUCTION/CANONICAL/NORMATIVE. All implementations must follow the specification exactly. Any deviation breaks interoperability and security guarantees.
The DH Set: 3DH + Optional OTP
IslandAccord uses three mandatory Diffie-Hellman operations, plus an optional fourth for enhanced security.
Key Material Overview
Each party maintains long-term and ephemeral keys:
| Party | Key Type | Purpose |
|---|---|---|
| Alice (Init) | IK_dh (X25519) | Long-term identity DH key |
| Alice | IK_sig (Ed25519) | Long-term signing key for authentication |
| Alice | EK (X25519) | Fresh ephemeral per-session |
| Bob (Resp) | IK_dh (X25519) | Long-term identity DH key |
| Bob | SPK (X25519) | Signed prekey (rotated periodically) |
| Bob | OTP (X25519) | One-time prekey (single-use, optional) |
DH Computations
The 3DH set ensures that compromise of any single long-term key (IK_dh or SPK) doesn't compromise session keys. The optional OTP adds an extra layer: even if both long-term keys are compromised, past sessions with OTP remain secure.
Transcript Binding & Authentication
Every handshake parameter is bound into a canonical transcript, protecting against server splicing and downgrade attacks.
Canonical Transcript Construction
The transcript is the single source of truth for binding. It includes (in exact order):
Initiator Authentication
The initiator proves their identity by signing a hash of the transcript and session parameters:
sig_input = SHA256(
"IA_OFFER_SIG_V1" ||
transcript_hash(32) ||
U8(C6P_VERSION) ||
U8(suite_id) ||
sessionId(8) ||
A_deviceId(16) ||
B_deviceId(16)
)
offer_signature = Ed25519.Sign(A_IK_sig_priv, sig_input)
If transcript hashes don't match or signature verification fails, the handshake MUST abort immediately. The responder MUST NOT create an active session. Any mismatch indicates either an active attack or implementation bug.
Three-Tier Key Hierarchy
Root → Chain → Message: a hierarchical derivation ensuring forward secrecy and domain separation.
Tier 1: Root Key Derivation
CTX = session_id(8) || initiator_device_id(16) || responder_device_id(16)
salt_root = SHA256("C6P_ROOT_V1" || CTX || transcript_hash)
prk_root = HKDF-Extract(salt=salt_root, ikm=IKM)
root_key = HKDF-Expand(prk_root,
info="C6P_ROOT_V1" || U8(0x01),
L=32)
Tier 2: Stream Chain Keys
Each session has two independent directional chain keys:
STREAM_CTX = U8(stream_id) || U8(message_type) || U8(suite_id)
salt_chain = SHA256("C6P_CHAIN_V1" || CTX || transcript_hash)
prk_chain = HKDF-Extract(salt=salt_chain, ikm=root_key)
// For stream_id ∈ {0x01 i2r, 0x02 r2i}
CK_stream = HKDF-Expand(prk_chain,
info="C6P_CHAIN_V1" || U8(0x01) || STREAM_CTX,
L=32)
Tier 3: Per-Message Keys
salt_msg = SHA256("C6P_MSG_V1" || CTX || transcript_hash ||
STREAM_CTX || BE64(counter))
prk_msg = HKDF-Extract(salt=salt_msg, ikm=chain_key)
// Message key material (32 bytes)
mk_material = HKDF-Expand(prk_msg,
info="C6P_MSG_V1" || U8(0x01) || STREAM_CTX,
L=32)
// Next chain key (ratchet forward)
next_chain_key = HKDF-Expand(prk_msg,
info="C6P_CHAIN_V1" || U8(0x01) ||
STREAM_CTX || BE64(counter),
L=32)
Key Hierarchy Visualization
Domain Separation & Nonces
Strict domain labels prevent key reuse across contexts. Deterministic nonces are safe by design.
Domain Separation Labels
Every derivation uses a unique ASCII label to prevent cross-context key reuse:
| Label | Purpose |
|---|---|
| C6P_ROOT_V1 | Root key derivation from IKM |
| C6P_CHAIN_V1 | Stream chain keys and ratcheting |
| C6P_MSG_V1 | Per-message key material |
| C6P_NONCE_V1 | Deterministic nonce derivation |
| C6P_KC_V1 | Key confirmation tags |
| C6P_REKEY_V1 | Reserved for future rekey operations |
| C6P_EXPORT_V1 | Reserved for app-level export keys |
Deterministic Nonce Derivation
C6P uses deterministic nonces, which is safe because each message uses a unique ephemeral key:
session_binding = SHA256("C6P_BIND_V1" || session_id(8) ||
initiator_device_id(16) ||
responder_device_id(16))
prk_nonce = HKDF-Extract(salt=session_binding, ikm=mk_material)
nonce_info = "C6P_NONCE_V1" ||
U8(0x01) ||
U8(suite_id) ||
U8(message_type) ||
U8(stream_id) ||
session_id(8) ||
BE64(counter)
nonce = HKDF-Expand(prk_nonce, info=nonce_info, L=nonce_len)
// Nonce lengths:
// ChaCha20-Poly1305: 12 bytes
// XChaCha20-Poly1305: 24 bytes
// AEGIS-128L: 16 bytes
Deterministic nonces are permitted because: (1) every message uses a unique mk_material derived from (session, stream, counter), (2) nonce derivation binds all context including counter, and (3) replay detection rejects duplicate counters even if AEAD verifies.
Test Vectors & Validation
Canonical test vectors ensure bit-for-bit compatibility across Rust, Swift, and Node implementations.
Handshake Test Vectors
IslandAccord v1 includes comprehensive test vectors for cross-implementation validation:
Offer construction, SPK signature, transcript hash, offer signature
DH computation, IKM, root/KC derivation, KC1
Mirrored DH, KC1 verification, KC2 computation
Invalid signatures, transcript mismatches, encoding errors
Crypto Core Test Vectors
Key schedule and AEAD operations have dedicated vectors:
Root, chain, and per-message key derivations
Deterministic nonce for all suites (ChaCha20, XChaCha20, AEGIS)
End-to-end seal/open with AAD for each suite
Skip-window acceptance and replay rejection scenarios
Hard rule: All implementations (Rust/Swift/Node) MUST pass test vectors bit-for-bit. CI MUST fail on any mismatch. This ensures identical cryptographic behavior across platforms.
Key Takeaways
IslandAccord provides mutual authentication without server learning session secrets
3DH + optional OTP ensures compromise of single long-term key doesn't break sessions
Three-tier key hierarchy (root → chain → message) enables perfect forward secrecy
Domain separation labels prevent key reuse across different cryptographic contexts
Deterministic nonces are safe because every message uses unique ephemeral keys
Test vectors ensure interoperability across Rust, Swift, and Node implementations