1use serde::{Deserialize, Serialize};
2
3pub type Hash32 = [u8; 32];
4pub type Signature64 = [u8; 64];
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
7pub struct AuditRecord {
8 pub device_id: String,
9 pub sequence: u64,
10 pub timestamp_ms: u64,
11 pub payload_hash: Hash32,
12 #[serde(with = "signature64_serde")]
13 pub signature: Signature64,
14 pub prev_record_hash: Hash32,
15 pub object_ref: String,
16}
17
18impl AuditRecord {
19 pub fn hash(&self) -> Hash32 {
20 let bytes = postcard::to_allocvec(self).expect("AuditRecord serialization should not fail");
21 *blake3::hash(&bytes).as_bytes()
22 }
23
24 pub fn zero_hash() -> Hash32 {
25 [0u8; 32]
26 }
27}
28
29mod signature64_serde {
30 use serde::{de::Error as DeError, Deserialize, Deserializer, Serializer};
31
32 pub fn serialize<S>(value: &[u8; 64], serializer: S) -> Result<S::Ok, S::Error>
33 where
34 S: Serializer,
35 {
36 serializer.serialize_bytes(value)
37 }
38
39 pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 64], D::Error>
40 where
41 D: Deserializer<'de>,
42 {
43 let bytes: Vec<u8> = Vec::<u8>::deserialize(deserializer)?;
44 if bytes.len() != 64 {
45 return Err(D::Error::custom(format!(
46 "invalid signature length: expected 64, got {}",
47 bytes.len()
48 )));
49 }
50
51 let mut out = [0u8; 64];
52 out.copy_from_slice(&bytes);
53 Ok(out)
54 }
55}