Core Architecture

Encryption & One-Time Pad

Information-theoretic security, keystream expansion, and local disk-locking mechanics.

Overview

Wiltkey implements a modified, offline-first version of the One-Time Pad (OTP). Rather than carrying physical pad cassettes, client devices swap high-entropy seeds face-to-face and expand them deterministically into large files on disk. Plaintext message characters are XORed with these keystream bytes, providing perfect secrecy against passive network observers.

The primary security invariant is that a keystream byte is never used twice. To enforce this, the pad is partitioned, and offsets are tracked. Once the pad bytes are consumed, they are gone forever, and the chat naturally wilts unless recharged in person.

Keystream Expansion Flow

Seed Bytes Agreed hex + Counter 0, 1, 2, ... SHA-256 Counter Mode keystream.pad 32B blocks buffered write

How it Works

  1. Deterministic Expansion: During pairing, both clients exchange a high-entropy seed. A background task expands this seed into a pad file. The client hashes the seed concatenated with a 32-bit counter using SHA-256:
    block_bytes = SHA-256(seed_bytes || counter_bytes)
    This continues until the requested pad buffer size (e.g. 10MB) is fully populated. Writing is done in 128KB chunks, yielding to the Flutter event loop to prevent main thread stutters.
  2. XOR Encryption & Decryption: To encrypt, the client opens the pad file as a cached RandomAccessFile descriptor, jumps to the outgoing offset, reads the required number of keystream bytes, and XORs them with the plaintext bytes:
    ciphertext = plaintext XOR keystream[offset ... offset + length]
    Decryption operates identically. Because the operation is symmetrical, the receiver reads from the same offset to restore the plaintext.
  3. Envelope Absolute Offset: Every message is sent wrapped in an envelope containing the sender's identifier, the message ID, the ciphertext, and the absolute offset used. Decryption key bytes are derived solely from the envelope's offset, ensuring independence of local state.

Key Files & Symbols

File Path Symbol Name Description
lib/core/crypto/otp_service.dart WiltkeyOtpService Core utility class for keystream files and XOR crypt operations.
lib/core/crypto/otp_service.dart generateKeystreamFile() Generates the keystream_<contactId>.pad file via chunked counter-mode hashing.
lib/core/crypto/otp_service.dart xorWithKeystream() Reads pad bytes at a specific offset and performs the XOR crypt operation. Runs under a mutex lock.
lib/core/crypto/otp_service.dart reconcilePads() Deletes orphan pad files whose identifiers are no longer active contacts.

Gotchas & Edge Cases

🛑 SERIALIZED MUTEX LOCKING
Parallel reads from the same pad file will corrupt the file descriptor seek pointer. To prevent this, WiltkeyOtpService serializes all XOR reads on a per-contact lock: _locks[contactId]. A failed read will block subsequent message processing for that contact.
⚠️ BUFFER OVERFLOW
If the message size pushes the offset past the file's end, xorWithKeystream throws a pad overflow exception. The message fails to encrypt, and the pointer resets.