greentic_component/
signing.rs1use std::fs;
2use std::path::Path;
3
4use blake3::Hasher;
5use thiserror::Error;
6
7use crate::manifest::ComponentManifest;
8
9#[derive(Debug, Clone, Default)]
10pub struct DevPolicy {
11 pub allow_missing_signatures: bool,
12}
13
14#[derive(Debug, Clone, Default)]
15pub struct StrictPolicy {
16 pub signatures: Vec<SignatureRef>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct SignatureRef {
21 pub issuer: String,
22 pub digest: String,
23}
24
25#[derive(Debug, Error)]
26pub enum SigningError {
27 #[error("failed to read component bytes: {source}")]
28 Io {
29 #[from]
30 source: std::io::Error,
31 },
32 #[error("component hash mismatch (expected {expected}, found {found})")]
33 HashMismatch { expected: String, found: String },
34}
35
36pub fn verify_manifest_hash(manifest: &ComponentManifest, root: &Path) -> Result<(), SigningError> {
37 let wasm_path = root.join(manifest.artifacts.component_wasm());
38 verify_wasm_hash(manifest.hashes.component_wasm.as_str(), &wasm_path)
39}
40
41pub fn verify_wasm_hash(expected: &str, wasm_path: &Path) -> Result<(), SigningError> {
42 let actual = compute_wasm_hash(wasm_path)?;
43 if actual != expected {
44 return Err(SigningError::HashMismatch {
45 expected: expected.to_string(),
46 found: actual,
47 });
48 }
49 Ok(())
50}
51
52pub fn compute_wasm_hash(wasm_path: &Path) -> Result<String, SigningError> {
53 let mut hasher = Hasher::new();
54 let bytes = fs::read(wasm_path)?;
55 hasher.update(&bytes);
56 let digest = hasher.finalize();
57 Ok(format!("blake3:{}", hex::encode(digest.as_bytes())))
58}