Cryptography Reference

Concise reference for all cryptographic operations in Vauchi.

Algorithms

PurposeAlgorithmLibraryNotes
SigningEd25519ed25519-dalekIdentity, registry
Key ExchangeX25519x25519-dalekX3DH + identity binding
Sym. EncryptXChaCha20-Poly1305chacha20poly1305192-bit nonce
Forward SecrecyDouble Ratchethkdf + hmacChain limit 2000
Key DerivationHKDF-SHA256hkdfRFC 5869
Password KDFArgon2idargon2m=64MB, t=3, p=4
CSPRNGOsRngrandOS entropy
TLSTLS 1.2/1.3rustls (aws-lc-rs)Relay transport + SPKI pinning
IP PrivacyOHTTP (RFC 9458)ohttp (rust-hpke)Unlinks client IP from request

Post-quantum (planned, ADR-060 / ADR-062). Key agreement is classical-only today. A hybrid X25519 + ML-KEM-768 upgrade is planned to close the harvest-now-decrypt-later gap on relay-carried ciphertext; signatures (Ed25519) migrate later since they are not harvest-now-forge-later.

Key Types

Identity Keys

KeyTypeSizePurpose
Master SeedSymmetric256-bitRoot of all keys
Signing KeyEd2551932+64 bytesIdentity, signatures
Exchange KeyX2551932 bytesKey agreement

Storage Keys (Shredding Hierarchy)

┌──────────────────────────────────────────────┐                                                                                                            
│                                              │                                                                                                            
│            Master Seed (256-bit)             │                                                                                                            
│                                              │                                                                                                            
└──────────────────────────────────────────────┘                                                                                                            
                        │                                                                                                                                   
                        │                                                                                                                                   
                        ├─────────────────────────────────────────────────────┬─────────────────────────────────────────────────────┐                       
                        │                                                     │                                                     │                       
                        ▼                                                     ▼                                                     ▼                       
┌──────────────────────────────────────────────┐     ┌─────────────────────────────────────────────────┐     ┌─────────────────────────────────────────────┐
│                                              │     │                                                 │     │                                             │
│                                              │     │                                                 │     │                                             │
│             Identity Signing Key             │     │                   Exchange Key                  │     │          SMK (Shredding Master Key)         │
│        raw seed (Ed25519 requirement)        │     │ HKDF(seed, "Vauchi_Exchange_Seed_v2") │     │ HKDF(seed, "Vauchi_Shred_Key_v2") │
│                                              │     │                                                 │     │                                             │
└──────────────────────────────────────────────┘     └─────────────────────────────────────────────────┘     └─────────────────────────────────────────────┘
                                                                                                                                    │                       
                                                                                                                                    │                       
                        ┌─────────────────────────────────────────────────────┬─────────────────────────────────────────────────────┤                       
                        │                                                     │                                                     │                       
                        ▼                                                     ▼                                                     ▼                       
┌──────────────────────────────────────────────┐     ┌─────────────────────────────────────────────────┐     ┌─────────────────────────────────────────────┐
│                                              │     │                                                 │     │                                             │
│         SEK (Storage Encryption Key)         │     │          FKEK (File Key Encryption Key)         │     │               Per-Contact CEK               │
│ HKDF(SMK, "Vauchi_Storage_Key_v2") │     │   HKDF(SMK, "Vauchi_FileKey_Key_v2")  │     │          random 256-bit per contact         │
│        encrypts all local SQLite data        │     │            encrypts file key storage            │     │   encrypts individual contact's card data   │
│                                              │     │                                                 │     │                                             │
└──────────────────────────────────────────────┘     └─────────────────────────────────────────────────┘     └─────────────────────────────────────────────┘

HKDF Convention: Master seed as IKM, no salt, domain string as info. All derivations use HKDF::derive_key(None, &seed, info).

HKDF Context Strings:

ContextUsage
Vauchi_Exchange_Seed_v2Exchange key derivation from master seed
Vauchi_Shred_Key_v2SMK derivation from master seed
Vauchi_Storage_Key_v2SEK derivation from SMK
Vauchi_FileKey_Key_v2FKEK derivation from SMK
vauchi-x3dh-symmetric-v2X3DH transcript binding (4-key HKDF info)
vauchi-x3dh-key-v2X3DH key agreement derivation
Vauchi_Root_RatchetDH ratchet root key step
Vauchi_Message_KeySymmetric ratchet message key
Vauchi_Chain_KeySymmetric ratchet chain key advance
Vauchi_AnonymousSender_v2Anonymous sender ID derivation
Vauchi_Mailbox_v1Contact mailbox token (daily rotation)
Vauchi_DeviceSyncDevice-to-device encryption key derivation
Vauchi_DeviceSync_v1Device sync self-token (daily rotation)

Ratchet Keys

KeyTypeLifecycle
Root Key32 bytesUpdated on DH ratchet
Chain Key32 bytesAdvances with each message
Message Key32 bytesSingle-use, deleted after

