veilvoice-crypto

veilvoice-crypto

Argon2id KDF, X25519+ML-KEM-768 hybrid KEM, XChaCha20-Poly1305 at-rest encryption and page-locked amnesic secrets for VeilVoice.

reference · the same page on GitHub

Key derivation, post-quantum-hybrid key agreement, authenticated encryption and amnesic secret storage for VeilVoice.

What this crate is for

veilvoice_core(veilvoice-core.html) makes a voice unrecognisable; it does not hide the words, and it is not meant to. When a recording needs to stay secret as well, at rest on disk or in transit to someone else, that is this crate's job.

  • kdf, Argon2id, for turning a password into a key.
  • hybrid, X25519 + ML-KEM-768, so a recording captured today is not readable by a quantum adversary tomorrow.
  • aead, XChaCha20-Poly1305, with random nonces and authenticated associated data.
  • container, the .veil file format that ties the three together.
  • amnesia, page-locked, zeroizing, constant-time-comparable secrets.
  • shred, secure erasure, and an honest account of what that is worth on flash storage.
  • privatefile, writing a file that is owner-only from the moment it exists, rather than world-readable until a second syscall tightens it.
  • lock, the application lock: an Argon2id verifier with a rate limit, which protects against casual access and says so rather than pretending to be tamper-proof.

Threat model, stated plainly

This crate protects data at rest and in transit against an attacker who later obtains the file, including one who stores it until quantum hardware exists. It does not protect against an attacker who is already running code as you, or who can read this process's memory: page-locking keeps keys out of the swap file, not out of a debugger. Hibernation writes RAM to disk wholesale and defeats locking entirely.

Example

use veilvoice_crypto::{container, kdf};

// Cheap parameters so the doctest is fast; real callers use the default.
let params = kdf::KdfParams::weak_for_tests();
let sealed = container::seal_with_password(b"pass phrase", b"audio bytes", params)?;
assert_eq!(container::open_with_password(b"pass phrase", &sealed)?, b"audio bytes");
assert!(container::open_with_password(b"wrong", &sealed).is_err());

VeilVoice contains no unsafe code at all, including the page-locking in amnesia, which goes through a safe wrapper.

In plain words

This is the locking.

Two separate things use it. Recordings are sealed on your disk so that somebody who takes the disk cannot listen to them, and the app can be put behind a password so somebody who picks up your unlocked computer cannot open it.

Those two passwords are deliberately different. Opening the program should not be the same act as unsealing everything it has ever written.

The maths is chosen to be slow to guess and to still be safe if somebody one day builds a quantum computer. Nothing is sent anywhere; the sealing happens on your machine and the key is made from your password each time.

HOW THE CRATE FITS TOGETHER

Every arrow is a crate:: or super:: path one module actually uses, read out of the source rather than drawn by hand.

The same graph as Mermaid source
%%{init: {"theme":"base","themeVariables":{"background":"#1a1b26","primaryColor":"#1f2335","primaryTextColor":"#c0caf5","primaryBorderColor":"#7aa2f7","secondaryColor":"#16161e","tertiaryColor":"#16161e","lineColor":"#737aa2","textColor":"#c0caf5","mainBkg":"#1f2335","nodeBorder":"#7aa2f7","clusterBkg":"#16161e","clusterBorder":"#2f3549","fontFamily":"ui-monospace, SFMono-Regular, Consolas, monospace","fontSize":"14px"}}}%%
flowchart TD
    n_lib(["lib.rs<br/>241 lines"])
    n_aead["aead.rs<br/>280 lines"]
    n_amnesia["amnesia.rs<br/>428 lines"]
    n_container["container.rs<br/>618 lines"]
    n_decoy["decoy.rs<br/>464 lines"]
    n_hoard["hoard.rs<br/>1099 lines"]
    n_hybrid["hybrid.rs<br/>524 lines"]
    n_kdf["kdf.rs<br/>633 lines"]
    n_lock["lock.rs<br/>2264 lines"]
    n_privatefile["privatefile.rs<br/>313 lines"]
    n_shred["shred.rs<br/>417 lines"]
    n_studio["studio.rs<br/>1508 lines"]
    n_tape["tape.rs<br/>391 lines"]
    n_vault["vault.rs<br/>688 lines"]
    n_weave["weave.rs<br/>1727 lines"]
    n_hoard --> n_amnesia
    n_hoard --> n_privatefile
    n_hoard --> n_weave
    n_lock --> n_aead
    n_lock --> n_hoard
    n_lock --> n_privatefile
    n_lock --> n_vault
    n_studio --> n_aead
    n_studio --> n_privatefile
    n_vault --> n_kdf
    n_vault --> n_lock
    n_vault --> n_privatefile
    click n_lib href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/lib.rs" "open the source"
    click n_aead href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/aead.rs" "open the source"
    click n_amnesia href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/amnesia.rs" "open the source"
    click n_container href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs" "open the source"
    click n_decoy href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/decoy.rs" "open the source"
    click n_hoard href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/hoard.rs" "open the source"
    click n_hybrid href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/hybrid.rs" "open the source"
    click n_kdf href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/kdf.rs" "open the source"
    click n_lock href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/lock.rs" "open the source"
    click n_privatefile href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/privatefile.rs" "open the source"
    click n_shred href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/shred.rs" "open the source"
    click n_studio href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/studio.rs" "open the source"
    click n_tape href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/tape.rs" "open the source"
    click n_vault href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/vault.rs" "open the source"
    click n_weave href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/weave.rs" "open the source"

This site loads no third-party script, so it cannot run Mermaid; the diagram above is the same nodes and edges drawn by the generator instead. GitHub renders the source below directly.

THE FILES

FileLinesWhat it is
aead.rs280Authenticated encryption with XChaCha20-Poly1305.
amnesia.rs428Amnesic secret storage: page-locked, zeroized, and never printed.
container.rs618The .veil encrypted container format.
decoy.rs464A second passphrase that opens a different, empty VeilVoice.
hoard.rs1099The obfuscated program folder: what VeilVoice keeps on disk, under names that mean nothing and beside files that hold nothing.
hybrid.rs524Post-quantum hybrid key encapsulation: X25519 + ML-KEM-768.
kdf.rs633Password-based key derivation with Argon2id.
lib.rs241Key derivation, post-quantum-hybrid key agreement, authenticated encryption and amnesic secret storage for VeilVoice.
lock.rs2264The application lock: an Argon2id password verifier with a rate limit.
privatefile.rs313Writing a file that only its owner can read.
shred.rs417Secure erasure, the self-destruct.
studio.rs1508The studio vault: a key that exists only when both locks have been opened.
tape.rs391A recording held in locked, zeroizing memory while it is still being made.
vault.rs688Where the app lock is kept: two copies, unpredictable names, and a restore.
weave.rs1727Thirty-one reversible encodings, chosen at random, applied around the encryption -- before it, after it, or both.
seal_and_open.rs80no module documentation yet
parser_fuzz.rs368Randomised robustness testing for the two parsers that read untrusted input.
timing.rs249Timing measurement of the password paths.