1use crate::Hasher;
30use bytes::{Buf, BufMut};
31use commonware_codec::{Error as CodecError, 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::CryptoRngCore;
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)]
100#[repr(transparent)]
101pub struct Digest(pub [u8; SIZE]);
102
103#[cfg(feature = "arbitrary")]
104impl<'a> arbitrary::Arbitrary<'a> for Digest {
105 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
106 let len = u.int_in_range(0..=256)?;
108 let data = u.bytes(len)?;
109 Ok(Crc32::hash(data))
110 }
111}
112
113impl Digest {
114 #[inline]
116 pub const fn as_u32(&self) -> u32 {
117 u32::from_be_bytes(self.0)
118 }
119}
120
121impl Write for Digest {
122 fn write(&self, buf: &mut impl BufMut) {
123 self.0.write(buf);
124 }
125}
126
127impl Read for Digest {
128 type Cfg = ();
129
130 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
131 let array = <[u8; SIZE]>::read(buf)?;
132 Ok(Self(array))
133 }
134}
135
136impl FixedSize for Digest {
137 const SIZE: usize = SIZE;
138}
139
140impl Span for Digest {}
141
142impl Array for Digest {}
143
144impl From<[u8; SIZE]> for Digest {
145 fn from(value: [u8; SIZE]) -> Self {
146 Self(value)
147 }
148}
149
150impl From<u32> for Digest {
151 fn from(value: u32) -> Self {
152 Self(value.to_be_bytes())
153 }
154}
155
156impl AsRef<[u8]> for Digest {
157 fn as_ref(&self) -> &[u8] {
158 &self.0
159 }
160}
161
162impl Deref for Digest {
163 type Target = [u8];
164 fn deref(&self) -> &[u8] {
165 &self.0
166 }
167}
168
169impl Debug for Digest {
170 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
171 write!(f, "{}", Hex(&self.0))
172 }
173}
174
175impl Display for Digest {
176 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
177 write!(f, "{}", Hex(&self.0))
178 }
179}
180
181impl crate::Digest for Digest {
182 const EMPTY: Self = Self([0u8; SIZE]);
183}
184
185impl Random for Digest {
186 fn random(mut rng: impl CryptoRngCore) -> Self {
187 let mut array = [0u8; SIZE];
188 rng.fill_bytes(&mut array);
189 Self(array)
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::Hasher;
197 use commonware_codec::{DecodeExt, Encode};
198 use crc::{Crc, CRC_32_ISCSI};
199
200 const CRC32C_REF: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
202
203 fn verify(data: &[u8], expected: u32) {
205 assert_eq!(CRC32C_REF.checksum(data), expected);
206 assert_eq!(Crc32::checksum(data), expected);
207 }
208
209 fn sequential_data(len: usize) -> Vec<u8> {
211 (0..len).map(|i| (i & 0xFF) as u8).collect()
212 }
213
214 #[test]
217 fn rfc3720_test_vectors() {
218 verify(&[0x00; 32], 0x8A9136AA);
220
221 verify(&[0xFF; 32], 0x62A8AB43);
223
224 let ascending: Vec<u8> = (0x00..0x20).collect();
226 verify(&ascending, 0x46DD794E);
227
228 let descending: Vec<u8> = (0x00..0x20).rev().collect();
230 verify(&descending, 0x113FDB5C);
231
232 let iscsi_read_pdu: [u8; 48] = [
234 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14,
236 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 ];
239 verify(&iscsi_read_pdu, 0xD9963A56);
240 }
241
242 #[test]
247 fn external_test_vectors() {
248 verify(b"", 0x00000000);
250 verify(b"123456789", 0xE3069283);
251
252 verify(b"23456789", 0xBFE92A83);
254 verify(b"The quick brown fox jumps over the lazy dog", 0x22620404);
255
256 let sequential_240: Vec<u8> = (0x01..=0xF0).collect();
258 verify(&sequential_240, 0x24C5D375);
259 }
260
261 #[test]
266 fn simd_boundaries() {
267 const BOUNDARY_SIZES: &[usize] = &[
275 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, ];
285
286 const EXPECTED: &[(usize, u32)] = &[
289 (0, 0x00000000),
290 (1, 0x527D5351),
291 (2, 0x030AF4D1),
292 (3, 0x92FD4BFA),
293 (4, 0xD9331AA3),
294 (7, 0xA359ED4C),
295 (8, 0x8A2CBC3B),
296 (9, 0x7144C5A8),
297 (15, 0x68EF03F6),
298 (16, 0xD9C908EB),
299 (17, 0x38435E17),
300 (31, 0xE95CABCB),
301 (32, 0x46DD794E), (33, 0x9F85A26D),
303 (63, 0x7A873004),
304 (64, 0xFB6D36EB),
305 (65, 0x694420FA),
306 (127, 0x6C31BD0C),
307 (128, 0x30D9C515),
308 (129, 0xF514629F),
309 (255, 0x8953C482),
310 (256, 0x9C44184B),
311 (257, 0x8A13A1CE),
312 (511, 0x35348950),
313 (512, 0xAE10EE5A),
314 (513, 0x6814B154),
315 (1023, 0x0C8F24D0),
316 (1024, 0x2CDF6E8F),
317 (1025, 0x8EB48B63),
318 (4095, 0xBCB5BD82),
319 (4096, 0x9C71FE32),
320 (4097, 0x83391BE9),
321 ];
322
323 assert_eq!(
324 BOUNDARY_SIZES,
325 EXPECTED.iter().map(|(size, _)| *size).collect::<Vec<_>>()
326 );
327
328 for &(size, expected) in EXPECTED {
329 let data = sequential_data(size);
330 verify(&data, expected);
331 }
332 }
333
334 #[test]
336 fn chunk_size_independence() {
337 let data = sequential_data(1024);
338 let expected = CRC32C_REF.checksum(&data);
339
340 for chunk_size in 1..=64 {
342 let mut hasher = Crc32::new();
343 for chunk in data.chunks(chunk_size) {
344 hasher.update(chunk);
345 }
346 assert_eq!(hasher.finalize().as_u32(), expected);
347 }
348 }
349
350 #[test]
352 fn alignment_independence() {
353 let base_data: Vec<u8> = (0..256).map(|i| i as u8).collect();
355 let test_len = 64;
356
357 let reference = CRC32C_REF.checksum(&base_data[..test_len]);
359
360 for offset in 0..16 {
363 let data = &base_data[offset..offset + test_len];
364 let expected = CRC32C_REF.checksum(data);
365 assert_eq!(Crc32::checksum(data), expected);
366 }
367
368 verify(&base_data[..test_len], reference);
370 }
371
372 #[test]
373 fn test_crc32_hasher_trait() {
374 let msg = b"hello world";
375
376 let mut hasher = Crc32::new();
378 hasher.update(msg);
379 let digest = hasher.finalize();
380 assert!(Digest::decode(digest.as_ref()).is_ok());
381
382 let expected = CRC32C_REF.checksum(msg);
384 assert_eq!(digest.as_u32(), expected);
385
386 hasher.update(msg);
388 let digest2 = hasher.finalize();
389 assert_eq!(digest, digest2);
390
391 let hash = Crc32::hash(msg);
393 assert_eq!(hash.as_u32(), expected);
394 }
395
396 #[test]
397 fn test_crc32_len() {
398 assert_eq!(Digest::SIZE, SIZE);
399 assert_eq!(SIZE, 4);
400 }
401
402 #[test]
403 fn test_codec() {
404 let msg = b"hello world";
405 let mut hasher = Crc32::new();
406 hasher.update(msg);
407 let digest = hasher.finalize();
408
409 let encoded = digest.encode();
410 assert_eq!(encoded.len(), SIZE);
411 assert_eq!(encoded, digest.as_ref());
412
413 let decoded = Digest::decode(encoded).unwrap();
414 assert_eq!(digest, decoded);
415 }
416
417 #[test]
418 fn test_digest_from_u32() {
419 let value: u32 = 0xDEADBEEF;
420 let digest = Digest::from(value);
421 assert_eq!(digest.as_u32(), value);
422 assert_eq!(digest.0, [0xDE, 0xAD, 0xBE, 0xEF]);
423 }
424
425 #[test]
426 fn test_checksum_returns_u32() {
427 let checksum: u32 = Crc32::checksum(b"test");
429 let expected = CRC32C_REF.checksum(b"test");
430 assert_eq!(checksum, expected);
431 }
432
433 #[cfg(feature = "arbitrary")]
434 mod conformance {
435 use super::*;
436 use commonware_codec::conformance::CodecConformance;
437
438 commonware_conformance::conformance_tests! {
439 CodecConformance<Digest>,
440 }
441 }
442}