2f16e735c3
The crypto engine was only reachable through the fcry binary; embedding
it in another Rust project meant shelling out to the CLI. Restructure
the crate so the binary sits on top of a proper library API.
- Add src/lib.rs exposing encrypt/decrypt/decrypt_range/derive_key, the
header and policy types, and the secret-handling primitives.
- Replace the positional-argument wrapper ladder
(encrypt_with_output_options, decrypt_with_argon_cap, ...) with
options structs: EncryptOptions, DecryptOptions, DecryptRangeOptions
and HeaderReadOptions. OutSinkOptions becomes the public
OutputOptions and no longer carries the input path; the input is now
an explicit parameter to OutSink::open_with_options so the
same-file-aliasing guard's inputs are visible at each call site.
- File parameters take Option<PathBuf>/&Path instead of AsRef<str>, so
non-UTF-8 paths work.
- FcryError implements Display and std::error::Error so it composes
with anyhow/thiserror-style error handling in downstream crates.
- Move read_key_file and normalize_passphrase from main.rs into
secrets.rs so library users get the same strict 32-byte key-file
parsing and NFC passphrase normalization. The world-readable
key-file warning stays in the CLI wrapper (read_key_file_cli).
- Drop now-unneeded #[allow(dead_code)] markers; ReadInfoChunk::Normal
loses its unused byte-count payload.
- Add rustfmt.toml (StdExternalCrate grouping, crate-granularity
imports) and reformat imports accordingly.
- Add tests/library_api.rs covering a file round-trip and a range
decrypt through the public API with a raw key.
User-visible change: CLI behavior is unchanged except error output,
which is now human-readable Display text ("Error: wrong key or
passphrase") instead of the Rust Debug representation.
Test plan: cargo clippy (default, --tests, --benches) is clean;
cargo +nightly fmt produces no diff; cargo test passes 43 tests
including the new library_api integration tests.
69 lines
1.7 KiB
Rust
69 lines
1.7 KiB
Rust
// SPDX-License-Identifier: MIT-0
|
|
|
|
use std::{fmt, io};
|
|
|
|
use chacha20poly1305::aead;
|
|
|
|
#[derive(Debug)]
|
|
pub enum FcryError {
|
|
Io(io::Error),
|
|
Crypto(aead::Error),
|
|
Rng(getrandom::Error),
|
|
Format(String),
|
|
Kdf(String),
|
|
Passphrase(String),
|
|
WrongKey,
|
|
}
|
|
|
|
impl fmt::Display for FcryError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Io(e) => write!(f, "I/O error: {e}"),
|
|
Self::Crypto(_) => write!(f, "cryptographic authentication failed"),
|
|
Self::Rng(e) => write!(f, "randomness error: {e}"),
|
|
Self::Format(msg) => write!(f, "format error: {msg}"),
|
|
Self::Kdf(msg) => write!(f, "KDF error: {msg}"),
|
|
Self::Passphrase(msg) => write!(f, "passphrase error: {msg}"),
|
|
Self::WrongKey => write!(f, "wrong key or passphrase"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for FcryError {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
match self {
|
|
Self::Io(e) => Some(e),
|
|
Self::Rng(e) => Some(e),
|
|
Self::Crypto(_)
|
|
| Self::Format(_)
|
|
| Self::Kdf(_)
|
|
| Self::Passphrase(_)
|
|
| Self::WrongKey => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for FcryError {
|
|
fn from(e: io::Error) -> Self {
|
|
FcryError::Io(e)
|
|
}
|
|
}
|
|
|
|
impl From<aead::Error> for FcryError {
|
|
fn from(e: aead::Error) -> Self {
|
|
FcryError::Crypto(e)
|
|
}
|
|
}
|
|
|
|
impl From<getrandom::Error> for FcryError {
|
|
fn from(e: getrandom::Error) -> Self {
|
|
FcryError::Rng(e)
|
|
}
|
|
}
|
|
|
|
impl From<argon2::Error> for FcryError {
|
|
fn from(e: argon2::Error) -> Self {
|
|
FcryError::Kdf(e.to_string())
|
|
}
|
|
}
|