Skip to main content

fp_runtime/asupersync/
integrity.rs

1use serde::{Deserialize, Serialize};
2
3use crate::asupersync::error::AsupersyncError;
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6pub struct IntegrityProof {
7    pub algorithm: String,
8    pub expected_digest: String,
9    pub observed_digest: String,
10    pub verified: bool,
11}
12
13pub trait IntegrityVerifier {
14    fn verify(
15        &self,
16        artifact_id: &str,
17        bytes: &[u8],
18        expected_digest: &str,
19    ) -> Result<IntegrityProof, AsupersyncError>;
20}
21
22#[derive(Debug, Clone, Copy, Default)]
23pub struct Fnv1aVerifier;
24
25impl IntegrityVerifier for Fnv1aVerifier {
26    fn verify(
27        &self,
28        artifact_id: &str,
29        bytes: &[u8],
30        expected_digest: &str,
31    ) -> Result<IntegrityProof, AsupersyncError> {
32        let observed_digest = fnv1a_hex(bytes);
33        if observed_digest != expected_digest {
34            return Err(AsupersyncError::IntegrityMismatch {
35                artifact_id: artifact_id.to_string(),
36                expected: expected_digest.to_string(),
37                observed: observed_digest,
38            });
39        }
40
41        Ok(IntegrityProof {
42            algorithm: "fnv1a64".to_string(),
43            expected_digest: expected_digest.to_string(),
44            observed_digest,
45            verified: true,
46        })
47    }
48}
49
50fn fnv1a_hex(bytes: &[u8]) -> String {
51    let mut hash = 0xcbf29ce484222325_u64;
52    for byte in bytes {
53        hash ^= u64::from(*byte);
54        hash = hash.wrapping_mul(0x100000001b3);
55    }
56    format!("{hash:016x}")
57}