lib.rs

crates/veilvoice-policy/src/lib.rs

veilvoice-policy · 168 lines · read the source here · or on GitHub

Settings somebody else decided, sealed so they cannot be edited without a passphrase, and, more importantly, built so that editing them without one buys nothing worth having.

The design decision this crate turns on

A policy file has an obvious problem. To apply a policy at every launch, the program has to be able to read it at every launch. If reading it needs a passphrase, the user types one every time, and if it does not, then anybody who can write the file can rewrite the policy.

The usual answers are a privileged daemon holding the key, or a key hidden in the binary, and neither is honest here: this project needs no privileges, and a key in a binary anybody can download is not a key.

So the constraint is moved into the shape of the data. A requirement can only make VeilVoice stricter. There is no requirement that turns encryption off, none that lowers the de-identification floor, none that disables the app lock, and there is no room in Requirement to express one, because every variant is a tightening and the type has no other kind.

Then somebody who edits the plain policy file without the passphrase can do exactly one thing: make this machine's VeilVoice more restrictive than its owner asked for. That is a nuisance, and it is not a privacy failure: which is the failure this project exists to avoid. The passphrase-sealed copy is what proves the policy is the one the administrator wrote; the shape of the type is what makes the answer survive the seal not having been checked yet.

What the seal is for, and what it is not

Policy::seal uses the same container as everything else here: Argon2id over the passphrase, X25519 with ML-KEM-768 for the hybrid modes, XChaCha20-Poly1305 for the contents. verify opens the sealed copy and compares it against the plain one, which is how anybody with the passphrase establishes that the policy in force is the policy that was written.

It is not enforcement. Anything with write access to VeilVoice's own executable can replace VeilVoice, and no file it reads can prevent that. Anything running as the user can delete the policy entirely. What a sealed policy gives is a policy that cannot be quietly rewritten into something weaker, and that is a smaller claim than "enforced" on purpose. See SCOPE.

Detecting deletion is veilvoice-guard's job, not this one's: put the policy files in a tamper manifest and the removal shows up there.

Reading a policy costs nothing

Policy::load reads the plain file and applies it. It never asks for a passphrase, never blocks, and reports the seal as Verification::Unchecked rather than pretending to have looked. A front end that wants the stronger statement calls verify when it has a passphrase to offer.

In plain words

This lets settings be locked down, and only in one direction.

Someone setting up a machine for other people can seal a set of settings so they can be made stricter but never looser. Nobody needs a password to read what the rules are -- only to change them -- because a rule people cannot see is a rule they will trip over.

WHAT THIS FILE CONTAINS

168 lines defining 4 functions (0 public), 1 type and 2 constants. Everything below is read out of the source, so it cannot disagree with the code.

The types it owns.

  • enum Error line 95 · Everything that can go wrong in this crate.

WHAT CALLS WHAT

Error::from line 105 Error::from line 111 Error::fmt line 117 Error::source line 127 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_from["Error::from<br/>line 105"]
    n_from["Error::from<br/>line 111"]
    n_fmt["Error::fmt<br/>line 117"]
    n_source["Error::source<br/>line 127"]
    click n_from href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-policy/src/lib.rs#L105" "open the source"
    click n_from href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-policy/src/lib.rs#L111" "open the source"
    click n_fmt href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-policy/src/lib.rs#L117" "open the source"
    click n_source href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-policy/src/lib.rs#L127" "open the source"
    classDef helper fill:#1f2335,stroke:#bb9af7,color:#c0caf5
    class n_from,n_from,n_fmt,n_source 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
VERSION pub const77Crate version string, surfaced in the About panel.
SCOPE pub const83What a sealed policy is worth, in the words a front end should show.
Error pub enum95Everything that can go wrong in this crate.
Error::from fn105
Error::from fn111
Error::fmt fn117
Error::source fn127