dstar_gateway_core/slowdata/
scrambler.rs

1//! Slow data XOR scrambler.
2//!
3//! Reference: `ircDDBGateway/Common/DStarDefines.h:111-113`
4//! (`SCRAMBLER_BYTE1 = 0x70U`, `SCRAMBLER_BYTE2 = 0x4FU`,
5//! `SCRAMBLER_BYTE3 = 0x93U`).
6
7const SCRAMBLER_KEY: [u8; 3] = [0x70, 0x4F, 0x93];
8
9/// Descramble 3 bytes of slow data using the D-STAR XOR key.
10#[must_use]
11pub const fn descramble(bytes: [u8; 3]) -> [u8; 3] {
12    [
13        bytes[0] ^ SCRAMBLER_KEY[0],
14        bytes[1] ^ SCRAMBLER_KEY[1],
15        bytes[2] ^ SCRAMBLER_KEY[2],
16    ]
17}
18
19/// Scramble 3 bytes of slow data using the D-STAR XOR key.
20///
21/// `scramble(descramble(b)) == b` for any input, since XOR is symmetric.
22#[must_use]
23pub const fn scramble(bytes: [u8; 3]) -> [u8; 3] {
24    descramble(bytes)
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30
31    #[test]
32    fn descramble_then_scramble_roundtrips() {
33        // Exhaustive over the first 4096 possible inputs is too much.
34        // Pick a sampling of byte patterns.
35        for a in [0u8, 1, 0x42, 0x7F, 0x80, 0xFE, 0xFF] {
36            for b in [0u8, 1, 0x42, 0x7F, 0x80, 0xFE, 0xFF] {
37                for c in [0u8, 1, 0x42, 0x7F, 0x80, 0xFE, 0xFF] {
38                    let input = [a, b, c];
39                    assert_eq!(
40                        scramble(descramble(input)),
41                        input,
42                        "roundtrip failed for {input:?}"
43                    );
44                }
45            }
46        }
47    }
48
49    #[test]
50    fn descramble_zero_returns_key() {
51        assert_eq!(descramble([0u8; 3]), [0x70, 0x4F, 0x93]);
52    }
53
54    #[test]
55    fn scramble_zero_returns_key() {
56        assert_eq!(scramble([0u8; 3]), [0x70, 0x4F, 0x93]);
57    }
58
59    #[test]
60    fn scramble_key_returns_zero() {
61        assert_eq!(scramble([0x70, 0x4F, 0x93]), [0; 3]);
62    }
63}