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
//! # encryptor
//!
//! Encrypt a **Web3 wallet secret phrase** with an easy-to-remember password
//! and store only the resulting ciphertext string.
//!
//! * **KDF ** [`Argon2id`](https://en.wikipedia.org/wiki/Argon2) — password → 256-bit key
//! * **AEAD ** [`AES-256-GCM`](https://en.wikipedia.org/wiki/Galois/Counter_Mode) — key + nonce → authenticated ciphertext
//! * **Blob** `[salt | nonce | ciphertext]` Base64URL-encoded (no padding)
//!
//! ```rust
//! use encryptor::{encrypt, decrypt};
//!
//! let phrase = "satoshi doll mercy …"; // wallet seed phrase
//! let pass = "Fr33dom-2025!"; // memorable password
//!
//! let blob = encrypt(phrase, pass)?; // store this string
//! assert_eq!(phrase, decrypt(&blob, pass)?);
//! # Ok::<(), encryptor::CryptoError>(())
//! ```
//!
//! ## Threat model
//! | ✅ Protects against | ❌ Does **not** protect against |
//! |--------------------------------|----------------------------------------------|
//! | Lost / stolen disk or backup | Very weak or leaked passwords |
//! | Curious cloud operator | Attackers who can key-log or phish your pass |
//!
//! > **Security disclaimer:** *No formal audit yet. Use at your own risk.*
//!
//! ---
//! ### API overview
//! * [`encrypt`] – passphrase → ciphertext string
//! * [`decrypt`] – ciphertext string → original secret phrase
//! * [`CryptoError`] – unified error enum
use ;
use Argon2;
use ;
pub use CryptoError;
use Zeroize;
/// Length in **bytes** of the random salt prepended to the ciphertext blob.
///
/// A 32-byte salt avoids collisions even at very high usage volumes.
pub const SALT_LEN: usize = 32;
/// Length in **bytes** of the AES-GCM nonce.
///
/// GCM requires a unique nonce per key; 96 bits (12 bytes) is the
/// recommended size and is what [`aes-gcm`] generates natively.
pub const NONCE_LEN: usize = 12;
/// Number of bytes in the derived symmetric key (`256 bits`).
pub const KEY_LEN: usize = 32;
/// The key is automatically zeroed.
pub type Key = ;
/// Encrypt UTF-8 data with a **password**, returning a single Base64URL
/// string (`no = padding`) that embeds salt, nonce, and ciphertext.
///
/// The blob layout is
/// `salt[32] | nonce[12] | ciphertext[variable]` → Base64URL.
///
/// ### Errors
/// * `CryptoError::Argon2` – key derivation failed
/// * `CryptoError::Aes256Gcm` – encryption failure (rare)
///
/// ### Example
/// ```rust
/// let blob = encryptor::encrypt("hello 🦀", "PwD!")?;
/// assert_ne!("hello 🦀", blob);
/// # Ok::<(), encryptor::CryptoError>(())
/// ```
/// Decrypt a ciphertext produced by [`encrypt`].
///
/// If the password is wrong or the blob was tampered with, decryption
/// fails with `CryptoError::Aes256Gcm`.
///
/// ### Errors
/// * `CryptoError::BaseUrlDecode` – input was not valid Base64URL
/// * `CryptoError::Decode` – blob too short / malformed
/// * `CryptoError::Argon2` – key derivation failed
/// * `CryptoError::Aes256Gcm` – authentication failed (bad password)
/// * `CryptoError::Utf8` – plaintext not valid UTF-8
///
/// ### Example
/// ```rust
/// # use encryptor::{encrypt, decrypt};
/// let blob = encrypt("secret", "pass")?;
/// assert_eq!("secret", decrypt(&blob, "pass")?);
/// assert!(decrypt(&blob, "wrong").is_err());
/// # Ok::<(), encryptor::CryptoError>(())
/// ```
/// In-memory representation of the 256-bit key returned by [`derive_key`].
///
/// Derive a 256-bit symmetric key from a user **password** and random **salt**
/// using Argon2id.
///
/// ### Errors
/// * `CryptoError::Argon2` — underlying Argon2 implementation refused the
/// parameters (e.g. not enough memory on the host).
///
/// ### Example
/// ```rust, ignore
/// let salt = [0u8; SALT_LEN];
/// let key = derive_key("correct horse battery staple", &salt)?;
/// assert_eq!(32, key.len());
/// # Ok::<(), encryptor::CryptoError>(())
/// ```