1#[cfg(not(feature = "std"))]
11use alloc::{format, string::String, vec::Vec};
12
13use sha2::{Digest, Sha256};
14
15use crate::attribute::AttributeMessage;
16use crate::type_builders::{AttrValue, build_attr_message};
17
18pub const ATTR_SHA256: &str = "_provenance_sha256";
22pub const ATTR_CREATOR: &str = "_provenance_creator";
24pub const ATTR_TIMESTAMP: &str = "_provenance_timestamp";
26pub const ATTR_SOURCE: &str = "_provenance_source";
28
29pub fn sha256_hex(data: &[u8]) -> String {
33 let hash = Sha256::digest(data);
34 let mut hex = String::with_capacity(64);
35 for byte in hash.iter() {
36 hex.push_str(&format!("{byte:02x}"));
37 }
38 hex
39}
40
41pub struct Provenance {
45 pub creator: String,
46 pub timestamp: String,
47 pub source: Option<String>,
48}
49
50impl Provenance {
51 pub fn build_attrs(&self, raw_data: &[u8]) -> Vec<AttributeMessage> {
59 let hash = sha256_hex(raw_data);
60 let mut attrs = Vec::with_capacity(4);
61 let hash_val = AttrValue::String(hash);
62 attrs.push(build_attr_message(ATTR_SHA256, &hash_val));
63 let creator_val = AttrValue::String(self.creator.clone());
64 attrs.push(build_attr_message(ATTR_CREATOR, &creator_val));
65 let ts_val = AttrValue::String(self.timestamp.clone());
66 attrs.push(build_attr_message(ATTR_TIMESTAMP, &ts_val));
67 if let Some(ref src) = self.source {
68 let src_val = AttrValue::String(src.clone());
69 attrs.push(build_attr_message(ATTR_SOURCE, &src_val));
70 }
71 attrs
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum VerifyResult {
87 Ok,
89 Mismatch {
92 stored: String,
94 computed: String,
96 },
97 NoHash,
100}
101
102#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn sha256_empty() {
110 let h = sha256_hex(b"");
112 assert_eq!(
113 h,
114 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
115 );
116 }
117
118 #[test]
119 fn sha256_hello() {
120 let h = sha256_hex(b"hello");
121 assert_eq!(
122 h,
123 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
124 );
125 }
126
127 #[test]
128 fn provenance_builds_attrs_without_source() {
129 let prov = Provenance {
130 creator: "rustyhdf5".into(),
131 timestamp: "2026-02-19T00:00:00Z".into(),
132 source: None,
133 };
134 let attrs = prov.build_attrs(b"hello");
135 assert_eq!(attrs.len(), 3);
136 assert_eq!(attrs[0].name, ATTR_SHA256);
137 assert_eq!(attrs[1].name, ATTR_CREATOR);
138 assert_eq!(attrs[2].name, ATTR_TIMESTAMP);
139 }
140
141 #[test]
142 fn provenance_builds_attrs_with_source() {
143 let prov = Provenance {
144 creator: "test".into(),
145 timestamp: "2026-01-01T00:00:00Z".into(),
146 source: Some("sensor_42".into()),
147 };
148 let attrs = prov.build_attrs(b"data");
149 assert_eq!(attrs.len(), 4);
150 assert_eq!(attrs[3].name, ATTR_SOURCE);
151 }
152}