fp_runtime/asupersync/
integrity.rs1use 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}