1use crate::Hasher;
30use bytes::BufMut;
31use commonware_codec::{Buf, Error as CodecError, FixedArray, FixedSize, Read, ReadExt, Write};
32use commonware_formatting::Hex;
33use commonware_math::algebra::Random;
34use commonware_parallel::Strategy;
35use commonware_utils::{Array, Span};
36use core::{
37 fmt::{Debug, Display},
38 ops::Deref,
39};
40use rand_core::CryptoRng;
41
42const SIZE: usize = 4;
44
45const ALGORITHM: crc_fast::CrcAlgorithm = crc_fast::CrcAlgorithm::Crc32Iscsi;
47
48#[derive(Debug)]
52pub struct Crc32 {
53 inner: crc_fast::Digest,
54}
55
56impl Default for Crc32 {
57 fn default() -> Self {
58 Self {
59 inner: crc_fast::Digest::new(ALGORITHM),
60 }
61 }
62}
63
64impl Crc32 {
65 #[inline]
69 pub fn checksum(data: &[u8]) -> u32 {
70 crc_fast::checksum(ALGORITHM, data) as u32
71 }
72
73 pub fn resume(checksum: u32) -> Self {
75 Self {
78 inner: crc_fast::Digest::new_with_init_state(ALGORITHM, u64::from(checksum ^ u32::MAX)),
79 }
80 }
81}
82
83impl Hasher for Crc32 {
84 type Digest = Digest;
85
86 fn hash(parts: &[&[u8]]) -> Self::Digest {
87 let mut hasher = Self::default();
88 for part in parts {
89 hasher.update(part);
90 }
91 hasher.finalize().1
92 }
93
94 fn hash_pair(
95 left: &[&[u8]],
96 right: &[&[u8]],
97 _strategy: &impl Strategy,
98 ) -> (Self::Digest, Self::Digest) {
99 (Self::hash(left), Self::hash(right))
100 }
101
102 fn update(&mut self, message: &[u8]) -> &mut Self {
103 self.inner.update(message);
104 self
105 }
106
107 fn finalize(mut self) -> (Self, Self::Digest) {
108 let digest = Self::Digest::from(self.inner.finalize_reset() as u32);
109 (self, digest)
110 }
111}
112
113#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, FixedArray)]
115#[fixed_array(infallible)]
116#[repr(transparent)]
117pub struct Digest(pub [u8; SIZE]);
118
119#[cfg(feature = "arbitrary")]
120impl<'a> arbitrary::Arbitrary<'a> for Digest {
121 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
122 let len = u.int_in_range(0..=256)?;
124 let data = u.bytes(len)?;
125 Ok(Crc32::hash(&[data]))
126 }
127}
128
129impl Digest {
130 #[inline]
132 pub const fn as_u32(&self) -> u32 {
133 u32::from_be_bytes(self.0)
134 }
135}
136
137impl Write for Digest {
138 fn write(&self, buf: &mut impl BufMut) {
139 self.0.write(buf);
140 }
141}
142
143impl Read for Digest {
144 type Cfg = ();
145
146 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
147 let array = <[u8; SIZE]>::read(buf)?;
148 Ok(Self(array))
149 }
150}
151
152impl FixedSize for Digest {
153 const SIZE: usize = SIZE;
154}
155
156impl Span for Digest {}
157
158impl Array for Digest {}
159
160impl From<u32> for Digest {
161 fn from(value: u32) -> Self {
162 Self(value.to_be_bytes())
163 }
164}
165
166impl AsRef<[u8]> for Digest {
167 fn as_ref(&self) -> &[u8] {
168 &self.0
169 }
170}
171
172impl Deref for Digest {
173 type Target = [u8];
174 fn deref(&self) -> &[u8] {
175 &self.0
176 }
177}
178
179impl Debug for Digest {
180 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
181 write!(f, "{}", Hex(&self.0))
182 }
183}
184
185impl Display for Digest {
186 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
187 write!(f, "{}", Hex(&self.0))
188 }
189}
190
191impl crate::Digest for Digest {
192 const EMPTY: Self = Self([0u8; SIZE]);
193}
194
195impl Random for Digest {
196 fn random(mut rng: impl CryptoRng) -> Self {
197 let mut array = [0u8; SIZE];
198 rng.fill_bytes(&mut array);
199 Self(array)
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use crate::Hasher;
207 use commonware_codec::{Copying, DecodeExt, Encode};
208 use crc::{CRC_32_ISCSI, Crc};
209
210 const CRC32C_REF: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
212
213 fn verify(data: &[u8], expected: u32) {
215 assert_eq!(CRC32C_REF.checksum(data), expected);
216 assert_eq!(Crc32::checksum(data), expected);
217 }
218
219 fn sequential_data(len: usize) -> Vec<u8> {
221 (0..len).map(|i| (i & 0xFF) as u8).collect()
222 }
223
224 #[test]
227 fn rfc3720_test_vectors() {
228 verify(&[0x00; 32], 0x8A9136AA);
230
231 verify(&[0xFF; 32], 0x62A8AB43);
233
234 let ascending: Vec<u8> = (0x00..0x20).collect();
236 verify(&ascending, 0x46DD794E);
237
238 let descending: Vec<u8> = (0x00..0x20).rev().collect();
240 verify(&descending, 0x113FDB5C);
241
242 let iscsi_read_pdu: [u8; 48] = [
244 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14,
246 0x00, 0x00, 0x00, 0x18, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 ];
249 verify(&iscsi_read_pdu, 0xD9963A56);
250 }
251
252 #[test]
257 fn external_test_vectors() {
258 verify(b"", 0x00000000);
260 verify(b"123456789", 0xE3069283);
261
262 verify(b"23456789", 0xBFE92A83);
264 verify(b"The quick brown fox jumps over the lazy dog", 0x22620404);
265
266 let sequential_240: Vec<u8> = (0x01..=0xF0).collect();
268 verify(&sequential_240, 0x24C5D375);
269 }
270
271 #[test]
276 fn simd_boundaries() {
277 const BOUNDARY_SIZES: &[usize] = &[
285 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, ];
295
296 const EXPECTED: &[(usize, u32)] = &[
299 (0, 0x00000000),
300 (1, 0x527D5351),
301 (2, 0x030AF4D1),
302 (3, 0x92FD4BFA),
303 (4, 0xD9331AA3),
304 (7, 0xA359ED4C),
305 (8, 0x8A2CBC3B),
306 (9, 0x7144C5A8),
307 (15, 0x68EF03F6),
308 (16, 0xD9C908EB),
309 (17, 0x38435E17),
310 (31, 0xE95CABCB),
311 (32, 0x46DD794E), (33, 0x9F85A26D),
313 (63, 0x7A873004),
314 (64, 0xFB6D36EB),
315 (65, 0x694420FA),
316 (127, 0x6C31BD0C),
317 (128, 0x30D9C515),
318 (129, 0xF514629F),
319 (255, 0x8953C482),
320 (256, 0x9C44184B),
321 (257, 0x8A13A1CE),
322 (511, 0x35348950),
323 (512, 0xAE10EE5A),
324 (513, 0x6814B154),
325 (1023, 0x0C8F24D0),
326 (1024, 0x2CDF6E8F),
327 (1025, 0x8EB48B63),
328 (4095, 0xBCB5BD82),
329 (4096, 0x9C71FE32),
330 (4097, 0x83391BE9),
331 ];
332
333 assert_eq!(
334 BOUNDARY_SIZES,
335 EXPECTED.iter().map(|(size, _)| *size).collect::<Vec<_>>()
336 );
337
338 for &(size, expected) in EXPECTED {
339 let data = sequential_data(size);
340 verify(&data, expected);
341 }
342 }
343
344 #[test]
346 fn chunk_size_independence() {
347 let data = sequential_data(1024);
348 let expected = CRC32C_REF.checksum(&data);
349
350 for chunk_size in 1..=64 {
352 let mut hasher = Crc32::default();
353 for chunk in data.chunks(chunk_size) {
354 hasher.update(chunk);
355 }
356 assert_eq!(hasher.finalize().1.as_u32(), expected);
357 }
358 }
359
360 #[test]
362 fn alignment_independence() {
363 let base_data: Vec<u8> = (0..256).map(|i| i as u8).collect();
365 let test_len = 64;
366
367 let reference = CRC32C_REF.checksum(&base_data[..test_len]);
369
370 for offset in 0..16 {
373 let data = &base_data[offset..offset + test_len];
374 let expected = CRC32C_REF.checksum(data);
375 assert_eq!(Crc32::checksum(data), expected);
376 }
377
378 verify(&base_data[..test_len], reference);
380 }
381
382 #[test]
383 fn test_crc32_hasher_trait() {
384 let msg = b"hello world";
385
386 let mut hasher = Crc32::default();
388 hasher.update(msg);
389 let (hasher, digest) = hasher.finalize();
390 assert!(Digest::decode(Copying(&digest)).is_ok());
391
392 let expected = CRC32C_REF.checksum(msg);
394 assert_eq!(digest.as_u32(), expected);
395
396 let mut hasher = hasher;
398 hasher.update(msg);
399 let (_, digest2) = hasher.finalize();
400 assert_eq!(digest, digest2);
401
402 let hash = Crc32::hash(&[msg]);
404 assert_eq!(hash.as_u32(), expected);
405
406 let hash = Crc32::hash(&[b"hello", b" world"]);
408 assert_eq!(hash.as_u32(), expected);
409 }
410
411 #[test]
414 fn resumed_hasher_resets_after_finalize() {
415 let prefix = b"durable prefix";
416 let suffix = b"new suffix";
417 let mut hasher = Crc32::resume(Crc32::checksum(prefix));
418 hasher.update(suffix);
419
420 let (mut hasher, digest) = hasher.finalize();
421 assert_eq!(
422 digest.as_u32(),
423 Crc32::hash(&[prefix.as_slice(), suffix.as_slice()]).as_u32()
424 );
425
426 hasher.update(suffix);
427 let (_, digest) = hasher.finalize();
428 assert_eq!(digest.as_u32(), Crc32::checksum(suffix));
429 }
430
431 #[test]
436 fn resume_split_independence() {
437 let data = sequential_data(4097);
438 let expected = CRC32C_REF.checksum(&data);
439 for split in 0..=data.len() {
440 let mut hasher = Crc32::resume(CRC32C_REF.checksum(&data[..split]));
441 hasher.update(&data[split..]);
442 assert_eq!(hasher.finalize().1.as_u32(), expected);
443 }
444 }
445
446 #[test]
449 fn resume_finalize_round_trip() {
450 for checksum in [
451 0x00000000,
452 0xFFFFFFFF,
453 0xDEADBEEF,
454 Crc32::checksum(b"resume"),
455 ] {
456 assert_eq!(Crc32::resume(checksum).finalize().1.as_u32(), checksum);
457 }
458 }
459
460 #[test]
461 fn test_crc32_len() {
462 assert_eq!(Digest::SIZE, SIZE);
463 assert_eq!(SIZE, 4);
464 }
465
466 #[test]
467 fn test_codec() {
468 let msg = b"hello world";
469 let mut hasher = Crc32::default();
470 hasher.update(msg);
471 let (_, digest) = hasher.finalize();
472
473 let encoded = digest.encode();
474 assert_eq!(encoded.len(), SIZE);
475 assert_eq!(encoded, digest.as_ref());
476
477 let decoded = Digest::decode(encoded).unwrap();
478 assert_eq!(digest, decoded);
479 }
480
481 #[test]
482 fn test_digest_from_u32() {
483 let value: u32 = 0xDEADBEEF;
484 let digest = Digest::from(value);
485 assert_eq!(digest.as_u32(), value);
486 assert_eq!(digest.0, [0xDE, 0xAD, 0xBE, 0xEF]);
487 }
488
489 #[test]
490 fn test_checksum_returns_u32() {
491 let checksum: u32 = Crc32::checksum(b"test");
493 let expected = CRC32C_REF.checksum(b"test");
494 assert_eq!(checksum, expected);
495 }
496
497 #[cfg(feature = "arbitrary")]
498 mod conformance {
499 use super::*;
500 use commonware_codec::conformance::CodecConformance;
501
502 commonware_conformance::conformance_tests! {
503 CodecConformance<Digest>,
504 }
505 }
506}