Expand description
§ferrocrypt
Authenticated encryption of files and directories.
FerroCrypt writes .fcr files using one recipient-oriented container:
one random per-file key, one streamed authenticated payload, and one or more
typed recipient entries that independently wrap the same file key. The public
API exposes this through Encryptor and Decryptor rather than through
low-level cryptographic building blocks.
§Design goals
- Recipient-oriented encryption: passphrases and public keys are native
recipient schemes over the same
.fcrcontainer. - Path-based file workflows: archiving, streaming encryption, staging, and output naming are handled by the library.
- Typed routing:
Decryptor::openinspects the recipient list and returns a passphrase or private-key decryptor variant. - Typed diagnostics: operations return
CryptoErrorvalues with structured format, KDF, recipient, authentication, and I/O failures.
§Quick start (passphrase recipient)
These examples assume the input paths exist and the output names are unused.
use ferrocrypt::{Decryptor, Encryptor, CryptoError, Passphrase};
std::fs::create_dir_all("./out")?;
std::fs::create_dir_all("./restored")?;
// Encrypt
let encrypted = Encryptor::with_passphrase(Passphrase::new("correct horse battery staple"))
.write("./secrets", "./out", |ev| eprintln!("{ev}"))?;
println!("Encrypted to {}", encrypted.output_path.display());
// Decrypt — each operation consumes its own `Passphrase` value
let restored = match Decryptor::open(&encrypted.output_path)? {
Decryptor::Passphrase(d) => d.decrypt(
Passphrase::new("correct horse battery staple"),
"./restored",
|ev| eprintln!("{ev}"),
)?,
Decryptor::PrivateKey(_) => unreachable!("we just encrypted with a passphrase"),
_ => unreachable!("Decryptor is non_exhaustive; only Passphrase and PrivateKey exist today"),
};
println!("Decrypted to {}", restored.output_path.display());§Quick start (public-key recipients)
use ferrocrypt::{
Decryptor, Encryptor, generate_key_pair, PublicKey, PrivateKey,
CryptoError, Passphrase,
};
std::fs::create_dir_all("./out")?;
std::fs::create_dir_all("./restored")?;
// 1) Generate X25519 keypair
let keys = generate_key_pair("./keys", Passphrase::new("my-key-pass"), |ev| eprintln!("{ev}"))?;
println!("Fingerprint: {}", keys.fingerprint);
// 2) Encrypt with the recipient's public key (no passphrase required)
let encrypted = Encryptor::with_public_key(PublicKey::from_key_file(&keys.public_key_path)?)
.write("./payload", "./out", |ev| eprintln!("{ev}"))?;
// 3) Decrypt with the recipient's private key + its passphrase
let restored = match Decryptor::open(&encrypted.output_path)? {
Decryptor::PrivateKey(d) => d.decrypt(
PrivateKey::from_key_file(&keys.private_key_path, Passphrase::new("my-key-pass")),
"./restored",
|ev| eprintln!("{ev}"),
)?,
Decryptor::Passphrase(_) => unreachable!("we just encrypted to a public key"),
_ => unreachable!("Decryptor is non_exhaustive; only Passphrase and PrivateKey exist today"),
};
println!("Decrypted to {}", restored.output_path.display());§Choosing a recipient path
- Passphrase recipient: use
Encryptor::with_passphrasewhen the same passphrase should encrypt and decrypt the file. The resulting.fcrcontains exactly one nativeargon2idrecipient. - Public-key recipient: use
Encryptor::with_public_keyorEncryptor::with_public_keyswhen the sender should encrypt to one or more public recipient keys. Decryption requires a matchingPrivateKeyfile and that key file’s passphrase. This does not authenticate the sender.
§Format compatibility
FerroCrypt stores four independent version bytes: .fcr outer-container
version 0x01, FCA archive version 0x01, public-key encoding version
0x01, and private-key encoding version 0x01. The compatibility
guarantee starts with the first stable 0.3.0 release. The 0.3.0
pre-release series (-alpha.N, -beta.N, -rc.N) is outside it: files
written by a pre-release or by an untagged build of main carry no
cross-version guarantee and may fail to decrypt under a later release.
Stable 0.3.0 establishes the 0.3.0 compatibility baseline
(FORMAT.md §11.4): artifacts that use only the four stored versions
above stay readable under every later release. A future release may
introduce a new version in any one domain; that adds support without
removing baseline read support.
Older pre-0.3.0 files and key pairs use a different format family and, for
historical hybrid encryption, a different key-agreement stack. To migrate
older data, decrypt it with the release that created it, then re-encrypt it
with the current release.
§API stability
The on-disk format is versioned independently from the crate; see
Format compatibility above for its stability guarantee.
The public Rust API (Encryptor, Decryptor, PublicKey,
PrivateKey, the error types) is pre-1.0; it may change in minor releases
(0.x → 0.y), while patch releases (0.x.y → 0.x.z) preserve it. See the
repository CHANGELOG.md
for release notes.
§Security notes
- Encryption and key generation require a secure OS random-number source.
- Sender authentication is out of scope; public-key encryption identifies who can decrypt, not who encrypted.
- A successful decrypt requires header and payload authentication. Wrong
credentials or corrupted ciphertext return
CryptoError; authenticated partial plaintext may already have been staged when a later check fails. - Passphrase and key-pair writing reject Argon2id memory below a fixed
19 MiB floor with
CryptoError::KdfBelowWriteFloor. This prevents accidentally choosing a lower memory cost; it does not make a weak passphrase strong. The floor applies only when writing; readers accept lower memory costs subject to structural validation and theirKdfLimit. - FerroCrypt authenticates each file as written, but it does not detect
replay or rollback to an older valid
.fcr; use external versioning or a freshness check if that matters. - FerroCrypt encrypts file contents and, for directory inputs, internal
names, tree structure, and per-file sizes. It does not hide the total
ciphertext length (an approximate plaintext-size signal), recipient count,
or that the file is a FerroCrypt
.fcrcontainer. - Choose output directories and path ancestors that untrusted processes cannot modify during an operation. Existing output entries are refused; an error after commit can still leave complete output on disk.
- Decryption stages authenticated plaintext under an
.incompletename. SeeIncompleteOutputPolicyfor cleanup, retention, and interruption behavior. Do not consume staged output as a successfully decrypted file. - Encryption is not a filesystem snapshot. Prevent concurrent source changes or use a snapshot when a point-in-time copy is required.
- Unattended services must configure resource limits and bound concurrent
operations. The default Argon2id memory budget is 1 GiB per derivation;
see
KdfLimit. These APIs perform blocking filesystem and cryptographic work; asynchronous applications should run them on a blocking worker. - Public-key checksums do not establish ownership. Verify the recipient’s fingerprint through a trusted channel before encrypting.
- This crate is not third-party audited and is not advertised as compliance-certified.
§Error handling
Every fallible operation returns Result<T, CryptoError>. See
CryptoError for variant meanings and remediation hints.
§License
Licensed under GPL-3.0-only. See the LICENSE file in the repository.
Structs§
- Archive
Limits - Resource caps for FCA archive encoding and extraction.
- Authenticated
Recipient Mode - Recipient mode returned after successful authenticated decryption.
- Decrypt
Outcome - Successful outcome of
PassphraseDecryptor::decryptorPrivateKeyDecryptor::decrypt. - Encrypt
Outcome - Successful outcome of an
Encryptor::writecall. - Encryptor
- Builder-style entry point for encryption.
- Header
Read Limits - Resource caps for encrypted-file headers.
- KdfLimit
- Resource policy for Argon2id derivation during reading and writing.
- KdfParams
- KDF parameters stored in file headers and key files.
- KeyGen
Outcome - Successful outcome of
generate_key_pairorKeyPairGenerator::write. - KeyPair
Generator - Builder for X25519 key-pair generation.
- KeyRead
Limits - Local resource caps applied while reading key material: the
recipient-string cap to a
public.keyor afcr1…recipient string, and the wrapped-secret cap to aprivate.key. - Passphrase
- A passphrase held in a buffer that is wiped when the value drops.
- Passphrase
Decryptor - Decryptor for passphrase-sealed
.fcrfiles. Returned fromDecryptor::openwhen the file’s recipient list classifies asUnauthenticatedRecipientMode::Passphrase. - Private
Key - Private key for public-key-recipient decryption.
- Private
KeyDecryptor - Decryptor for public-key-sealed
.fcrfiles. Returned fromDecryptor::openwhen the file’s recipient list classifies asUnauthenticatedRecipientMode::PublicKey. - Public
Key - Public recipient key for FerroCrypt public-key encryption.
Enums§
- Authenticated
Recipient Mode Kind - Recipient kind exposed by
AuthenticatedRecipientMode::kind. - Crypto
Error - Errors from key generation, encryption, decryption, and validation.
- Decryptor
- Type-safe entry point for decryption.
- Format
Defect - Structural defects detected while parsing a FerroCrypt encrypted file
or key file. Carried inside
CryptoError::InvalidFormatso format failures can be pattern-matched without substring comparisons and without parsing display text. - Incomplete
Output Policy - Cleanup policy for staged plaintext after a returned decryption error.
- Invalid
KdfParams - KDF parameter that violates a format structural bound.
- Mixing
Policy - Public diagnostic category for a recipient mixing rule, surfaced
through
CryptoError::IncompatibleRecipients. - Progress
Event - Progress notification for encryption, decryption, or key generation.
- Unauthenticated
Recipient Mode - Result of a cheap structural probe of an
.fcrfile’s recipient list. - Unsupported
Version - File-format or key-file version rejection. Carries the raw version byte so callers can inspect it without parsing a formatted string.
Constants§
- ARGO
N2_ SALT_ SIZE - Argon2id salt size in bytes. Stored alongside
KdfParamsin any header that consumes a passphrase (argon2id recipient body,private.keycleartext header); the crate’s internal Argon2id derivation takes its salt at exactly this width. - ENCRYPTED_
EXTENSION - Default extension for encrypted FerroCrypt files, without the leading dot.
- FCR_
FILE_ V1_ VERSION .fcrouter-container version byte (0x01) for the first container revision (FORMAT.md§3.1). Stays pinned when a future revision advancesFCR_FILE_VERSION, mirroringcrate::key::private::PRIVATE_KEY_V1_VERSIONandcrate::key::public::PUBLIC_KEY_V1_VERSION.- FCR_
FILE_ VERSION .fcrouter-container version byte the current writer emits (FORMAT.md§3.1). EqualsFCR_FILE_V1_VERSIONtoday; a future container revision advances this constant while first-revision files stay readable (FORMAT.md§11.4), so compare against the pinned constant — not this one — to identify a first-revision file.- KDF_
PARAMS_ SIZE - Serialized size of
KdfParamsin bytes (3 ×u32big-endian). - MAGIC
- Four-byte magic identifying binary
.fcrandprivate.keyfiles. Textpublic.keyfiles use thefcr1prefix instead. - PRIVATE_
KEY_ FILENAME - Default filename for the private key file (binary, wrapped).
- PRIVATE_
KEY_ V1_ VERSION - Private-key encoding version byte (
0x01) for key-pair suite KPS-1. Pinned to KPS-1 even when the current writer moves to another suite. - PRIVATE_
KEY_ VERSION - Wire-version byte the current writer emits in
private.keyheaders. Derived fromWRITER_KEYPAIR_SUITE(crate-internal); not an independent support list. - PUBLIC_
KEY_ FILENAME - Default filename for the public key file (text form).
- PUBLIC_
KEY_ V1_ VERSION - Public-key encoding version byte (
0x01) for key-pair suite KPS-1. Pinned to KPS-1 even when the current writer moves to another suite. - PUBLIC_
KEY_ VERSION - Wire-version byte the current writer emits at offset 0 of every
public.keyrecipient payload. Derived fromWRITER_KEYPAIR_SUITE(crate-internal); mirrorscrate::key::private::PRIVATE_KEY_VERSIONso a future suite bump flows through both writers in lockstep. - VERSION
- The crate version from
Cargo.toml, captured at compile time.
Functions§
- decode_
x25519_ recipient_ string - Decodes a Bech32 recipient string (
fcr1…) into raw X25519 public-key material. - default_
encrypted_ filename - Returns the default encrypted filename for a given input path.
- generate_
key_ pair - Generates and stores an X25519 key pair for public-key (recipient) encryption.
- probe_
recipient_ mode - Cheap structural probe of an
.fcrfile’s recipient list. Not a security claim. - probe_
recipient_ mode_ with_ limits - Same as
probe_recipient_modebut uses the suppliedHeaderReadLimitsfor the structural header read instead of the conservative defaults. - validate_
private_ key_ file - Checks the cleartext structure of a FerroCrypt
private.keyfile. - validate_
public_ key_ file - Validates that a file is a well-formed FerroCrypt
public.keytext file.