const START_STATE: u16 = 0xE9CF; pub(crate) fn whiten(data: &mut [u8]) { let mut state = START_STATE; for d in data.iter_mut() { let b; (b, state) = lfsr_byte(state); *d ^= b; } } // (byte, state) fn lfsr_byte(mut state: u16) -> (u8, u16) { let mut out = 0; for i in (0..8).rev() { out |= u8::try_from(state & 1).unwrap() << i; state = lfsr(state); } (out, state) } // https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs fn lfsr(mut state: u16) -> u16 { let lsb = state & 1; state >>= 1; if lsb > 0 { state ^= 0xB400; // apply toggle mask } state } #[cfg(test)] mod tests { use super::*; #[test] fn basic() { let mut data = [0; 64]; data[0..57] .clone_from_slice(&b"Hello world! The quick brown fox jumped over the lazy dog"[..]); let orig = data; whiten(&mut data); assert_ne!(orig, data); whiten(&mut data); assert_eq!(orig, data); } #[test] fn test_lfsr() { let start = 0xACE1; let end_expected = 0xE270; let state = lfsr(start); assert_eq!(end_expected, state); } #[test] fn test_lfsr_byte() { let start = 0xE9CF; let (out, state) = lfsr_byte(start); assert_eq!(0xF3, out); assert_eq!(0xE3B1, state); } #[test] fn test_doc_example() { let start = 0xE9CF; let expected_out = [ 0xF3, 0x8D, 0xD0, 0x6E, 0x1F, 0x65, 0x75, 0x75, 0xA5, 0xBA, 0xA9, 0xD0, 0x7A, 0x1D, 0x1, 0x21, ]; let mut actual_out = [0; 16]; let mut state = start; for a in &mut actual_out { let (out, ns) = lfsr_byte(state); state = ns; *a = out; } assert_eq!(expected_out, actual_out); } #[test] fn test_doc_example_through_whitener() { let expected_out = [ 0xF3, 0x8D, 0xD0, 0x6E, 0x1F, 0x65, 0x75, 0x75, 0xA5, 0xBA, 0xA9, 0xD0, 0x7A, 0x1D, 0x1, 0x21, ]; let mut actual_out = [0; 16]; whiten(&mut actual_out); assert_eq!(expected_out, actual_out); } }