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 only

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, SP-33)
Vauchi_DeviceSyncDevice-to-device encryption key derivation
Vauchi_DeviceSync_v1Device sync self-token (daily rotation, SP-33)

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

v2 (Current)

[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

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 (Noise NK)

Client-to-relay communication uses a Noise NK inner transport layer as defense-in-depth inside TLS.

Pattern

Noise_NK_25519_ChaChaPoly_BLAKE2s

NK means the relay's static public key is known to the client before the handshake (distributed via the /info HTTP endpoint as base64url). The client does not authenticate to the relay (anonymous initiator).

Handshake

Pre-message:  <- s   (relay's static public key, known to client)
Message 1:    -> e, es   (client sends ephemeral, DH with relay static)
Message 2:    <- e, ee   (relay sends ephemeral, DH between ephemerals)

After Message 2, both sides derive symmetric keys for bidirectional encryption.

v2 Framing

v2 (Noise-encrypted) connections are identified by a 3-byte magic prefix:

0x00 'V' '2' || 48-byte NK handshake message

All connections use the 3-byte 0x00 V 2 prefix followed by the NK handshake. After the handshake completes, all subsequent WebSocket frames are Noise-encrypted.

Why NK?

PropertyBenefit
No client authRelay cannot link connections to identities
Forward secrecyPast sessions cannot be decrypted
Relay authClient verifies relay via static key
Defense-in-depthRouting metadata encrypted if TLS fails

Configuration

VariableDefaultDescription
RELAY_REQUIRE_NOISEfalseRemoved — NK always on

The relay's Noise keypair is auto-generated on first start and persisted to {data_dir}/relay_noise_key.bin.

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 EncryptionNoise NK inside TLS (defense-in-depth)

Source Files

ModulePath
Key Derivationcore/vauchi-core/src/crypto/kdf.rs
Signingcore/vauchi-core/src/crypto/signing.rs
Encryptioncore/vauchi-core/src/crypto/encryption.rs
Double Ratchetcore/vauchi-core/src/crypto/ratchet.rs
Chain Keycore/vauchi-core/src/crypto/chain.rs
CEKcore/vauchi-core/src/crypto/cek.rs
Shreddingcore/vauchi-core/src/crypto/shredding.rs
Password KDFcore/vauchi-core/src/crypto/password_kdf.rs
X3DHcore/vauchi-core/src/exchange/x3dh.rs
X3DH Session (Symmetric)core/vauchi-core/src/exchange/session.rs
Paddingcore/vauchi-core/src/crypto/padding.rs