bitfold_protocol/command_codec/
checksum.rs

1//! CRC32 checksum utilities for data integrity verification.
2
3use std::io;
4
5use crc32fast::Hasher;
6
7/// Appends a CRC32 checksum to the encoded packet data.
8/// Returns a new vector with the checksum appended.
9pub fn append_checksum(data: &[u8]) -> Vec<u8> {
10    let mut hasher = Hasher::new();
11    hasher.update(data);
12    let checksum = hasher.finalize();
13
14    let mut result = Vec::with_capacity(data.len() + 4);
15    result.extend_from_slice(data);
16    result.extend_from_slice(&checksum.to_be_bytes());
17    result
18}
19
20/// Appends a CRC32 checksum to the provided buffer in-place.
21pub fn append_checksum_in_place(data: &mut Vec<u8>) {
22    let mut hasher = Hasher::new();
23    hasher.update(data);
24    let checksum = hasher.finalize();
25    data.extend_from_slice(&checksum.to_be_bytes());
26}
27
28/// Validates and strips the CRC32 checksum from packet data.
29/// Returns the data without checksum if valid, or an error if checksum fails.
30pub fn validate_and_strip_checksum(data: &[u8]) -> io::Result<&[u8]> {
31    if data.len() < 4 {
32        return Err(io::Error::new(io::ErrorKind::InvalidData, "Data too short for checksum"));
33    }
34
35    let (payload, checksum_bytes) = data.split_at(data.len() - 4);
36    let received_checksum = u32::from_be_bytes([
37        checksum_bytes[0],
38        checksum_bytes[1],
39        checksum_bytes[2],
40        checksum_bytes[3],
41    ]);
42
43    let mut hasher = Hasher::new();
44    hasher.update(payload);
45    let computed_checksum = hasher.finalize();
46
47    if received_checksum != computed_checksum {
48        return Err(io::Error::new(
49            io::ErrorKind::InvalidData,
50            format!(
51                "CRC32 checksum mismatch: expected {}, got {}",
52                computed_checksum, received_checksum
53            ),
54        ));
55    }
56
57    Ok(payload)
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_checksum_append_and_validate() {
66        let data = b"Hello, world!";
67        let with_checksum = append_checksum(data);
68        assert_eq!(with_checksum.len(), data.len() + 4);
69
70        let validated = validate_and_strip_checksum(&with_checksum).unwrap();
71        assert_eq!(validated, data);
72    }
73
74    #[test]
75    fn test_checksum_validation_fails_on_corruption() {
76        let data = b"Hello, world!";
77        let mut with_checksum = append_checksum(data);
78
79        // Corrupt the checksum
80        let len = with_checksum.len();
81        with_checksum[len - 1] ^= 0xFF;
82
83        assert!(validate_and_strip_checksum(&with_checksum).is_err());
84    }
85
86    #[test]
87    fn test_checksum_validation_rejects_short_data() {
88        let data = b"Hi";
89        assert!(validate_and_strip_checksum(data).is_err());
90    }
91
92    #[test]
93    fn test_checksum_with_empty_data() {
94        let data = b"";
95        let with_checksum = append_checksum(data);
96        assert_eq!(with_checksum.len(), 4);
97
98        let validated = validate_and_strip_checksum(&with_checksum).unwrap();
99        assert_eq!(validated, data);
100    }
101
102    #[test]
103    fn test_append_checksum_in_place() {
104        let data = b"Test data";
105        let mut buffer = data.to_vec();
106        append_checksum_in_place(&mut buffer);
107
108        assert_eq!(buffer.len(), data.len() + 4);
109        let validated = validate_and_strip_checksum(&buffer).unwrap();
110        assert_eq!(validated, data);
111    }
112}