1use crate::Hasher;
30use bytes::{Buf, BufMut};
31use commonware_codec::{Error as CodecError, FixedArray, FixedSize, Read, ReadExt, Write};
32use commonware_formatting::Hex;
33use commonware_math::algebra::Random;
34use commonware_utils::{Array, Span};
35use core::{
36 fmt::{Debug, Display},
37 ops::Deref,
38};
39use rand_core::CryptoRng;
40
41const SIZE: usize = 4;
43
44const ALGORITHM: crc_fast::CrcAlgorithm = crc_fast::CrcAlgorithm::Crc32Iscsi;
46
47#[derive(Debug)]
51pub struct Crc32 {
52 inner: crc_fast::Digest,
53}
54
55impl Default for Crc32 {
56 fn default() -> Self {
57 Self {
58 inner: crc_fast::Digest::new(ALGORITHM),
59 }
60 }
61}
62
63impl Clone for Crc32 {
64 fn clone(&self) -> Self {
65 Self::default()
67 }
68}
69
70impl Crc32 {
71 #[inline]
75 pub fn checksum(data: &[u8]) -> u32 {
76 crc_fast::checksum(ALGORITHM, data) as u32
77 }
78}
79
80impl Hasher for Crc32 {
81 type Digest = Digest;
82
83 fn update(&mut self, message: &[u8]) -> &mut Self {
84 self.inner.update(message);
85 self
86 }
87
88 fn finalize(&mut self) -> Self::Digest {
89 Self::Digest::from(self.inner.finalize_reset() as u32)
90 }
91
92 fn reset(&mut self) -> &mut Self {
93 self.inner = crc_fast::Digest::new(ALGORITHM);
94 self
95 }
96}
97
98#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, FixedArray)]
100#[fixed_array(infallible)]
101#[repr(transparent)]
102pub struct Digest(pub [u8; SIZE]);
103
104#[cfg(feature = "arbitrary")]
105impl<'a> arbitrary::Arbitrary<'a> for Digest {
106 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
107 let len = u.int_in_range(0..=256)?;
109 let data = u.bytes(len)?;
110 Ok(Crc32::hash(data))
111 }
112}
113
114impl Digest {
115 #[inline]
117 pub const fn as_u32(&self) -> u32 {
118 u32::from_be_bytes(self.0)
119 }
120}
121
122impl Write for Digest {
123 fn write(&self, buf: &mut impl BufMut) {
124 self.0.write(buf);
125 }
126}
127
128impl Read for Digest {
129 type Cfg = ();
130
131 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
132 let array = <[u8; SIZE]>::read(buf)?;
133 Ok(Self(array))
134 }
135}
136
137impl FixedSize for Digest {
138 const SIZE: usize = SIZE;
139}
140
141impl Span for Digest {}
142
143impl Array for Digest {}
144
145impl From<u32> for Digest {
146 fn from(value: u32) -> Self {
147 Self(value.to_be_bytes())
148 }
149}
150
151impl AsRef<[u8]> for Digest {
152 fn as_ref(&self) -> &[u8] {
153 &self.0
154 }
155}
156
157impl Deref for Digest {
158 type Target = [u8];
159 fn deref(&self) -> &[u8] {
160 &self.0
161 }
162}
163
164impl Debug for Digest {
165 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166 write!(f, "{}", Hex(&self.0))
167 }
168}
169
170impl Display for Digest {
171 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172 write!(f, "{}", Hex(&self.0))
173 }
174}
175
176impl crate::Digest for Digest {
177 const EMPTY: Self = Self([0u8; SIZE]);
178}
179
180impl Random for Digest {
181 fn random(mut rng: impl CryptoRng) -> Self {
182 let mut array = [0u8; SIZE];
183 rng.fill_bytes(&mut array);
184 Self(array)
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::Hasher;
192 use commonware_codec::{DecodeExt, Encode};
193 use crc::{Crc, CRC_32_ISCSI};
194
195 const CRC32C_REF: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
197
198 fn verify(data: &[u8], expected: u32) {
200 assert_eq!(CRC32C_REF.checksum(data), expected);
201 assert_eq!(Crc32::checksum(data), expected);
202 }
203
204 fn sequential_data(len: usize) -> Vec<u8> {
206 (0..len).map(|i| (i & 0xFF) as u8).collect()
207 }
208
209 #[test]
212 fn rfc3720_test_vectors() {
213 verify(&[0x00; 32], 0x8A9136AA);
215
216 verify(&[0xFF; 32], 0x62A8AB43);
218
219 let ascending: Vec<u8> = (0x00..0x20).collect();
221 verify(&ascending, 0x46DD794E);
222
223 let descending: Vec<u8> = (0x00..0x20).rev().collect();
225 verify(&descending, 0x113FDB5C);
226
227 let iscsi_read_pdu: [u8; 48] = [
229 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14,
231 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 ];
234 verify(&iscsi_read_pdu, 0xD9963A56);
235 }
236
237 #[test]
242 fn external_test_vectors() {
243 verify(b"", 0x00000000);
245 verify(b"123456789", 0xE3069283);
246
247 verify(b"23456789", 0xBFE92A83);
249 verify(b"The quick brown fox jumps over the lazy dog", 0x22620404);
250
251 let sequential_240: Vec<u8> = (0x01..=0xF0).collect();
253 verify(&sequential_240, 0x24C5D375);
254 }
255
256 #[test]
261 fn simd_boundaries() {
262 const BOUNDARY_SIZES: &[usize] = &[
270 0, 1, 2, 3, 4, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 129, 255, 256, 257, 511, 512, 513, 1023, 1024, 1025, 4095, 4096, 4097, ];
280
281 const EXPECTED: &[(usize, u32)] = &[
284 (0, 0x00000000),
285 (1, 0x527D5351),
286 (2, 0x030AF4D1),
287 (3, 0x92FD4BFA),
288 (4, 0xD9331AA3),
289 (7, 0xA359ED4C),
290 (8, 0x8A2CBC3B),
291 (9, 0x7144C5A8),
292 (15, 0x68EF03F6),
293 (16, 0xD9C908EB),
294 (17, 0x38435E17),
295 (31, 0xE95CABCB),
296 (32, 0x46DD794E), (33, 0x9F85A26D),
298 (63, 0x7A873004),
299 (64, 0xFB6D36EB),
300 (65, 0x694420FA),
301 (127, 0x6C31BD0C),
302 (128, 0x30D9C515),
303 (129, 0xF514629F),
304 (255, 0x8953C482),
305 (256, 0x9C44184B),
306 (257, 0x8A13A1CE),
307 (511, 0x35348950),
308 (512, 0xAE10EE5A),
309 (513, 0x6814B154),
310 (1023, 0x0C8F24D0),
311 (1024, 0x2CDF6E8F),
312 (1025, 0x8EB48B63),
313 (4095, 0xBCB5BD82),
314 (4096, 0x9C71FE32),
315 (4097, 0x83391BE9),
316 ];
317
318 assert_eq!(
319 BOUNDARY_SIZES,
320 EXPECTED.iter().map(|(size, _)| *size).collect::<Vec<_>>()
321 );
322
323 for &(size, expected) in EXPECTED {
324 let data = sequential_data(size);
325 verify(&data, expected);
326 }
327 }
328
329 #[test]
331 fn chunk_size_independence() {
332 let data = sequential_data(1024);
333 let expected = CRC32C_REF.checksum(&data);
334
335 for chunk_size in 1..=64 {
337 let mut hasher = Crc32::new();
338 for chunk in data.chunks(chunk_size) {
339 hasher.update(chunk);
340 }
341 assert_eq!(hasher.finalize().as_u32(), expected);
342 }
343 }
344
345 #[test]
347 fn alignment_independence() {
348 let base_data: Vec<u8> = (0..256).map(|i| i as u8).collect();
350 let test_len = 64;
351
352 let reference = CRC32C_REF.checksum(&base_data[..test_len]);
354
355 for offset in 0..16 {
358 let data = &base_data[offset..offset + test_len];
359 let expected = CRC32C_REF.checksum(data);
360 assert_eq!(Crc32::checksum(data), expected);
361 }
362
363 verify(&base_data[..test_len], reference);
365 }
366
367 #[test]
368 fn test_crc32_hasher_trait() {
369 let msg = b"hello world";
370
371 let mut hasher = Crc32::new();
373 hasher.update(msg);
374 let digest = hasher.finalize();
375 assert!(Digest::decode(digest.as_ref()).is_ok());
376
377 let expected = CRC32C_REF.checksum(msg);
379 assert_eq!(digest.as_u32(), expected);
380
381 hasher.update(msg);
383 let digest2 = hasher.finalize();
384 assert_eq!(digest, digest2);
385
386 let hash = Crc32::hash(msg);
388 assert_eq!(hash.as_u32(), expected);
389 }
390
391 #[test]
392 fn test_crc32_len() {
393 assert_eq!(Digest::SIZE, SIZE);
394 assert_eq!(SIZE, 4);
395 }
396
397 #[test]
398 fn test_codec() {
399 let msg = b"hello world";
400 let mut hasher = Crc32::new();
401 hasher.update(msg);
402 let digest = hasher.finalize();
403
404 let encoded = digest.encode();
405 assert_eq!(encoded.len(), SIZE);
406 assert_eq!(encoded, digest.as_ref());
407
408 let decoded = Digest::decode(encoded).unwrap();
409 assert_eq!(digest, decoded);
410 }
411
412 #[test]
413 fn test_digest_from_u32() {
414 let value: u32 = 0xDEADBEEF;
415 let digest = Digest::from(value);
416 assert_eq!(digest.as_u32(), value);
417 assert_eq!(digest.0, [0xDE, 0xAD, 0xBE, 0xEF]);
418 }
419
420 #[test]
421 fn test_checksum_returns_u32() {
422 let checksum: u32 = Crc32::checksum(b"test");
424 let expected = CRC32C_REF.checksum(b"test");
425 assert_eq!(checksum, expected);
426 }
427
428 #[cfg(feature = "arbitrary")]
429 mod conformance {
430 use super::*;
431 use commonware_codec::conformance::CodecConformance;
432
433 commonware_conformance::conformance_tests! {
434 CodecConformance<Digest>,
435 }
436 }
437}