container.rs

crates/veilvoice-crypto/src/container.rs

veilvoice-crypto · 618 lines · read the source here · or on GitHub

The .veil encrypted container format.

A single self-describing blob: everything needed to decrypt except the password or private key travels with the ciphertext, so a file stays readable after the default KDF costs are raised.

offset  size  field
0     8  magic "VEILVOX1"
8     1  format version (1)
9     1  mode: 1 = password, 2 = hybrid public key
10     2  reserved, must be zero
12     4  Argon2id m_cost (KiB, little-endian)     password mode only
16     4  Argon2id t_cost                          password mode only
20     4  Argon2id p_cost                          password mode only
24    16  Argon2id salt                            password mode only
40    24  XChaCha20 nonce
64     4  encapsulation length (little-endian)
68     N  encapsulation                            hybrid mode only
68+N     …  ciphertext ‖ Poly1305 tag

The entire header is the AEAD's associated data. Editing any byte of it by downgrading the KDF cost, swapping the mode or corrupting the salt, makes decryption fail rather than silently changing behaviour. Unused fields are written as zero and are still authenticated, so they cannot be used as a covert channel or a downgrade vector.

In plain words

The shape of an encrypted .veil file.

Everything needed to open it travels inside it, apart from the passphrase or the key. That means a file made today still opens years later, even after VeilVoice has changed how hard it makes the encryption by default: the file remembers what it was made with.

The part at the front that describes the file is itself covered by the tamper check, so it cannot be edited to make the rest open more easily.

WHAT THIS FILE CONTAINS

618 lines defining 9 functions (8 public), 2 types and 5 constants. Everything below is read out of the source, so it cannot disagree with the code.

The types it owns.

  • enum Mode line 56 · How a container is locked.
  • struct Header line 65 · A parsed container header.

What happens when it runs. These are the ways in: public, and nothing else in this file calls them, so they are what an outside caller reaches first.

  • Header::to_bytes line 81 · Serialise exactly as it appears on disk.
  • veil_path line 180 · The conventional path of the sealed form of path.
  • seal_with_password line 193 · Encrypt plaintext under a password.
    reaches finish
  • seal_to_public_key line 210 · Encrypt plaintext to a recipient's hybrid public key.
    reaches finish
  • open_with_password line 244 · Decrypt a password-locked container.
    reaches open_with_password_within, parse
  • open_with_secret_key line 276 · Decrypt a container addressed to recipient.
    reaches parse

WHAT CALLS WHAT

Header::to_bytes line 81 Header::parse line 101 veil_path line 180 seal_with_password line 193 seal_to_public_key line 210 finish line 235 open_with_password line 244 open_with_password_within line 260 open_with_secret_key line 276 entry: a way in: public, and nothing in this file calls it api: public, and also used inside this file helper: private to this file dashed: a call that goes back up, or across a wrapped rank The functions this file defines, and the calls between them. An edge means the callee's name appears, called, inside the caller's body. This is a syntactic reading, not a type-resolved one.

The functions this file defines, and the calls between them. An edge means the callee's name appears, called, inside the caller's body. This is a syntactic reading, not a type-resolved one.

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_to_bytes(["Header::to_bytes<br/>line 81"])
    n_parse["Header::parse<br/>line 101"]
    n_veil_path(["veil_path<br/>line 180"])
    n_seal_with_password(["seal_with_password<br/>line 193"])
    n_seal_to_public_key(["seal_to_public_key<br/>line 210"])
    n_finish["finish<br/>line 235"]
    n_open_with_password(["open_with_password<br/>line 244"])
    n_open_with_password_within["open_with_password_within<br/>line 260"]
    n_open_with_secret_key(["open_with_secret_key<br/>line 276"])
    n_open_with_password --> n_open_with_password_within
    n_open_with_password_within --> n_parse
    n_open_with_secret_key --> n_parse
    n_seal_to_public_key --> n_finish
    n_seal_with_password --> n_finish
    click n_to_bytes href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L81" "open the source"
    click n_parse href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L101" "open the source"
    click n_veil_path href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L180" "open the source"
    click n_seal_with_password href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L193" "open the source"
    click n_seal_to_public_key href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L210" "open the source"
    click n_finish href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L235" "open the source"
    click n_open_with_password href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L244" "open the source"
    click n_open_with_password_within href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L260" "open the source"
    click n_open_with_secret_key href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-crypto/src/container.rs#L276" "open the source"
    classDef entry fill:#1f2335,stroke:#7aa2f7,color:#c0caf5
    class n_to_bytes,n_veil_path,n_seal_with_password,n_seal_to_public_key,n_open_with_password,n_open_with_secret_key entry
    classDef api fill:#1f2335,stroke:#7dcfff,color:#c0caf5
    class n_parse,n_open_with_password_within api
    classDef helper fill:#1f2335,stroke:#bb9af7,color:#c0caf5
    class n_finish helper

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.

ITEMS

ItemLineDocumentation
MAGIC pub const45Magic bytes at the start of every container.
FORMAT_VERSION pub const47Format version this build writes.
HEADER_LEN pub const49Fixed header length in bytes, before any encapsulation.
MODE_PASSWORD const51
MODE_HYBRID const52
Mode pub enum56How a container is locked.
Header pub struct65A parsed container header.
Header::to_bytes pub fn81Serialise exactly as it appears on disk.
Header::parse pub fn101Parse a header, returning it with the offset at which ciphertext starts.
veil_path pub fn180The conventional path of the sealed form of path.
seal_with_password pub fn193Encrypt plaintext under a password.
seal_to_public_key pub fn210Encrypt plaintext to a recipient's hybrid public key.
finish fn235Seal plaintext under header and return the whole file.
open_with_password pub fn244Decrypt a password-locked container.
open_with_password_within pub fn260Decrypt a password-locked container, refusing one that declares a memory cost above max_m_cost.
open_with_secret_key pub fn276Decrypt a container addressed to recipient.