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