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