Skip to main content

Crate ferrocrypt

Crate ferrocrypt 

Source
Expand description

§ferrocrypt

High-level file encryption for files and directories.

FerroCrypt writes .fcr files using one recipient-oriented v1 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 .fcr container.
  • Path-based file workflows: archiving, streaming encryption, staging, and output naming are handled by the library.
  • Typed routing: Decryptor::open inspects the recipient list and returns a passphrase or private-key decryptor variant.
  • Typed diagnostics: operations return CryptoError values with structured format, KDF, recipient, authentication, and I/O failures.

§Quick start (passphrase recipient)

use ferrocrypt::{Decryptor, Encryptor, CryptoError, secrecy::SecretString};

let passphrase = SecretString::from("correct horse battery staple".to_string());

// Encrypt
let encrypted = Encryptor::with_passphrase(passphrase.clone())
    .write("./secrets", "./out", |ev| eprintln!("{ev}"))?;
println!("Encrypted to {}", encrypted.output_path.display());

// Decrypt
let restored = match Decryptor::open(&encrypted.output_path)? {
    Decryptor::Passphrase(d) => d.decrypt(passphrase, "./restored", |ev| eprintln!("{ev}"))?,
    Decryptor::PrivateKey(_) => unreachable!("we just encrypted with a passphrase"),
    _ => unreachable!("Decryptor is non_exhaustive; v1 has only Passphrase + PrivateKey"),
};
println!("Decrypted to {}", restored.output_path.display());

§Quick start (public-key recipients)

use ferrocrypt::{
    Decryptor, Encryptor, generate_key_pair, PublicKey, PrivateKey,
    CryptoError, secrecy::SecretString,
};

