diff --git a/src/whitener.rs b/src/whitener.rs
index 8832616babfdf9d1d75c2833b8fd4b3362f14de1..d5210c5834b3d835205f9eb365b77e736151715f 100644
--- a/src/whitener.rs
+++ b/src/whitener.rs
@@ -1,11 +1,33 @@
-const WHITE: [u8; 16] = [
-    0xe9, 0xcf, 0x67, 0x20, 0x19, 0x1a, 0x07, 0xdc, 0xc0, 0x72, 0x79, 0x97, 0x51, 0xf7, 0xdd, 0x93,
-];
-
 pub(crate) fn whiten(data: &mut [u8]) {
-    for (i, d) in data.iter_mut().enumerate() {
-        *d ^= WHITE[i % 15];
+    let mut state = 0xE9CF;
+
+    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)]
@@ -25,4 +47,54 @@ mod tests {
         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 i in 0..16 {
+            let (out, ns) = lfsr_byte(state);
+            state = ns;
+            actual_out[i] = 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);
+    }
 }