feat: split crate into library and thin CLI binary
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.
This commit is contained in:
+20
-8
@@ -34,8 +34,7 @@
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
use crate::error::FcryError;
|
||||
use crate::policy;
|
||||
use crate::{error::FcryError, policy};
|
||||
|
||||
const MAGIC: [u8; 4] = *b"fcry";
|
||||
pub const VERSION_CURRENT: u8 = 3;
|
||||
@@ -152,6 +151,19 @@ pub struct Header {
|
||||
pub key_commitment: Option<[u8; KEY_COMMITMENT_LEN]>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct HeaderReadOptions {
|
||||
pub max_argon_memory_mib: u32,
|
||||
}
|
||||
|
||||
impl Default for HeaderReadOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_argon_memory_mib: policy::default_argon_decrypt_cap_mib(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
fn encode_without_commitment(&self) -> Vec<u8> {
|
||||
let mut out = Vec::with_capacity(104);
|
||||
@@ -188,14 +200,13 @@ impl Header {
|
||||
self.encode_without_commitment()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn read(r: &mut impl Read) -> Result<Self, FcryError> {
|
||||
Self::read_with_argon_cap(r, policy::default_argon_decrypt_cap_mib())
|
||||
Self::read_with_options(r, HeaderReadOptions::default())
|
||||
}
|
||||
|
||||
pub fn read_with_argon_cap(
|
||||
pub fn read_with_options(
|
||||
r: &mut impl Read,
|
||||
max_argon_memory_mib: u32,
|
||||
options: HeaderReadOptions,
|
||||
) -> Result<Self, FcryError> {
|
||||
let mut magic = [0u8; 4];
|
||||
r.read_exact(&mut magic)?;
|
||||
@@ -238,7 +249,7 @@ impl Header {
|
||||
let mut kdf_id = [0u8; 1];
|
||||
r.read_exact(&mut kdf_id)?;
|
||||
let kdf = KdfParams::read_from(kdf_id[0], r)?;
|
||||
policy::validate_header_kdf(&kdf, max_argon_memory_mib)?;
|
||||
policy::validate_header_kdf(&kdf, options.max_argon_memory_mib)?;
|
||||
|
||||
let mut nonce_prefix = [0u8; NONCE_PREFIX_LEN];
|
||||
r.read_exact(&mut nonce_prefix)?;
|
||||
@@ -274,9 +285,10 @@ impl Header {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn roundtrip() {
|
||||
let h = Header {
|
||||
|
||||
Reference in New Issue
Block a user