Skip to main content

Crate gunnar_crypt

Crate gunnar_crypt 

Source
Expand description

Encrypted repositories: the operator of a gunnar server cannot read a repository’s content; only holders of a member’s SSH private key can.

The design, the threat model and what still leaks are in .nornir/encrypted-repos.md. This crate is the part every door shares:

  • envelope — the format of one encrypted file (blob): a 13-byte header naming the key epoch, then AES-256-SIV over the content. Deterministic on purpose: the same content under the same key gives the same blob, so git’s clean filter is stable and git status stays quiet, and the blob id is a KEYED id (it depends on the secret key), so nobody without the key can confirm that a known file is in the repository.

  • keyring — the repository’s data keys, one per epoch. A rotation adds an epoch; old epochs stay so members can still read old content.

  • members — the plaintext list of members’ PUBLIC keys (.gunnar-crypt/members). The key ring is wrapped to every key in it.

  • policy — what a server checks on a push to an encrypted repository: every blob is an envelope, except the three files the scheme itself needs. It reads formats, never keys.

  • wrap (feature wrap) — the age wrap of the key ring, behind one trait with two schemes chosen per repository by its level: SshClassic (members’ SSH public keys, ssh-ed25519/ssh-rsa; unwrapped with an SSH private key FILE — ssh-agent cannot help, its protocol only signs) and StandalonePq (age’s hybrid ML-KEM-768 + X25519 recipient, one age1pq1… key per member device from gunnar crypt keygen).

  • pq (feature wrap) — that hybrid recipient and its identity, until an age release carries its own.

  • diff (feature diff) — line diffs of decrypted content. The server cannot diff ciphertext and keeps no pre-made diffs; the client does it.

One crate, two targets. The CLI and the browser console run this same code, native and wasm32: fetch ciphertext → unwrap the key ring → open and diff locally → seal before push. The only difference is where the SSH private key lives: the CLI reads ~/.ssh itself; the browser asks the user’s own gunnar unlock agent (the same CLI, on 127.0.0.1) to unwrap the ring, and is never given the SSH key. tests/vectors.rs runs the same vectors on both targets.

Who links what: gunnar-server takes default-features = false (policy and format only; no cipher, no key), the browser console takes cipher and diff, and the CLI takes everything.

Re-exports§

pub use zeroize;

Modules§

diff
Line diffs of DECRYPTED content, computed where the key is: in the CLI (gunnar crypt diff) and in the browser console’s wasm. The server never sees plaintext, so it cannot diff an encrypted repository; there are no pre-made diffs either (they would only cost space). Both sides run this same function, so a diff reads the same in the terminal and in the browser.
envelope
One encrypted file (blob). Two versions; this code writes v2 and reads both.
keyring
The repository’s data keys: one 64-byte AES-256-SIV key per EPOCH.
members
.gunnar-crypt/members: who the key ring is wrapped to, by PUBLIC key.
paths
Where the scheme’s own files live in a repository.
policy
What a server checks on a push to an ENCRYPTED repository.
pq
age’s hybrid post-quantum recipient, mlkem768x25519 (c2sp.org/age): an age1pq1… public key and its AGE-SECRET-KEY-PQ-1… identity.
wrap
The key ring, wrapped to every member key with age, and unwrapped with an SSH private key FILE or a post-quantum identity file.

Constants§

DRIVER
The name of the git filter, diff and merge drivers.
GITATTRIBUTES_LINES
The .gitattributes lines gunnar crypt init writes.

Functions§

diff_blobs
The diff between two stored blobs of one path (None = the path does not exist on that side), opened with ring. What gunnar crypt diff prints and what the console shows for an encrypted repository.
open_blob
Open a blob as text: an envelope is opened with ring; anything else is returned as it is (history from before gunnar crypt init is plaintext).
random_bytes
N bytes from the OS random source (pairing tokens, keys).