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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// Copyright (c) 2025-2026 Federico Hoerth <memparanoid@gmail.com>
// SPDX-License-Identifier: GPL-3.0-only
// See LICENSE in the repository root for full license text.
//! <picture>
//! <p align="center">
//! <source media="(prefers-color-scheme: dark)" width="320" srcset="https://raw.githubusercontent.com/memparanoid/redoubt/main/logo_light.png">
//! <source media="(prefers-color-scheme: light)" width="320" srcset="https://raw.githubusercontent.com/memparanoid/redoubt/main/logo_light.png">
//! <img alt="Redoubt" width="320" src="https://raw.githubusercontent.com/memparanoid/redoubt/main/logo_light.png">
//! </p>
//! </picture>
//!
//! <p align="center"><em>Systematic encryption-at-rest for in-memory sensitive data in Rust.</em></p>
//!
//! ---
//!
//! Redoubt is a Rust library for storing secrets in memory. Encrypted at rest, zeroized on drop, accessible only when you need them.
//!
//! # Features
//!
//! - โจ **Zero boilerplate** โ One macro, full protection
//! - ๐ **Ephemeral decryption** โ Secrets live encrypted, exist in plaintext only for the duration of access
//! - ๐ **No surprises** โ Allocation-free decryption with explicit zeroization on every path
//! - ๐งน **Automatic zeroization** โ Memory is wiped when secrets go out of scope
//! - โก **Amazingly fast** โ Powered by AEGIS-128L encryption, bit-level encoding, and decrypt-only-what-you-need
//! - ๐ก๏ธ **OS-level protection** โ Memory locking and protection against dumps
//! - ๐ฏ **Field-level access** โ Decrypt only the field you need, not the entire struct
//! - ๐ฆ **`no_std` compatible** โ Works in embedded and WASI environments
//!
//! # Installation
//!
//! ```bash
//! cargo add redoubt --features full
//! ```
//!
//! Or in your `Cargo.toml`:
//! ```toml
//! [dependencies]
//! redoubt = { version = "0.1.0-rc.4", features = ["full"] }
//! ```
//!
//! # Quick Start
//!
//! ```rust
//! use redoubt::alloc::{RedoubtArray, RedoubtString};
//! use redoubt::codec::RedoubtCodec;
//! use redoubt::secret::RedoubtSecret;
//! use redoubt::vault::cipherbox;
//! use redoubt::zero::RedoubtZero;
//!
//! #[cipherbox(Wallet)]
//! #[derive(Default, RedoubtCodec, RedoubtZero)]
//! struct WalletData {
//! seed: RedoubtArray<u8, 32>,
//! mnemonic: RedoubtString,
//! counter: RedoubtSecret<u64>,
//! }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut wallet = Wallet::new();
//!
//! // Open the box and modify secrets
//! wallet.open_mut(|w| {
//! w.seed.replace_from_mut_array(&mut [0u8; 32]);
//!
//! let mut mnemonic = String::from("abandon abandon ...");
//! w.mnemonic.replace_from_mut_string(&mut mnemonic);
//!
//! w.counter.replace(&mut 0u64);
//!
//! Ok(())
//! })?;
//! // Box is re-encrypted here
//!
//! // Read-only access
//! wallet.open(|w| {
//! let _ = w.counter.as_ref();
//! Ok(())
//! })?;
//!
//! // Field-level access (decrypts only that field)
//! wallet.open_counter_mut(|counter| {
//! let mut next = *counter.as_ref() + 1;
//! counter.replace(&mut next);
//!
//! Ok(())
//! })?;
//!
//! // Leak to use outside closure scope
//! {
//! let seed = wallet.leak_seed()?;
//! // use seed...
//! } // seed is zeroized on drop
//!
//! Ok(())
//! }
//! ```
//!
//! # API
//!
//! ## `open` / `open_mut`
//!
//! Access the entire decrypted struct. Re-encrypts when the closure returns:
//!
//! ```rust
//! # use redoubt::codec::RedoubtCodec;
//! # use redoubt::secret::RedoubtSecret;
//! # use redoubt::vault::cipherbox;
//! # use redoubt::zero::RedoubtZero;
//! # #[cipherbox(Wallet)]
//! # #[derive(Default, RedoubtCodec, RedoubtZero)]
//! # struct WalletData { counter: RedoubtSecret<u64> }
//! # let mut wallet = Wallet::new();
//! wallet.open(|w| {
//! // read-only access to all fields
//! Ok(())
//! })?;
//!
//! wallet.open_mut(|w| {
//! // read-write access to all fields
//! Ok(())
//! })?;
//! # Ok::<(), redoubt::vault::CipherBoxError>(())
//! ```
//!
//! ## `open_<field>` / `open_<field>_mut`
//!
//! Access individual fields without decrypting the entire struct:
//!
//! ```rust
//! # use redoubt::alloc::RedoubtArray;
//! # use redoubt::codec::RedoubtCodec;
//! # use redoubt::vault::cipherbox;
//! # use redoubt::zero::RedoubtZero;
//! # #[cipherbox(Wallet)]
//! # #[derive(Default, RedoubtCodec, RedoubtZero)]
//! # struct WalletData { seed: RedoubtArray<u8, 32> }
//! # let mut wallet = Wallet::new();
//! wallet.open_seed(|seed| {
//! // read-only access to seed
//! Ok(())
//! })?;
//!
//! wallet.open_seed_mut(|seed| {
//! // read-write access to seed
//! Ok(())
//! })?;
//! # Ok::<(), redoubt::vault::CipherBoxError>(())
//! ```
//!
//! ## `leak_<field>`
//!
//! Get a field value outside the closure. Returns a `ZeroizingGuard` that wipes memory on drop:
//!
//! ```rust
//! # use redoubt::alloc::RedoubtArray;
//! # use redoubt::codec::RedoubtCodec;
//! # use redoubt::vault::cipherbox;
//! # use redoubt::zero::RedoubtZero;
//! # #[cipherbox(Wallet)]
//! # #[derive(Default, RedoubtCodec, RedoubtZero)]
//! # struct WalletData { seed: RedoubtArray<u8, 32> }
//! # let mut wallet = Wallet::new();
//! let seed = wallet.leak_seed()?;
//! // use seed...
//! // seed is zeroized when dropped
//! # Ok::<(), redoubt::vault::CipherBoxError>(())
//! ```
//!
//! ## Returning values
//!
//! Closures can return values. The return value is wrapped in a `ZeroizingGuard` that wipes memory on drop:
//!
//! ```rust
//! # use redoubt::codec::RedoubtCodec;
//! # use redoubt::secret::RedoubtSecret;
//! # use redoubt::vault::cipherbox;
//! # use redoubt::zero::RedoubtZero;
//! # #[cipherbox(Wallet)]
//! # #[derive(Default, RedoubtCodec, RedoubtZero)]
//! # struct WalletData { counter: RedoubtSecret<u64> }
//! # let mut wallet = Wallet::new();
//! let counter = wallet.open_counter_mut(|c| {
//! let mut next = *c.as_ref() + 1;
//! c.replace(&mut next);
//!
//! Ok(next)
//! })?; // Returns Result<ZeroizingGuard<u64>, CipherBoxError>
//! // counter is zeroized when dropped
//! # Ok::<(), redoubt::vault::CipherBoxError>(())
//! ```
//!
//! # Types
//!
//! Redoubt provides secure containers for different use cases:
//!
//! ```rust
//! use redoubt::alloc::{RedoubtArray, RedoubtString, RedoubtVec};
//! use redoubt::secret::RedoubtSecret;
//!
//! // Fixed-size arrays (automatically zeroized on drop)
//! let mut api_key = RedoubtArray::<u8, 32>::new();
//! let mut signing_key = RedoubtArray::<u8, 64>::new();
//!
//! // Dynamic collections (zeroized on realloc and drop)
//! let mut tokens = RedoubtVec::<u8>::new();
//! let mut password = RedoubtString::new();
//!
//! // Primitives wrapped in Secret
//! let mut counter = RedoubtSecret::from(&mut 0u64);
//! let mut timestamp = RedoubtSecret::from(&mut 0i64);
//! ```
//!
//! ## When to use each type
//!
//! - **`RedoubtArray<T, N>`**: Fixed-size sensitive data (keys, hashes, seeds). Size known at compile time.
//! - **`RedoubtVec<T>`**: Variable-length byte arrays that may grow (encrypted tokens, variable-size keys).
//! - **`RedoubtString`**: Variable-length UTF-8 strings (passwords, mnemonics, API keys).
//! - **`RedoubtSecret<T>`**: Primitive types (u64, i32, bool) that need protection. Prevents accidental copies via controlled access.
//!
//! ## โ ๏ธ Critical: CipherBox fields MUST come from these types
//!
//! **All sensitive data in `#[cipherbox]` structs MUST ultimately come from: `RedoubtArray`, `RedoubtVec`, `RedoubtString`, or `RedoubtSecret`.**
//!
//! These types were forensically validated to leave no traces during the encryption-at-rest workflow.
//! You can compose them into nested structures, but the leaf values containing sensitive data must be these types.
//! Using standard types (`Vec<u8>`, `String`, `[u8; 32]`, `u64`) would leave unzeroized copies during encoding/decoding, defeating the security guarantees.
//!
//! ## How they prevent traces
//!
//! **`RedoubtVec` / `RedoubtString`**: Pre-zeroize old allocation before reallocation
//! - When capacity is exceeded, performs a safe 3-step reallocation:
//! 1. Copy data to temporary buffer
//! 2. Zeroize old allocation completely
//! 3. Allocate new buffer with 2x capacity and copy from temp (zeroizing temp)
//! - Safe methods: `extend_from_mut_slice`, `extend_from_mut_string` (zeroize source)
//! - **~40% performance penalty** for guaranteed security (double allocation during growth)
//! - Without this, standard `Vec`/`String` leave copies in abandoned allocations
//!
//! **`RedoubtArray`**: Prevents copies during assignment
//! - Simply redeclaring arrays (`let arr2 = arr1;`) can leave copies on stack
//! - `replace_from_mut_array` uses `ptr::swap_nonoverlapping` to exchange contents without intermediate copies
//! - Zeroizes the source after swap, ensuring no plaintext remains
//!
//! **`RedoubtSecret`**: Prevents accidental dereferencing of Copy types
//! - Forces explicit `as_ref()`/`as_mut()` calls to access the inner value
//! - Critical for primitives like `u64` which implement `Copy` and could silently duplicate if accessed directly
//!
//! ## Protections
//!
//! - All types implement `Debug` with **REDACTED** output (no accidental leaks in logs)
//! - **No `Copy` or `Clone`** traits (prevents unintended copies of sensitive data)
//! - Automatic zeroization on drop
//!
//! # Security
//!
//! - **Encryption at rest**: Sensitive data uses AEAD encryption (AEGIS-128L)
//! - **Guaranteed zeroization**: Memory is wiped using compiler barriers that prevent optimization
//! - **OS-level protections**: On Linux, the master key lives in a memory page protected by `prctl` and `mlock`, inaccessible to non-root memory dumps
//! - **Field-level encryption**: Decrypt only what you need, minimizing exposure time
//!
//! # Testing
//!
//! CipherBox generates failure injection methods for testing error handling:
//!
//! ```rust,ignore
//! #[cipherbox(WalletBox, testing_feature = "test-utils")]
//! struct Wallet { /* ... */ }
//!
//! // In tests:
//! let mut wallet = WalletBox::new();
//! wallet.set_failure_mode(WalletBoxFailureMode::FailOnNthOperation(2));
//!
//! assert!(wallet.open(|_| Ok(())).is_ok()); // 1st succeeds
//! assert!(wallet.open(|_| Ok(())).is_err()); // 2nd fails
//! ```
//!
//! - In the same crate, test utilities are always available under `#[cfg(test)]`
//! - For external crates, use `testing_feature` to export them conditionally
//!
//! See [examples/wallet/tests](https://github.com/memparanoid/redoubt/tree/main/examples/wallet/tests) for a complete example.
//!
//! # Platform support
//!
//! | Platform | Protection level |
//! |----------|------------------|
//! | Linux | Full (`prctl`, `rlimit`, `mlock`, `mprotect`) |
//! | macOS | Partial (`mlock`, `mprotect`) |
//! | Windows | Encryption only |
//! | WASI | Encryption only |
//! | `no_std` | Encryption only |
//!
//! # License
//!
//! GPL-3.0-only
pub use redoubt_aead as aead;
pub use redoubt_alloc as alloc;
pub use redoubt_guard as guard;
pub use redoubt_codec as codec;
pub use redoubt_hkdf as hkdf;
pub use redoubt_rand as rand;
pub use redoubt_secret as secret;
pub use redoubt_vault as vault;
pub use redoubt_zero as zero;