1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
3
4#[cfg(feature = "hashstrings")]
7mod hashstrings;
8#[cfg(feature = "hashstrings")]
9pub use hashstrings::*;
10
11#[cfg(not(feature = "std"))]
12extern crate core;
13
14#[cfg(not(feature = "std"))]
15extern crate alloc;
16
17#[cfg(not(feature = "std"))]
18use alloc::{string::String, vec::Vec};
19#[cfg(not(feature = "std"))]
20use core::ops::{Deref, DerefMut};
21#[cfg(feature = "std")]
22use std::ops::{Deref, DerefMut};
23
24use rand::{RngCore, SeedableRng, rngs::SmallRng};
25use zeroize::Zeroize;
26
27pub struct Encrusted<T>
33where
34 T: Encrustable + Zeroize,
35{
36 data: T,
37 seed: u64,
38}
39
40impl<T> Encrusted<T>
41where
42 T: Encrustable + Zeroize,
43{
44 pub fn new(mut data: T, seed: u64) -> Self {
46 let mut encrust_rng = SmallRng::seed_from_u64(seed);
47
48 unsafe {
52 data.toggle_encrust(&mut encrust_rng);
53 }
54
55 Self { data, seed }
56 }
57
58 #[doc(hidden)]
65 #[cfg(feature = "macros")]
66 pub const unsafe fn from_encrusted_data(data: T, seed: u64) -> Self {
67 Self { data, seed }
68 }
69
70 pub fn reseed(&mut self, new_seed: u64) {
72 {
73 let mut decruster = SmallRng::seed_from_u64(self.seed);
74
75 unsafe {
78 self.data.toggle_encrust(&mut decruster);
79 }
80 }
81
82 self.seed = new_seed;
83
84 let mut encrust_rng = SmallRng::seed_from_u64(self.seed);
85
86 unsafe {
89 self.data.toggle_encrust(&mut encrust_rng);
90 }
91 }
92
93 pub fn decrust(&mut self) -> Decrusted<'_, T> {
96 Decrusted::new(self)
97 }
98}
99
100impl<T> Drop for Encrusted<T>
101where
102 T: Encrustable + Zeroize,
103{
104 fn drop(&mut self) {
110 self.data.zeroize();
111 self.seed.zeroize();
112 }
113}
114
115pub struct Decrusted<'decrusted, T>
119where
120 T: Encrustable + Zeroize,
121{
122 encrusted_data: &'decrusted mut Encrusted<T>,
123}
124
125impl<'decrusted, T> Decrusted<'decrusted, T>
126where
127 T: Encrustable + Zeroize,
128{
129 fn new(encrusted_data: &'decrusted mut Encrusted<T>) -> Self {
130 let mut decruster = SmallRng::seed_from_u64(encrusted_data.seed);
131
132 unsafe {
136 encrusted_data.data.toggle_encrust(&mut decruster);
137 }
138
139 Self { encrusted_data }
140 }
141}
142
143impl<T> Drop for Decrusted<'_, T>
144where
145 T: Encrustable + Zeroize,
146{
147 fn drop(&mut self) {
148 let mut encrust_rng = SmallRng::seed_from_u64(self.encrusted_data.seed);
149
150 unsafe {
155 self.encrusted_data.data.toggle_encrust(&mut encrust_rng);
156 }
157 }
158}
159
160impl<T> Deref for Decrusted<'_, T>
161where
162 T: Encrustable + Zeroize,
163{
164 type Target = T;
165
166 fn deref(&self) -> &Self::Target {
167 &self.encrusted_data.data
168 }
169}
170
171impl<T> DerefMut for Decrusted<'_, T>
172where
173 T: Encrustable + Zeroize,
174{
175 fn deref_mut(&mut self) -> &mut Self::Target {
176 &mut self.encrusted_data.data
177 }
178}
179
180pub trait Encrustable {
183 unsafe fn toggle_encrust(&mut self, encrust_rng: &mut impl RngCore);
191}
192
193macro_rules! encrustable_number {
194 ( $( $t:ty ),* ) => {
195 $(
196 impl Encrustable for $t {
197 unsafe fn toggle_encrust(&mut self, encrust_rng: &mut impl ::rand::RngCore) {
198 let mut bytes = self.to_le_bytes();
199
200 let mut key: [u8; 8] = [0; 8];
203 for chunk in bytes.chunks_mut(8) {
204 encrust_rng.fill_bytes(&mut key);
205 for (byte, byte_key) in chunk.iter_mut().zip(key.iter()) {
206 *byte ^= byte_key;
207 }
208 }
209
210 *self = Self::from_le_bytes(bytes);
211 }
212 }
213 )*
214 };
215}
216
217encrustable_number!(
218 u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize
219);
220
221impl Encrustable for String {
222 unsafe fn toggle_encrust(&mut self, encrust_rng: &mut impl RngCore) {
223 let bytes = unsafe { self.as_mut_vec() };
226
227 let mut key: [u8; 16] = [0; 16];
230 for chunk in bytes.chunks_mut(16) {
231 encrust_rng.fill_bytes(&mut key);
232 for (byte, byte_key) in chunk.iter_mut().zip(key.iter()) {
233 *byte ^= byte_key;
234 }
235 }
236 }
237}
238
239impl<T, const N: usize> Encrustable for [T; N]
240where
241 T: Encrustable,
242{
243 unsafe fn toggle_encrust(&mut self, encrust_rng: &mut impl RngCore) {
244 for element in self {
245 unsafe {
248 element.toggle_encrust(encrust_rng);
249 }
250 }
251 }
252}
253
254impl<T> Encrustable for Vec<T>
255where
256 T: Encrustable,
257{
258 unsafe fn toggle_encrust(&mut self, encrust_rng: &mut impl RngCore) {
259 for element in self {
260 unsafe {
263 element.toggle_encrust(encrust_rng);
264 }
265 }
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 const TEST_STRING: &str = "The quick brown fox jumps over the lazy dog😊";
274
275 fn get_seed() -> u64 {
276 0x2357_bd11_1317_1d1f
277 }
278
279 macro_rules! test_ints {
280 ( $( $t:ty ),* ) => {
281 $(
282 {
283 let mut encrusted = Encrusted::<$t>::new(0, get_seed());
284 assert_ne!(encrusted.data, 0);
285
286 {
287 let decrusted = encrusted.decrust();
288 assert_eq!(*decrusted, 0);
289 }
290
291 assert_ne!(encrusted.data, 0);
292 }
293
294 {
295 let seed = get_seed();
296 let mut encrust_rng = SmallRng::seed_from_u64(seed);
297 let mut encrusted_data: $t = 0;
298
299 let mut encrusted = unsafe {
303 encrusted_data.toggle_encrust(&mut encrust_rng);
304 Encrusted::<$t>::from_encrusted_data(encrusted_data, seed)
305 };
306
307 assert_ne!(encrusted.data, 0);
308
309 {
310 let decrusted = encrusted.decrust();
311 assert_eq!(*decrusted, 0);
312 }
313
314 assert_ne!(encrusted.data, 0);
315 }
316 )*
317 };
318 }
319
320 #[test]
321 fn test_ints() {
322 test_ints!(
323 u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize
324 );
325 }
326
327 #[test]
328 fn test_strings() {
329 let mut encrusted = Encrusted::new(TEST_STRING.to_string(), get_seed());
330 assert_ne!(encrusted.data.as_bytes(), TEST_STRING.as_bytes());
331
332 {
333 let decrusted = encrusted.decrust();
334 assert_eq!(*decrusted, TEST_STRING);
335 }
336
337 assert_ne!(encrusted.data.as_bytes(), TEST_STRING.as_bytes());
338 }
339
340 #[test]
341 fn test_strings_from_encrusted() {
342 let seed = get_seed();
343 let mut encrust_rng = SmallRng::seed_from_u64(seed);
344
345 let mut encrusted_string = TEST_STRING.to_string();
346
347 let mut encrusted = unsafe {
350 encrusted_string.toggle_encrust(&mut encrust_rng);
351 Encrusted::from_encrusted_data(encrusted_string, seed)
352 };
353
354 assert_ne!(encrusted.data.as_bytes(), TEST_STRING.as_bytes());
355
356 {
357 let decrusted = encrusted.decrust();
358 assert_eq!(*decrusted, TEST_STRING);
359 }
360
361 assert_ne!(encrusted.data.as_bytes(), TEST_STRING.as_bytes());
362 }
363
364 #[test]
365 fn test_arrays() {
366 let orig_array: [u8; 45] = [
367 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
368 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
369 ];
370
371 let mut encrusted = Encrusted::new(orig_array, get_seed());
372 assert_ne!(encrusted.data, orig_array);
373
374 {
375 let decrusted = encrusted.decrust();
376 assert_eq!(*decrusted, orig_array);
377 }
378
379 assert_ne!(encrusted.data, orig_array);
380 }
381
382 #[test]
383 fn test_arrays_from_encrusted() {
384 let seed = get_seed();
385 let mut encrust_rng = SmallRng::seed_from_u64(seed);
386 let orig_array: [u8; 45] = [
387 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
388 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
389 ];
390
391 let mut encrusted_array = orig_array;
392
393 let mut encrusted = unsafe {
396 encrusted_array.toggle_encrust(&mut encrust_rng);
397 Encrusted::from_encrusted_data(encrusted_array, seed)
398 };
399
400 assert_ne!(encrusted.data, orig_array);
401
402 {
403 let decrusted = encrusted.decrust();
404 assert_eq!(*decrusted, orig_array);
405 }
406
407 assert_ne!(encrusted.data, orig_array);
408 }
409
410 #[test]
411 fn test_vecs() {
412 let orig_vec = TEST_STRING.as_bytes().to_vec();
413
414 let mut encrusted = Encrusted::new(orig_vec.clone(), get_seed());
415 assert_ne!(encrusted.data, orig_vec);
416
417 {
418 let decrusted = encrusted.decrust();
419 assert_eq!(*decrusted, orig_vec);
420 }
421
422 assert_ne!(encrusted.data, orig_vec);
423 }
424
425 #[test]
426 fn test_vecs_from_encrusted() {
427 let seed = get_seed();
428 let mut encrust_rng = SmallRng::seed_from_u64(seed);
429 let orig_vec = TEST_STRING.as_bytes().to_vec();
430
431 let mut encrusted_vec = orig_vec.clone();
432
433 let mut encrusted = unsafe {
436 encrusted_vec.toggle_encrust(&mut encrust_rng);
437 Encrusted::from_encrusted_data(encrusted_vec, seed)
438 };
439
440 assert_ne!(encrusted.data, orig_vec);
441
442 {
443 let decrusted = encrusted.decrust();
444 assert_eq!(*decrusted, orig_vec);
445 }
446
447 assert_ne!(encrusted.data, orig_vec);
448 }
449
450 #[test]
451 fn test_reseed() {
452 let num = 828_627_825_u64;
453 let mut encrusted = Encrusted::new(num, get_seed());
454 let orig_seed = encrusted.seed;
455 let mut rng = rand::rng();
456
457 encrusted.reseed(rng.next_u64());
458
459 assert_ne!(encrusted.seed, orig_seed);
462
463 {
464 let decrusted = encrusted.decrust();
465 assert_eq!(*decrusted, num);
466 }
467 }
468
469 #[test]
472 fn ensure_encrust_has_not_changed() {
473 let mut test_string = unsafe {
476 Encrusted::from_encrusted_data(
477 String::from_utf8_unchecked(
478 [
479 55u8, 10u8, 35u8, 94u8, 130u8, 81u8, 207u8, 225u8, 64u8, 17u8, 143u8, 78u8,
480 95u8, 204u8, 50u8, 183u8, 54u8, 185u8, 59u8, 50u8, 163u8, 122u8, 131u8,
481 136u8, 172u8, 79u8, 17u8, 12u8, 56u8, 64u8, 59u8, 173u8, 102u8, 54u8,
482 184u8, 186u8, 1u8, 246u8, 193u8, 136u8, 220u8, 224u8, 117u8, 144u8, 131u8,
483 65u8, 77u8,
484 ]
485 .to_vec(),
486 ),
487 #[allow(
488 clippy::unreadable_literal,
489 reason = "Arbitrary number chosen at random with no further meaning."
490 )]
491 5233902475398815152u64,
492 )
493 };
494
495 let decrusted_test_string = test_string.decrust();
496 assert_eq!(*decrusted_test_string, TEST_STRING);
497 }
498}