Ciphertext Format

algorithm_tag (1 byte) || nonce || ciphertext || tag
TagAlgorithmNonceNotes
0x01AES-256-GCM12 bytesRemoved — no longer supported
0x02XChaCha20-Poly130524 bytesDefault since v0.1.2
0x03XChaCha20-Poly1305 + AD24 bytesDouble Ratchet (header-bound)

Tag 0x03 binds message header as AEAD associated data to prevent relay manipulation.

Message Padding

All messages padded to fixed buckets before encryption:

BucketSizeTypical Content
Small256 BACK, presence, revocation
Medium-Small512 BShort card deltas, single-field updates
Medium1 KBCard deltas, small updates
Large4 KBMedia references, large payloads

Messages > 4 KB: rounded to next 256-byte boundary.

Format: [4-byte BE length prefix] [plaintext] [random padding]

X3DH Key Agreement

Full X3DH with identity binding (no signed pre-keys):

QR / Mutual Exchange (Symmetric)

Both sides:
  ephemeral ← generate X25519 keypair
  shared_bytes ← DH(our_ephemeral_secret, their_ephemeral_public)

  // Transcript binding: all four public keys sorted lexicographically
  // and appended to info, preventing identity misbinding attacks
  info ← "vauchi-x3dh-symmetric-v2" || sort(id_lo, id_hi) || sort(eph_lo, eph_hi)
  shared ← HKDF(ikm=shared_bytes, salt=None, info=info)

NFC/BLE Exchange

Same as Mutual QR — fresh ephemeral keys on both sides, HKDF-derived shared secret.

Double Ratchet

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│                                                                         DOUBLE RATCHET                                                                          │
│                                                                                                                                                                 │
│                                                                                                                                                                 │
│ ┌──────────────────────────────────────┐ ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐          │
│ │             DH RATCHET               │ │                                             SYMMETRIC RATCHET                                             │          │
│ │                                      │ │                                                                                                           │          │
│ │                                      │ │                                                                                                           │          │
│ │ ┌──────────────────────────────────┐ │ │ ┌─────────────────────────────────────────────────┐     ┌───────────────────────────────────────────────┐ │          │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │          │
│ │ │ our_dh_secret × their_dh_public  │ │ │ │                    chain_key                    │     │                       DH                      ├─┼──────┐   │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │      │   │
│ │ └─────────────────┬────────────────┘ │ │ └─────────────────────────────────────────────────┘     └───────────────────────────────────────────────┘ │      │   │
│ │                   │                  │ │                          │                                                                                │      │   │
│ │                   │                  │ │                          │                                                                                │      │   │
│ │                   │                  │ │                          ├──────────────────────────────────────────────────────┐                         │      │   │
│ │                   │                  │ │                          │                                                      │                         │      │   │
│ │                   ▼                  │ │                          ▼                                                      ▼                         │      ▼   │
│ │ ┌──────────────────────────────────┐ │ │ ┌─────────────────────────────────────────────────┐     ┌───────────────────────────────────────────────┐ │   ┌────┐ │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │   │    │ │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │   │    │ │
│ │ │  HKDF(root_key, shared_secret,   │ │ │ │ HKDF(chain_key, "Vauchi_Message_Key") │     │ HKDF(chain_key, "Vauchi_Chain_Key") │ │   │ SR │ │
│ │ │ "Vauchi_Root_Ratchet") │ │ │ │            → message_key (single use)           │     │                → next_chain_key               │ │   │    │ │
│ │ │                                  │ │ │ │                                                 │     │                                               │ │   │    │ │
│ │ └─────────────────┬────────────────┘ │ │ └─────────────────────────────────────────────────┘     └───────────────────────────────────────────────┘ │   └────┘ │
│ │                   │                  │ │                                                                                                           │          │
│ │                   │                  │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────┘          │
│ │                   │                  │                                                                                                                        │
│ │                   │                  │                                                                                                                        │
│ │                   ▼                  │                                                                                                                        │
│ │ ┌──────────────────────────────────┐ │                                                                                                                        │
│ │ │                                  │ │                                                                                                                        │
│ │ │  "[new_root_key, new_chain_key   │ │                                                                                                                        │
│ │ │                                  │ │                                                                                                                        │
│ │ └──────────────────────────────────┘ │                                                                                                                        │
│ │                                      │                                                                                                                        │
│ └──────────────────────────────────────┘                                                                                                                        │
│                                                                                                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Limits:

  • Max chain generations: 2000
  • Max skipped keys stored: 1000
  • Message key deleted immediately after use

Ratchet Message (Authenticated, Not Encrypted Header)

#![allow(unused)]
fn main() {
RatchetMessage {
    dh_public: [u8; 32],      // Current DH public key
    dh_generation: u32,       // DH ratchet step counter
    message_index: u32,       // Message index in current chain
    previous_chain_length: u32, // Messages sent in previous chain
    ciphertext: Vec<u8>,      // Encrypted payload
}
}

