ferrocrypt 0.3.0

Recipient-oriented file and directory encryption: passphrase (Argon2id) and X25519 public-key recipients, XChaCha20-Poly1305 STREAM payloads, HKDF-SHA3-256 / HMAC-SHA3-256 key derivation and authentication.
Documentation
//! Shared integration-test helpers.
//!
//! `passphrase_auto` and `recipient_auto` use `probe_recipient_mode` to select
//! encryption or decryption. A recognized encrypted header selects decryption;
//! `None` selects encryption, and a malformed encrypted header propagates an
//! error. The helpers use the fast test Argon2id parameters from
//! `ferrocrypt-test-support`; `generate_key_pair` uses the same configuration.
//!
//! Each integration-test binary compiles this module separately; when a
//! binary only imports a subset of the helpers the rest would trip
//! `dead_code`, so the module-level allow keeps that quiet without
//! weakening the lint for the binaries themselves.

#![allow(dead_code)]

use std::path::{Path, PathBuf};

use ferrocrypt::{
    CryptoError, Decryptor, Encryptor, KdfLimit, KeyGenOutcome, Passphrase, PrivateKey,
    ProgressEvent, PublicKey, probe_recipient_mode,
};
use ferrocrypt_test_support::{fast_keypair_generator, fast_passphrase_encryptor};

pub fn passphrase_auto(
    input: impl AsRef<Path>,
    output_dir: impl AsRef<Path>,
    passphrase: &str,
    save_as: Option<&Path>,
    kdf_limit: Option<&KdfLimit>,
    on_event: impl Fn(&ProgressEvent),
) -> Result<PathBuf, CryptoError> {
    let input = input.as_ref();
    let output_dir = output_dir.as_ref();
    if !input.exists() {
        return Err(CryptoError::InputPath);
    }
    if probe_recipient_mode(input)?.is_some() {
        match Decryptor::open(input)? {
            Decryptor::Passphrase(mut d) => {
                if let Some(limit) = kdf_limit {
                    d = d.kdf_limit(*limit);
                }
                d.decrypt(Passphrase::new(passphrase), output_dir, on_event)
                    .map(|o| o.output_path)
            }
            Decryptor::PrivateKey(_) => Err(CryptoError::NoSupportedRecipient),
            _ => Err(CryptoError::NoSupportedRecipient),
        }
    } else {
        let mut encryptor = fast_passphrase_encryptor(Passphrase::new(passphrase));
        if let Some(s) = save_as {
            encryptor = encryptor.save_as(s);
        }
        encryptor
            .write(input, output_dir, on_event)
            .map(|o| o.output_path)
    }
}

pub fn recipient_auto(
    input: impl AsRef<Path>,
    output_dir: impl AsRef<Path>,
    key_file: impl AsRef<Path>,
    passphrase: &str,
    save_as: Option<&Path>,
    kdf_limit: Option<&KdfLimit>,
    on_event: impl Fn(&ProgressEvent),
) -> Result<PathBuf, CryptoError> {
    let input = input.as_ref();
    let output_dir = output_dir.as_ref();
    let key_file = key_file.as_ref();
    if !input.exists() {
        return Err(CryptoError::InputPath);
    }
    if probe_recipient_mode(input)?.is_some() {
        match Decryptor::open(input)? {
            Decryptor::PrivateKey(mut d) => {
                if let Some(limit) = kdf_limit {
                    d = d.kdf_limit(*limit);
                }
                d.decrypt(
                    PrivateKey::from_key_file(key_file, Passphrase::new(passphrase)),
                    output_dir,
                    on_event,
                )
                .map(|o| o.output_path)
            }
            Decryptor::Passphrase(_) => Err(CryptoError::NoSupportedRecipient),
            _ => Err(CryptoError::NoSupportedRecipient),
        }
    } else {
        let mut encryptor = Encryptor::with_public_key(PublicKey::from_key_file(key_file)?);
        if let Some(s) = save_as {
            encryptor = encryptor.save_as(s);
        }
        encryptor
            .write(input, output_dir, on_event)
            .map(|o| o.output_path)
    }
}

pub fn generate_key_pair(
    passphrase: &str,
    output_dir: impl AsRef<Path>,
    on_event: impl Fn(&ProgressEvent),
) -> Result<KeyGenOutcome, CryptoError> {
    fast_keypair_generator(Passphrase::new(passphrase)).write(output_dir, on_event)
}