greentic_component/
signing.rs

1use 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}