Skip to main content

stackforge_core/layer/dot15d4/
crc.rs

1//! CRC-16 CCITT Kermit implementation for IEEE 802.15.4 FCS.
2//!
3//! The 802.15.4 standard uses CRC-16 CCITT with the Kermit polynomial (0x8408),
4//! which is the bit-reversed form of the standard CCITT polynomial (0x1021).
5//! Initial value is 0x0000, and the result is stored in little-endian byte order.
6
7/// Compute CRC-16 CCITT Kermit checksum over the given data.
8///
9/// Algorithm from the Kermit Protocol Manual (June 1986).
10/// Polynomial: 0x8408 (bit-reversed 0x1021)
11/// Initial value: 0x0000
12///
13/// Returns the CRC as a u16 in native byte order.
14#[must_use]
15pub fn crc_ccitt_kermit(data: &[u8]) -> u16 {
16    let mut crc: u16 = 0;
17    for &byte in data {
18        let c = u16::from(byte);
19        // Process low-order 4 bits
20        let q = (crc ^ c) & 0x0F;
21        crc = (crc >> 4) ^ (q * 4225);
22        // Process high-order 4 bits
23        let q = (crc ^ (c >> 4)) & 0x0F;
24        crc = (crc >> 4) ^ (q * 4225);
25    }
26    crc
27}
28
29/// Compute the FCS bytes (2 bytes, little-endian) for the given data.
30#[must_use]
31pub fn compute_fcs(data: &[u8]) -> [u8; 2] {
32    crc_ccitt_kermit(data).to_le_bytes()
33}
34
35/// Verify the FCS of a frame.
36///
37/// `data` is the frame data (without FCS), and `expected_fcs` is the
38/// 2-byte FCS value read from the frame (already in native u16 order).
39#[must_use]
40pub fn verify_fcs(data: &[u8], expected_fcs: u16) -> bool {
41    crc_ccitt_kermit(data) == expected_fcs
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn test_crc_empty() {
50        assert_eq!(crc_ccitt_kermit(&[]), 0x0000);
51    }
52
53    #[test]
54    fn test_crc_known_value() {
55        // Known test vector: "123456789" should produce 0x2189 for Kermit CRC
56        let data = b"123456789";
57        assert_eq!(crc_ccitt_kermit(data), 0x2189);
58    }
59
60    #[test]
61    fn test_compute_fcs_le_bytes() {
62        let data = b"123456789";
63        let fcs = compute_fcs(data);
64        // 0x2189 in little-endian is [0x89, 0x21]
65        assert_eq!(fcs, [0x89, 0x21]);
66    }
67
68    #[test]
69    fn test_verify_fcs() {
70        let data = b"123456789";
71        assert!(verify_fcs(data, 0x2189));
72        assert!(!verify_fcs(data, 0x0000));
73    }
74
75    #[test]
76    fn test_crc_single_byte() {
77        // Single byte 0x00
78        let crc = crc_ccitt_kermit(&[0x00]);
79        assert_eq!(crc, 0x0000);
80    }
81
82    #[test]
83    fn test_crc_802154_beacon_frame() {
84        // A minimal 802.15.4 beacon frame (FCF + seqnum = 3 bytes)
85        // FCF: 0x0000 (beacon, no addressing), seqnum: 0x01
86        let data = [0x00, 0x00, 0x01];
87        let crc = crc_ccitt_kermit(&data);
88        let fcs_bytes = compute_fcs(&data);
89        // Verify round-trip
90        assert!(verify_fcs(&data, crc));
91        assert_eq!(fcs_bytes, crc.to_le_bytes());
92    }
93
94    #[test]
95    fn test_crc_deterministic() {
96        let data = [0x41, 0x88, 0x05, 0xFF, 0xFF, 0x34, 0x12];
97        let crc1 = crc_ccitt_kermit(&data);
98        let crc2 = crc_ccitt_kermit(&data);
99        assert_eq!(crc1, crc2);
100    }
101
102    #[test]
103    fn test_verify_fcs_wrong_data() {
104        let data1 = [0x01, 0x02, 0x03];
105        let crc = crc_ccitt_kermit(&data1);
106        let data2 = [0x01, 0x02, 0x04]; // different
107        assert!(!verify_fcs(&data2, crc));
108    }
109}