Skip to main content

Overview

Crosslink uses a layered cryptographic design:

Identity keys

Each device (host or client) has a long-term Ed25519 key pair:
  • Private key: Stored in OS keychain (host) or WebCrypto non-extractable key (client)
  • Public key: Shared during pairing, used for authentication
  • Fingerprint: SHA-256 of the public key, first 16 hex chars displayed in QR code

Key generation

CLX1 handshake

The CLX1 handshake authenticates both parties and establishes a shared secret:

Handshake security properties

Session encryption

Once the handshake completes, all communication uses XChaCha20-Poly1305:

Why XChaCha20-Poly1305?

The 192-bit nonce makes random nonce generation safe without worrying about collisions, which is critical for the concurrent frame streams in Crosslink.

Key derivation

Session keys are derived using HKDF-SHA256:
The ephemeral term gives forward secrecy; the static-static term binds the session to the two paired identities, so a stolen ephemeral alone proves nothing. Each direction gets its own key, so a frame cannot be reflected back at its sender. See Protocol for the full transcript.

Secret storage encryption

Host (Node.js)

When the OS keychain is unavailable, the SDK falls back to AES-256-GCM file encryption:

Client (Browser)

When using CrosslinkClient.create(), the SDK encrypts at rest with AES-256-GCM under a WebCrypto key:
The extractable: false flag prevents JavaScript from reading the key material. However, a script running on the same origin can still use the key to decrypt data. This protects against copy-and-leave attacks (XSS exfiltration), not against code execution on the origin.

Algorithm choices and alternatives

Security limitations

  • No post-quantum resistance: Ed25519 and X25519 are vulnerable to quantum computers
  • No key escrow: Lost identity keys cannot be recovered (by design)
  • No anonymity: Device fingerprints are visible during pairing
  • No traffic analysis resistance: Frame sizes and timing are visible to network observers