1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
//! # ferrocrypt
//!
//! High-level helpers for encrypting and decrypting files or directories using
//! password-based symmetric encryption or hybrid (asymmetric + symmetric)
//! encryption. Designed for straightforward, scriptable workflows rather than
//! low-level cryptographic building blocks.
//!
//! ## Design goals
//! - **Confidentiality + integrity** for small-to-medium file trees.
//! - **Simple ergonomics**: pick symmetric (password) or hybrid (public/private
//! key + optional passphrase) based on your distribution needs.
//! - **Batteries included**: temporary workspace management, path normalization,
//! and output file naming are handled for you.
//!
//! ## Quick start (symmetric path, mirrors `ferrocrypt symmetric` CLI)
//! ```rust,no_run
//! use ferrocrypt::{symmetric_encryption, CryptoError, secrecy::SecretString};
//!
//! # fn run() -> Result<(), CryptoError> {
//! // Encrypt a folder to out/secrets.fcs
//! let passphrase = SecretString::from("correct horse battery staple".to_string());
//! let produced = symmetric_encryption("./secrets", "./out", &passphrase, false)?;
//! println!("wrote {produced}");
//!
//! // Decrypt the archive back
//! let recovered = symmetric_encryption("./out/secrets.fcs", "./restored", &passphrase, false)?;
//! println!("restored to {recovered}");
//! # Ok(()) }
//! # fn main() { run().unwrap(); }
//! ```
//!
//! ## Quick start (hybrid path, mirrors `ferrocrypt hybrid` CLI)
//! ```rust,no_run
//! use ferrocrypt::{generate_asymmetric_key_pair, hybrid_encryption, CryptoError, secrecy::SecretString};
//!
//! # fn run() -> Result<(), CryptoError> {
//! // 1) Generate RSA keypair files under ./keys
//! // The passphrase encrypts the private key file itself
//! let passphrase = SecretString::from("my-key-pass".to_string());
//! let _msg = generate_asymmetric_key_pair(4096, &passphrase, "./keys")?;
//!
//! // 2) Encrypt to out/payload.fch using the public key (no passphrase needed)
//! let mut pub_key_path = "./keys/rsa-4096-pub-key.pem".to_string();
//! let empty_passphrase = SecretString::from("".to_string());
//! let produced = hybrid_encryption("./payload", "./out", &mut pub_key_path, &empty_passphrase)?;
//! println!("wrote {produced}");
//!
//! // 3) Decrypt out/payload.fch using the private key + passphrase to unlock it
//! let mut priv_key_path = "./keys/rsa-4096-priv-key.pem".to_string();
//! let restored = hybrid_encryption("./out/payload.fch", "./restored", &mut priv_key_path, &passphrase)?;
//! println!("restored to {restored}");
//! # Ok(()) }
//! # fn main() { run().unwrap(); }
//! ```
//!
//! ## When to choose which mode
//! - **Symmetric**: Fastest; same password encrypts and decrypts. Great for
//! personal backups or team secrets when you can share the password securely.
//! Produces `.fcs` files.
//! - **Hybrid**: Safer for distribution—encrypt with a recipient's public key
//! (no password needed for encryption); only their passphrase-protected
//! private key can decrypt. Each file gets a unique random key. Produces
//! `.fch` files.
//!
//! ## Security notes
//! - All cryptographic operations depend on a secure OS RNG; ensure the target
//! platform provides one.
//! - Ciphertext integrity is enforced; modification or wrong keys will yield
//! `CryptoError` results rather than corrupted plaintext.
//! - 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.
use fs;
use SecretString;
use cratenormalize_paths;
pub use crateCryptoError;
pub use secrecy;
/// Encrypt or decrypt files/directories using password-based symmetric crypto.
///
/// - **Encrypt**: if `input_path` is not already an `.fcs` archive, it is
/// packaged and encrypted to `output_dir` (writing `<name>.fcs`).
/// - **Decrypt**: if `input_path` ends with `.fcs`, it is decrypted and
/// extracted into `output_dir`.
/// - `large = true` mirrors the CLI `--large` flag for streaming large inputs.
///
/// Returns the path to the produced file or directory.
///
/// # Examples
///
/// ```no_run
/// use ferrocrypt::{symmetric_encryption, secrecy::SecretString};
///
/// // Encrypt a file
/// let passphrase = SecretString::from("my-secret-password".to_string());
/// let result = symmetric_encryption("./document.txt", "./encrypted", &passphrase, false)?;
/// println!("{}", result); // "Encrypted to ./encrypted/document.fcs for X.XXs"
///
/// // Decrypt it back
/// let result = symmetric_encryption("./encrypted/document.fcs", "./decrypted", &passphrase, false)?;
/// println!("{}", result); // "Decrypted to ./decrypted/document.txt for X.XXs"
/// # Ok::<(), ferrocrypt::CryptoError>(())
/// ```
/// Encrypt or decrypt using hybrid (RSA + XChaCha20-Poly1305) envelope encryption.
///
/// - `rsa_key_pem` is a **mutable string containing a file path** (not PEM
/// contents); it is zeroized after decryption for security.
/// - **Encrypt** when `input_path` is not `.fch`: uses the public key file
/// at `rsa_key_pem` to seal a random symmetric key, producing `<name>.fch`.
/// The `passphrase` parameter is **ignored during encryption** (pass empty
/// string).
/// - **Decrypt** when `input_path` ends with `.fch`: uses the private key file
/// at `rsa_key_pem`. The `passphrase` is **required** to decrypt the private
/// key file (must match the passphrase used when generating the keypair).
///
/// Returns a human-readable message describing the output path.
///
/// # Examples
///
/// ```no_run
/// use ferrocrypt::{hybrid_encryption, secrecy::SecretString};
///
/// // Encrypt with public key (no passphrase needed)
/// let mut pub_key = "./keys/rsa-4096-pub-key.pem".to_string();
/// let empty = SecretString::from("".to_string());
/// let result = hybrid_encryption("./secrets", "./encrypted", &mut pub_key, &empty)?;
/// println!("{}", result); // "Encrypted to ./encrypted/secrets.fch for X.XXs"
///
/// // Decrypt with private key (passphrase required)
/// let mut priv_key = "./keys/rsa-4096-priv-key.pem".to_string();
/// let passphrase = SecretString::from("my-key-passphrase".to_string());
/// let result = hybrid_encryption("./encrypted/secrets.fch", "./decrypted", &mut priv_key, &passphrase)?;
/// println!("{}", result); // "Decrypted to ./decrypted/secrets for X.XXs"
/// # Ok::<(), ferrocrypt::CryptoError>(())
/// ```
/// Generate and store an RSA key pair for hybrid encryption (default: RSA-4096).
///
/// - `byte_size` is the RSA modulus size in **bits** (e.g., 4096),
/// aligned with the CLI flag `--bit-size`.
/// - Keys are written into `output_dir` as `rsa-<bits>-priv-key.pem` and
/// `rsa-<bits>-pub-key.pem`.
/// - The `passphrase` **encrypts the private key file** for protection at rest;
/// the same passphrase is needed later when decrypting. The public key file
/// is unencrypted.
///
/// Returns a human-readable message pointing to the output directory.
///
/// # Examples
///
/// ```no_run
/// use ferrocrypt::{generate_asymmetric_key_pair, secrecy::SecretString};
///
/// let passphrase = SecretString::from("protect-my-private-key".to_string());
/// let result = generate_asymmetric_key_pair(4096, &passphrase, "./my_keys")?;
/// println!("{}", result); // "Generated key pair to ./my_keys"
/// // Creates: ./my_keys/rsa-4096-priv-key.pem (encrypted)
/// // ./my_keys/rsa-4096-pub-key.pem (plain)
/// # Ok::<(), ferrocrypt::CryptoError>(())
/// ```