ham_cats/
whitener.rs

1const START_STATE: u16 = 0xE9CF;
2
3pub(crate) fn whiten(data: &mut [u8]) {
4    let mut state = START_STATE;
5
6    for d in data.iter_mut() {
7        let b;
8        (b, state) = lfsr_byte(state);
9        *d ^= b;
10    }
11}
12
13// (byte, state)
14fn lfsr_byte(mut state: u16) -> (u8, u16) {
15    let mut out = 0;
16    for i in (0..8).rev() {
17        out |= u8::try_from(state & 1).unwrap() << i;
18        state = lfsr(state);
19    }
20
21    (out, state)
22}
23
24// https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs
25fn lfsr(mut state: u16) -> u16 {
26    let lsb = state & 1;
27    state >>= 1;
28    if lsb > 0 {
29        state ^= 0xB400; // apply toggle mask
30    }
31
32    state
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn basic() {
41        let mut data = [0; 64];
42        data[0..57]
43            .clone_from_slice(&b"Hello world! The quick brown fox jumped over the lazy dog"[..]);
44        let orig = data;
45
46        whiten(&mut data);
47        assert_ne!(orig, data);
48
49        whiten(&mut data);
50        assert_eq!(orig, data);
51    }
52
53    #[test]
54    fn test_lfsr() {
55        let start = 0xACE1;
56        let end_expected = 0xE270;
57
58        let state = lfsr(start);
59
60        assert_eq!(end_expected, state);
61    }
62
63    #[test]
64    fn test_lfsr_byte() {
65        let start = 0xE9CF;
66        let (out, state) = lfsr_byte(start);
67        assert_eq!(0xF3, out);
68        assert_eq!(0xE3B1, state);
69    }
70
71    #[test]
72    fn test_doc_example() {
73        let start = 0xE9CF;
74        let expected_out = [
75            0xF3, 0x8D, 0xD0, 0x6E, 0x1F, 0x65, 0x75, 0x75, 0xA5, 0xBA, 0xA9, 0xD0, 0x7A, 0x1D,
76            0x1, 0x21,
77        ];
78
79        let mut actual_out = [0; 16];
80        let mut state = start;
81        for a in &mut actual_out {
82            let (out, ns) = lfsr_byte(state);
83            state = ns;
84            *a = out;
85        }
86
87        assert_eq!(expected_out, actual_out);
88    }
89
90    #[test]
91    fn test_doc_example_through_whitener() {
92        let expected_out = [
93            0xF3, 0x8D, 0xD0, 0x6E, 0x1F, 0x65, 0x75, 0x75, 0xA5, 0xBA, 0xA9, 0xD0, 0x7A, 0x1D,
94            0x1, 0x21,
95        ];
96
97        let mut actual_out = [0; 16];
98        whiten(&mut actual_out);
99
100        assert_eq!(expected_out, actual_out);
101    }
102}