bitfold_protocol/command_codec/
checksum.rs1use std::io;
4
5use crc32fast::Hasher;
6
7pub 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
20pub 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
28pub 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 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}