# fcry - filecrypt `fcry` encrypts and decrypts files with an authenticated chunked format. New files use XChaCha20-Poly1305 in a STREAM-style construction: a 19-byte random nonce prefix, a 32-bit chunk counter, and a last-chunk bit form each 24-byte XChaCha nonce. Every chunk authenticates the full file header as AEAD associated data. The tool is intended for local file encryption, scripted backups, and streaming-friendly decrypts. It is not a general archive format, does not hide file size, and does not protect plaintext after another process has received it. ## Usage Encrypt with an interactive passphrase: ```sh fcry -i plain.bin -o plain.bin.fcry --passphrase ``` Decrypt with the same passphrase: ```sh fcry -d -i plain.bin.fcry -o plain.bin --passphrase ``` Use a raw 32-byte key file instead of a passphrase: ```sh fcry -i plain.bin -o plain.bin.fcry --key-file key.bin fcry -d -i plain.bin.fcry -o plain.bin --key-file key.bin ``` For non-interactive passphrase use: ```sh FCRY_PASSWORD='correct horse battery staple' \ fcry -i plain.bin -o plain.bin.fcry --passphrase-env FCRY_PASSWORD ``` `--passphrase-env` is useful for automation, but the environment variable can remain visible to the current process environment and platform tooling. Prefer interactive entry or a protected key file when possible. ## Safety Properties - File outputs are written to private, randomly named temporary files and are renamed into place only after encryption or decryption succeeds. Existing outputs require `--force`, except for the self-replacement case that is handled through the temporary file. - New passphrase encryptions use Argon2id by default with 1024 MiB of memory, 2 passes, and 4 lanes. Passphrases must be non-empty and at least 12 UTF-8 bytes unless `--allow-weak-kdf` is explicitly supplied for tests or legacy interop. - Decryption enforces a memory ceiling for Argon2id headers. The default cap is the lower of 4096 MiB, the architecture limit, and available Linux memory when that can be detected. Override it with `--max-argon-memory-mib` only for files you trust. - Chunk size is bounded to `1..=64 MiB`. Worker threads are capped at 256, and the pipeline bounds in-flight chunk memory. - v3 files carry a key commitment derived from the stretched key and committed header fields. This gives a fast, clear wrong-key failure before chunk processing and prevents stripping or downgrading the commitment without authentication failure. - On Unix, `fcry` makes a best-effort call to disable core dumps for the process before handling secrets. ## Format The current on-disk format version is v3. ```text magic "fcry" 4 bytes version u8 1 alg_id u8 1 (1 = XChaCha20-Poly1305) flags u8 1 reserved u8 1 (must be 0) chunk_size u32 LE 4 kdf_id u8 1 (0 = raw key, 1 = Argon2id) kdf_params variable nonce_prefix [u8; 19] plaintext_length u64 LE only when flags bit 0 is set key_commitment [u8; 32] only when flags bit 1 is set ciphertext chunks each plaintext chunk plus a 16-byte Poly1305 tag ``` The encoded header is AEAD associated data for every chunk. Changing the chunk size, KDF parameters, nonce prefix, committed plaintext length, key commitment, or other header bytes causes authentication failure. Version history: - v1: no flags and no committed plaintext length. - v2: adds the length-committed flag and optional `plaintext_length`. - v3: requires the key-commitment flag and stores the 32-byte key commitment. Regular file encryption commits `plaintext_length` in the header. Stdin encryption cannot know the final length up front, so stdin-produced files do not support random-access decrypt. ## Streaming And Ranges Normal decrypt-to-stdout emits each plaintext chunk after that chunk has authenticated. This means a truncated ciphertext can produce an authentic prefix on stdout before the final truncation error is reported. That is inherent to chunked streaming AE when bytes are released immediately. Use `--buffer-verify` when decrypting to stdout if downstream consumers must not see any plaintext until the whole file has authenticated: ```sh fcry -d -i plain.bin.fcry --passphrase --buffer-verify > plain.bin ``` `--buffer-verify` writes plaintext to a private temporary file first, verifies the complete ciphertext, and copies to stdout only after success. File outputs already get atomic temporary-file behavior, so `--buffer-verify` is only valid for decrypt-to-stdout. Random-access decrypt requires `--decrypt`, `--input-file`, `--offset`, and `--length`, and the input must have a length-committed header: ```sh fcry -d -i plain.bin.fcry --passphrase --offset 1048576 --length 4096 > slice.bin ``` A successful range decrypt authenticates the requested chunks and header. It does not prove that the rest of the file is present or untampered. Use a full decrypt when you need whole-file integrity. `--length 0` is rejected because it would authenticate no chunks. ## Threat Model `fcry` aims to provide confidentiality and integrity for file contents against an attacker who can read, copy, truncate, replace, or modify ciphertext files after encryption. With passphrase mode, offline guessing is still possible; the Argon2id parameters make each guess expensive but cannot make a weak passphrase safe. The format authenticates all header fields that affect decryption, including KDF parameters, chunk size, nonce prefix, committed plaintext length, and key commitment. Unknown header flags and unsupported algorithms are rejected. The following are explicit non-goals: - Hiding plaintext length or access patterns. `plaintext_length` is cleartext for regular-file encryptions, and ciphertext length already reveals an approximate plaintext size. There is no padding scheme. - Preventing plaintext exposure after successful decrypt. Plaintext written to stdout, files, pipes, shell history, terminals, swap, backups, or downstream tools is outside `fcry`'s control. - Protecting plaintext chunk buffers from every local memory-forensics route. Keys and passphrases use protected/zeroizing storage where practical, and chunk buffers are zeroized on drop, but decrypted plaintext necessarily exists in ordinary process memory while being processed. - Disabling Windows Error Reporting or minidumps. Unlike Unix core dumps, those are controlled by per-machine Windows policy; `fcry` records this as an operator/deployment responsibility rather than changing host-wide policy. - Recovering from loss of the passphrase or raw key file. There is no escrow or backdoor. ## Operational Notes - Keep backups of important plaintext until you have verified the encrypted file and your recovery path. - Store raw key files with restrictive permissions. On Unix, `fcry` warns when a key file is group/world accessible. - Use `--allow-weak-kdf` only for tests or compatibility with old intentionally weak files. - Use `--temp-dir` when the default temporary-file location is not acceptable for decrypt-to-stdout buffering or output staging.