toolkit_zero/serialization/
veil.rs1use bincode::{
9 config::standard,
10 encode_to_vec, decode_from_slice,
11 Encode, Decode,
12 error::{EncodeError, DecodeError},
13};
14
15#[derive(Debug)]
19pub enum SerializationError {
20 Encode(EncodeError),
22 Decode(DecodeError),
24}
25
26impl std::fmt::Display for SerializationError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 Self::Encode(e) => write!(f, "seal encode error: {e}"),
30 Self::Decode(e) => write!(f, "open decode error: {e}"),
31 }
32 }
33}
34
35impl std::error::Error for SerializationError {}
36
37impl From<EncodeError> for SerializationError {
38 fn from(e: EncodeError) -> Self { Self::Encode(e) }
39}
40
41impl From<DecodeError> for SerializationError {
42 fn from(e: DecodeError) -> Self { Self::Decode(e) }
43}
44
45const DEFAULT_KEY: &str = "serialization/deserialization";
48const BLOCK: usize = 16;
49
50pub fn seal<T: Encode>(value: &T, key: Option<&str>) -> Result<Vec<u8>, SerializationError> {
63 let key = key.unwrap_or(DEFAULT_KEY);
64
65 let plain = encode_to_vec(value, standard())?;
67
68 let cipher = veil_encrypt(&plain, key);
70
71 let blob = encode_to_vec(&cipher, standard())?;
73 Ok(blob)
74}
75
76pub fn open<T: Decode<()>>(blob: &[u8], key: Option<&str>) -> Result<T, SerializationError> {
85 let key = key.unwrap_or(DEFAULT_KEY);
86
87 let (cipher, _): (Vec<u8>, _) = decode_from_slice(blob, standard())?;
89
90 let plain = veil_decrypt(&cipher, key)?;
92
93 let (value, _): (T, _) = decode_from_slice(&plain, standard())?;
95 Ok(value)
96}
97
98fn veil_encrypt(plain: &[u8], key: &str) -> Vec<u8> {
101 let ks = KeySchedule::new(key);
102
103 let mut buf: Vec<u8> = plain.iter().map(|&b| ks.sbox[b as usize]).collect();
105
106 for (i, b) in buf.iter_mut().enumerate() {
108 *b ^= ks.stream_byte(i);
109 }
110
111 position_mix_forward(&mut buf);
113
114 block_diffuse_forward(&mut buf);
116
117 block_shuffle_forward(&mut buf, &ks);
119
120 buf
121}
122
123fn veil_decrypt(cipher: &[u8], key: &str) -> Result<Vec<u8>, SerializationError> {
126 let ks = KeySchedule::new(key);
127
128 let mut buf = cipher.to_vec();
129
130 block_shuffle_reverse(&mut buf, &ks);
132
133 block_diffuse_reverse(&mut buf);
135
136 position_mix_reverse(&mut buf);
138
139 for (i, b) in buf.iter_mut().enumerate() {
141 *b ^= ks.stream_byte(i);
142 }
143
144 for b in buf.iter_mut() {
146 *b = ks.sbox_inv[*b as usize];
147 }
148
149 Ok(buf)
150}
151
152struct KeySchedule {
158 sbox: [u8; 256],
160 sbox_inv: [u8; 256],
162 stream_seed: u64,
164 shuffle_seed: u64,
166}
167
168impl KeySchedule {
169 fn new(key: &str) -> Self {
170 let h0 = fnv1a_64(key.as_bytes());
172 let h1 = splitmix64(h0 ^ 0xdeadbeef_cafebabe);
173
174 let mut sbox: [u8; 256] = std::array::from_fn(|i| i as u8);
176 let mut rng = Rng::new(h0);
177 for i in (1..256usize).rev() {
178 let j = (rng.next() as usize) % (i + 1);
179 sbox.swap(i, j);
180 }
181
182 let mut sbox_inv = [0u8; 256];
184 for (i, &v) in sbox.iter().enumerate() {
185 sbox_inv[v as usize] = i as u8;
186 }
187
188 Self {
189 sbox,
190 sbox_inv,
191 stream_seed: h1,
192 shuffle_seed: splitmix64(h1 ^ 0x1234567890abcdef),
193 }
194 }
195
196 #[inline]
201 fn stream_byte(&self, pos: usize) -> u8 {
202 let salted = self.stream_seed
203 .wrapping_add(pos as u64)
204 .wrapping_mul(0x9e3779b97f4a7c15);
205 let v = splitmix64(salted);
206 ((v >> 32) ^ (v & 0xffff_ffff)) as u8
208 }
209
210 fn block_perm_len(&self, block_index: usize, len: usize) -> Vec<usize> {
216 let seed = self.shuffle_seed
217 .wrapping_add(block_index as u64)
218 .wrapping_mul(0x6c62272e07bb0142);
219 let mut perm: Vec<usize> = (0..len).collect();
220 let mut rng = Rng::new(splitmix64(seed));
221 for i in (1..len).rev() {
222 let j = (rng.next() as usize) % (i + 1);
223 perm.swap(i, j);
224 }
225 perm
226 }
227
228 fn block_perm(&self, block_index: usize) -> [usize; BLOCK] {
230 let v = self.block_perm_len(block_index, BLOCK);
231 std::array::from_fn(|i| v[i])
232 }
233}
234
235struct Rng(u64);
238
239impl Rng {
240 fn new(seed: u64) -> Self { Self(seed) }
241 #[inline]
242 fn next(&mut self) -> u64 {
243 self.0 = splitmix64(self.0);
244 self.0
245 }
246}
247
248#[inline]
250fn splitmix64(mut x: u64) -> u64 {
251 x = x.wrapping_add(0x9e3779b97f4a7c15);
252 x = (x ^ (x >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
253 x = (x ^ (x >> 27)).wrapping_mul(0x94d049bb133111eb);
254 x ^ (x >> 31)
255}
256
257#[inline]
259fn fnv1a_64(data: &[u8]) -> u64 {
260 const OFFSET: u64 = 0xcbf29ce484222325;
261 const PRIME: u64 = 0x00000100000001b3;
262 let mut h = OFFSET;
263 for &b in data {
264 h ^= b as u64;
265 h = h.wrapping_mul(PRIME);
266 }
267 h
268}
269
270fn position_mix_forward(buf: &mut [u8]) {
277 let mut prev_orig: u8 = 0xA7;
281 for (i, b) in buf.iter_mut().enumerate() {
282 let original = *b;
283 let mix = (i as u8).wrapping_add(prev_orig).wrapping_mul(0x6b);
284 *b ^= mix;
285 prev_orig = original;
286 }
287}
288
289fn position_mix_reverse(buf: &mut [u8]) {
290 let mut prev_orig: u8 = 0xA7;
293 for (i, b) in buf.iter_mut().enumerate() {
294 let mix = (i as u8).wrapping_add(prev_orig).wrapping_mul(0x6b);
295 let original = *b ^ mix;
296 *b = original;
297 prev_orig = original;
298 }
299}
300
301fn block_diffuse_forward(buf: &mut [u8]) {
308 let mut acc: u8 = 0xB3;
309 for b in buf.iter_mut() {
310 let out = *b ^ acc;
311 acc = acc.rotate_left(3) ^ out;
312 *b = out;
313 }
314}
315
316fn block_diffuse_reverse(buf: &mut [u8]) {
317 let mut acc: u8 = 0xB3;
321 for b in buf.iter_mut() {
322 let out = *b;
323 let original = out ^ acc;
324 acc = acc.rotate_left(3) ^ out;
325 *b = original;
326 }
327}
328
329fn block_shuffle_forward(buf: &mut [u8], ks: &KeySchedule) {
336 let n = buf.len();
337 let full_blocks = n / BLOCK;
338 for bi in 0..full_blocks {
339 let perm = ks.block_perm(bi);
340 let base = bi * BLOCK;
341 let block: [u8; BLOCK] = std::array::from_fn(|i| buf[base + i]);
342 for i in 0..BLOCK {
343 buf[base + i] = block[perm[i]];
344 }
345 }
346 let tail_start = full_blocks * BLOCK;
348 let tail_len = n - tail_start;
349 if tail_len > 1 {
350 let perm = ks.block_perm_len(full_blocks, tail_len);
351 let tail: Vec<u8> = buf[tail_start..].to_vec();
352 for i in 0..tail_len {
353 buf[tail_start + i] = tail[perm[i]];
354 }
355 }
356}
357
358fn block_shuffle_reverse(buf: &mut [u8], ks: &KeySchedule) {
359 let n = buf.len();
360 let full_blocks = n / BLOCK;
361 for bi in 0..full_blocks {
362 let perm = ks.block_perm(bi);
363 let base = bi * BLOCK;
364 let block: [u8; BLOCK] = std::array::from_fn(|i| buf[base + i]);
365 let mut orig = [0u8; BLOCK];
366 for i in 0..BLOCK {
367 orig[perm[i]] = block[i];
368 }
369 buf[base..base + BLOCK].copy_from_slice(&orig);
370 }
371 let tail_start = full_blocks * BLOCK;
373 let tail_len = n - tail_start;
374 if tail_len > 1 {
375 let perm = ks.block_perm_len(full_blocks, tail_len);
376 let tail: Vec<u8> = buf[tail_start..].to_vec();
377 let mut orig = vec![0u8; tail_len];
378 for i in 0..tail_len {
379 orig[perm[i]] = tail[i];
380 }
381 buf[tail_start..].copy_from_slice(&orig);
382 }
383}
384
385#[cfg(test)]
388mod tests {
389 use super::*;
390 use bincode::{Encode, Decode};
391
392 #[derive(Encode, Decode, Debug, PartialEq)]
393 struct Point { x: f64, y: f64, label: String }
394
395 #[derive(Encode, Decode, Debug, PartialEq)]
396 struct Nested { id: u64, inner: Point, tags: Vec<String> }
397
398 #[test]
399 fn round_trip_default_key() {
400 let p = Point { x: 1.5, y: -3.0, label: "origin".into() };
401 let blob = seal(&p, None).unwrap();
402 let back: Point = open(&blob, None).unwrap();
403 assert_eq!(p, back);
404 }
405
406
407 #[test]
408 fn round_trip_custom_key() {
409 let p = Point { x: 42.0, y: 0.001, label: "custom".into() };
410 let blob = seal(&p, Some("hunter2")).unwrap();
411 let back: Point = open(&blob, Some("hunter2")).unwrap();
412 assert_eq!(p, back);
413 }
414
415 #[test]
416 fn round_trip_nested() {
417 let n = Nested {
418 id: 9999,
419 inner: Point { x: -1.0, y: 2.5, label: "nested".into() },
420 tags: vec!["a".into(), "bb".into(), "ccc".into()],
421 };
422 let blob = seal(&n, Some("nested-key")).unwrap();
423 let back: Nested = open(&blob, Some("nested-key")).unwrap();
424 assert_eq!(n, back);
425 }
426
427 #[test]
428 fn wrong_key_fails() {
429 let p = Point { x: 1.0, y: 2.0, label: "x".into() };
430 let blob = seal(&p, Some("correct")).unwrap();
431 let result: Result<Point, _> = open(&blob, Some("wrong"));
434 assert!(result.is_err());
435 }
436
437 #[test]
438 fn ciphertext_differs_from_plaintext() {
439 let p = Point { x: 0.0, y: 0.0, label: "zero".into() };
440 let plain = bincode::encode_to_vec(&p, bincode::config::standard()).unwrap();
441 let blob = seal(&p, None).unwrap();
442 assert_ne!(blob, plain);
444 }
445
446 #[test]
447 fn same_plaintext_same_key_produces_same_ciphertext() {
448 let p = Point { x: 1.0, y: 2.0, label: "det".into() };
449 let b1 = seal(&p, Some("k")).unwrap();
450 let b2 = seal(&p, Some("k")).unwrap();
451 assert_eq!(b1, b2); }
453}