// 1) Generate X25519 keypair
let passphrase = SecretString::from("my-key-pass".to_string());
let keys = generate_key_pair("./keys", passphrase.clone(), |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 + passphrase
let restored = match Decryptor::open(&encrypted.output_path)? {
    Decryptor::PrivateKey(d) => d.decrypt(
        PrivateKey::from_key_file(&keys.private_key_path),
        passphrase,
        "./restored",
        |ev| eprintln!("{ev}"),
    )?,
    Decryptor::Passphrase(_) => unreachable!("we just encrypted to a public key"),
    _ => unreachable!("Decryptor is non_exhaustive; v1 has only Passphrase + PrivateKey"),
};
println!("Decrypted to {}", restored.output_path.display());

§Choosing a recipient path

  • Passphrase recipient: use Encryptor::with_passphrase when the same passphrase should encrypt and decrypt the file. The resulting .fcr contains exactly one native argon2id recipient.
  • Public-key recipient: use Encryptor::with_public_key or Encryptor::with_public_keys when the sender should encrypt to one or more public recipient keys. Decryption requires a matching PrivateKey file and that key file’s passphrase. This does not authenticate the sender.

§Format compatibility

The current on-disk format is FerroCrypt v1 for .fcr, public.key, and private.key. The compatibility guarantee starts with the first stable 0.3.0 release. During the 0.3.0 pre-release series (-alpha.N, -beta.N, -rc.N), the format is not yet frozen: files written by a pre-release or by main carry no cross-version guarantee and may fail to decrypt under a later pre-release.

Once the format is frozen, files written by any release that produces format v1 will decrypt under any later release that supports format v1. If a future release introduces format v2, format v1 reading will be maintained for compatibility with older files.

Older pre-v1 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

  • All cryptographic operations depend on a secure OS RNG; ensure the target platform provides one.
  • Sender authentication is out of scope; public-key encryption identifies who can decrypt, not who encrypted.
  • Ciphertext integrity is enforced; modification or wrong keys yield CryptoError results rather than corrupted plaintext.
  • Passphrase and key-pair writing reject Argon2id memory below a fixed 19 MiB floor with CryptoError::KdfBelowWriteFloor, so a .fcr or private.key cannot be sealed with a weak passphrase work factor by accident. The floor applies only when writing; decryption accepts any structurally valid file.
  • 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 .fcr container.
  • 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.

Re-exports§

pub use secrecy;

Structs§

ArchiveLimits
Resource caps for FCA archive encoding and extraction.
AuthenticatedRecipientMode
Recipient mode established by a successful authenticated decrypt.
DecryptOutcome
Successful outcome of PassphraseDecryptor::decrypt or PrivateKeyDecryptor::decrypt.
EncryptOutcome
Successful outcome of an Encryptor::write call.
Encryptor
Builder-style entry point for encryption.
HeaderReadLimits
Local resource caps applied while reading an encrypted-file header.
KdfLimit
Local policy limit for Argon2id work accepted during decryption.
KdfParams
KDF parameters stored in file headers and key files.
KeyGenOutcome
Successful outcome of generate_key_pair or KeyPairGenerator::write.
KeyPairGenerator
Builder for X25519 key-pair generation.
PassphraseDecryptor
Decryptor for passphrase-sealed .fcr files. Returned from Decryptor::open when the file’s recipient list classifies as UnauthenticatedRecipientMode::Passphrase.
PrivateKey
Private key for public-key-recipient decryption.
PrivateKeyDecryptor
Decryptor for public-key-sealed .fcr files. Returned from Decryptor::open when the file’s recipient list classifies as UnauthenticatedRecipientMode::PublicKey.
PublicKey
Public recipient key for FerroCrypt public-key encryption.

Enums§

AuthenticatedRecipientModeKind
Public, forward-compatible discriminant for AuthenticatedRecipientMode. The variant carries no authentication authority on its own — only an AuthenticatedRecipientMode value does, and that wrapper is unforgeable outside the crate.
CryptoError
Errors that can occur during key generation, encryption, or decryption.
Decryptor
Type-safe entry point for decryption.
FormatDefect
Structural defects detected while parsing a FerroCrypt encrypted file or key file. Carried inside CryptoError::InvalidFormat so format failures can be pattern-matched without substring comparisons and without heap-allocated Strings.
IncompleteOutputPolicy
Policy for the .incomplete working tree when decrypt fails.
InvalidKdfParams
Which KDF parameter from an untrusted header failed its structural bound check. Carries the raw value so callers can decide whether to re-try with looser limits.
MixingPolicy
Public diagnostic category for a recipient mixing rule, surfaced through CryptoError::IncompatibleRecipients.
ProgressEvent
Structured progress signal emitted during encrypt, decrypt, and key generation.
UnauthenticatedRecipientMode
Result of a cheap structural probe of an .fcr file’s recipient list.
UnsupportedVersion
File-format or key-file version rejection. Carries the raw version byte so callers can inspect it without parsing a formatted string.

Constants§

ARGON2_SALT_SIZE
Argon2id salt size in bytes. Stored alongside KdfParams in any header that consumes a passphrase (argon2id recipient body, private.key cleartext header). KdfParams::hash_passphrase takes its salt as &[u8; ARGON2_SALT_SIZE].
ENCRYPTED_EXTENSION
Default file extension for encrypted FerroCrypt payload files.
FCR_FILE_VERSION
Version byte for the .fcr outer encrypted file (FORMAT.md §3.1).
KDF_PARAMS_SIZE
Serialized size of KdfParams in bytes (3 × u32 big-endian).
MAGIC
4-byte ASCII magic identifying every FerroCrypt v1 artefact.
PRIVATE_KEY_FILENAME
Default filename for the private key file (binary, wrapped).
PRIVATE_KEY_V1_VERSION
Canonical v1 private.key wire-version byte. Mirrors the suite constant from KeypairSuite::V1 (crate-internal) so bumping the keypair suite flows through this constant automatically.
PRIVATE_KEY_VERSION
Wire-version byte the current writer emits in private.key headers. Derived from WRITER_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
Canonical v1 wire-version byte for public.key recipient payloads. Mirrors the suite constant from KeypairSuite::V1 (crate-internal) so bumping the keypair suite flows through this constant automatically.
PUBLIC_KEY_VERSION
Wire-version byte the current writer emits at offset 0 of every public.key recipient payload. Derived from WRITER_KEYPAIR_SUITE (crate-internal); mirrors crate::key::private::PRIVATE_KEY_VERSION so a future suite bump flows through both writers in lockstep.
VERSION
The version of this crate, captured from Cargo.toml at compile time.

Functions§

decode_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 .fcr file’s recipient list. Not a security claim.
probe_recipient_mode_with_limits
Same as probe_recipient_mode but uses the supplied HeaderReadLimits for the structural header read instead of the conservative defaults.
validate_private_key_file
Validates that a file is a well-formed FerroCrypt private.key file.
validate_public_key_file
Validates that a file is a well-formed FerroCrypt public.key text file.