Header (44 bytes) bound as AEAD associated data (tag 0x03).

Backup Format

v3 — Full Backup (Current)

[0x03] || salt(16) || ciphertext
  • Key derivation: Argon2id (m=64MB, t=3, p=4), followed by HKDF-SHA256 with domain separation b"vauchi-backup-v3"
  • Cipher: XChaCha20-Poly1305
  • Plaintext: JSON FullBackupEnvelope
FullBackupEnvelope {
  version, created_at,
  sections: {
    identity:  { display_name, master_seed_b64, device_index, device_name },
    contacts:  [ ... ],
    own_card:  ContactCard?,     // optional
    labels:    [ LabelSection ]  // optional
  }
}

The v3 envelope carries the full account: identity master seed, contacts, your own card, and labels. Per-contact Double Ratchet state is not included — it is ephemeral by design and re-established on the next sync after restore. Source: core/vauchi-core/src/backup/full_backup.rs.

v2 — Identity-Only (Legacy)

[0x02] || salt(16) || ciphertext
  • Key derivation: Argon2id (m=64MB, t=3, p=4)
  • Cipher: XChaCha20-Poly1305
  • Plaintext: display_name_len(4) || display_name || master_seed(32) || device_index(4) || device_name_len(4) || device_name
  • Status: Superseded by v3. Read path retained for migration; new backups are written as v3.

v1 (Removed)

salt(16) || nonce(12) || ciphertext || tag(16)
  • Key derivation: PBKDF2-HMAC-SHA256
  • Cipher: AES-256-GCM
  • Status: Removed from codebase. Documented for format reference only.

Transport Encryption

The shipping client (vauchi-core) talks to the relay over the HTTP v2 protocol: a synchronous request/response API (suited to contact-card sync, not real-time chat), carried over TLS 1.3 with SPKI certificate pinning. The WebSocket + Noise modules were removed from the client in favour of HTTP v2 (core/vauchi-core/src/network/mod.rs: "websocket and noise modules removed — relay uses HTTP v2 transport").

Oblivious HTTP (OHTTP, RFC 9458)

To unlink the client's IP address from its requests, HTTP v2 requests are encapsulated with OHTTP and relayed through an independent OHTTP gateway:

  • Gateway key config is fetched from GET /v2/ohttp-key (application/ohttp-keys).
  • Each request is encapsulated single-use (OhttpClient::encapsulate), HPKE-sealed to the gateway.
  • ADR-037 requires the gateway operator and the relay operator to be distinct entities — the gateway sees the client IP but not the (sealed) request; the relay sees the request but only the gateway's IP. Neither sees both.

The protocol supports this separation, but the current Vauchi deployment operates both hops. Using separate hosts reduces single-host compromise risk; it does not provide the distinct-operator guarantee. This limitation concerns IP and timing metadata, not the end-to-end encryption of contact-card contents.

Source: core/vauchi-core/src/network/http_transport.rs, core/vauchi-core/src/network/ohttp_client.rs, relay/src/ohttp_gateway.rs.

Mailbox Routing (Daily-Rotating Tokens)

The relay routes without identities. Messages are addressed to daily-rotating mailbox tokens, HKDF(shared_key, day_epoch, "Vauchi_Mailbox_v1"), which both parties derive independently for a given UTC day (day_epoch = unix_time / 86400). Clients register today's and yesterday's tokens to absorb clock skew. Source: core/vauchi-core/src/network/mailbox_token.rs.

Noise NK (Removed — Historical)

The original relay transport was a Noise NK inner layer (Noise_NK_25519_ChaChaPoly_BLAKE2s) over WebSocket, as defense-in-depth inside TLS. It is fully retired: the shipping client migrated to HTTP v2 + OHTTP (ADR-004, superseded), and the relay's Noise NK implementation — the snow dependency and the Noise transport — was deleted. The relay keeps only its X25519 identity keypair, from which its Ed25519 federation signing key is derived; no client or relay performs a Noise handshake. (A residual relay identity public key field still rides in the discovery payload, unused by clients.)

Security Properties

PropertyMechanism
ConfidentialityXChaCha20-Poly1305 encryption
IntegrityAEAD authentication tag
AuthenticityEd25519 signatures
Forward SecrecyDouble Ratchet, message keys deleted
Break-in RecoveryDH ratchet with ephemeral keys
No Nonce ReuseRandom 24-byte nonces
Memory Safetyzeroize on drop for all keys
Traffic Analysis PreventionStandardized bucket-size message padding
Replay PreventionDouble Ratchet counters
Transport EncryptionTLS 1.3 + SPKI certificate pinning (HTTP v2)
Network-Location PrivacyOblivious HTTP (RFC 9458), independent gateway
Unlinkable RoutingDaily-rotating mailbox tokens

Source Files

Paths are in the core repository.