use std::fmt;
use hush_core::MerkleProof;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TrustError {
#[error("trust requirement not met: need {required}, got {actual}")]
InsufficientTrust {
required: TrustLevel,
actual: TrustLevel,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TrustLevel {
Unverified = 0,
Signed = 1,
Verified = 2,
Certified = 3,
}
impl TrustLevel {
pub fn rank(self) -> u8 {
self as u8
}
fn ansi_color(self) -> &'static str {
match self {
TrustLevel::Unverified => "\x1b[31m", TrustLevel::Signed => "\x1b[33m", TrustLevel::Verified => "\x1b[32m", TrustLevel::Certified => "\x1b[92m", }
}
pub fn colored(&self) -> String {
format!("{}{}\x1b[0m", self.ansi_color(), self)
}
}
impl fmt::Display for TrustLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
TrustLevel::Unverified => "unverified",
TrustLevel::Signed => "signed",
TrustLevel::Verified => "verified",
TrustLevel::Certified => "certified",
};
write!(f, "{label}")
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TrustRequirement {
None,
Signed,
Verified,
Certified,
}
impl TrustRequirement {
pub fn minimum_level(self) -> TrustLevel {
match self {
TrustRequirement::None => TrustLevel::Unverified,
TrustRequirement::Signed => TrustLevel::Signed,
TrustRequirement::Verified => TrustLevel::Verified,
TrustRequirement::Certified => TrustLevel::Certified,
}
}
}
impl fmt::Display for TrustRequirement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
TrustRequirement::None => "none",
TrustRequirement::Signed => "signed",
TrustRequirement::Verified => "verified",
TrustRequirement::Certified => "certified",
};
write!(f, "{label}")
}
}
pub fn compute_trust_level(
publisher_sig: Option<&str>,
registry_sig: Option<&str>,
merkle_proof: Option<&MerkleProof>,
) -> TrustLevel {
match (publisher_sig, registry_sig, merkle_proof) {
(Some(_), Some(_), Some(_)) => TrustLevel::Certified,
(Some(_), Some(_), None) => TrustLevel::Verified,
(Some(_), _, _) => TrustLevel::Signed,
_ => TrustLevel::Unverified,
}
}
pub fn check_trust(actual: TrustLevel, required: TrustRequirement) -> Result<(), TrustError> {
let min = required.minimum_level();
if actual >= min {
Ok(())
} else {
Err(TrustError::InsufficientTrust {
required: min,
actual,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use hush_core::Hash;
#[test]
fn trust_level_ordering() {
assert!(TrustLevel::Unverified < TrustLevel::Signed);
assert!(TrustLevel::Signed < TrustLevel::Verified);
assert!(TrustLevel::Verified < TrustLevel::Certified);
}
#[test]
fn compute_unverified() {
let level = compute_trust_level(None, None, None);
assert_eq!(level, TrustLevel::Unverified);
}
#[test]
fn compute_signed() {
let level = compute_trust_level(Some("abc123"), None, None);
assert_eq!(level, TrustLevel::Signed);
}
#[test]
fn compute_verified() {
let level = compute_trust_level(Some("abc123"), Some("def456"), None);
assert_eq!(level, TrustLevel::Verified);
}
#[test]
fn compute_certified() {
let proof = MerkleProof {
tree_size: 4,
leaf_index: 1,
audit_path: vec![Hash::zero()],
};
let level = compute_trust_level(Some("abc123"), Some("def456"), Some(&proof));
assert_eq!(level, TrustLevel::Certified);
}
#[test]
fn compute_registry_sig_without_publisher_sig_is_unverified() {
let level = compute_trust_level(None, Some("def456"), None);
assert_eq!(level, TrustLevel::Unverified);
}
#[test]
fn compute_merkle_proof_without_registry_sig_is_signed() {
let proof = MerkleProof {
tree_size: 4,
leaf_index: 1,
audit_path: vec![Hash::zero()],
};
let level = compute_trust_level(Some("abc123"), None, Some(&proof));
assert_eq!(level, TrustLevel::Signed);
}
#[test]
fn check_trust_passes_when_met() {
assert!(check_trust(TrustLevel::Certified, TrustRequirement::Verified).is_ok());
assert!(check_trust(TrustLevel::Verified, TrustRequirement::Verified).is_ok());
assert!(check_trust(TrustLevel::Signed, TrustRequirement::Signed).is_ok());
assert!(check_trust(TrustLevel::Unverified, TrustRequirement::None).is_ok());
}
#[test]
fn check_trust_fails_when_insufficient() {
let err = check_trust(TrustLevel::Signed, TrustRequirement::Verified).unwrap_err();
match err {
TrustError::InsufficientTrust { required, actual } => {
assert_eq!(required, TrustLevel::Verified);
assert_eq!(actual, TrustLevel::Signed);
}
}
}
#[test]
fn check_trust_unverified_rejected_by_signed_requirement() {
assert!(check_trust(TrustLevel::Unverified, TrustRequirement::Signed).is_err());
}
#[test]
fn trust_level_display() {
assert_eq!(TrustLevel::Unverified.to_string(), "unverified");
assert_eq!(TrustLevel::Signed.to_string(), "signed");
assert_eq!(TrustLevel::Verified.to_string(), "verified");
assert_eq!(TrustLevel::Certified.to_string(), "certified");
}
#[test]
fn trust_level_rank() {
assert_eq!(TrustLevel::Unverified.rank(), 0);
assert_eq!(TrustLevel::Signed.rank(), 1);
assert_eq!(TrustLevel::Verified.rank(), 2);
assert_eq!(TrustLevel::Certified.rank(), 3);
}
#[test]
fn trust_level_colored_output() {
let output = TrustLevel::Unverified.colored();
assert!(output.contains("unverified"));
assert!(output.contains("\x1b[31m")); assert!(output.contains("\x1b[0m")); }
#[test]
fn trust_level_serde_roundtrip() {
let level = TrustLevel::Certified;
let json = serde_json::to_string(&level).unwrap();
assert_eq!(json, "\"certified\"");
let restored: TrustLevel = serde_json::from_str(&json).unwrap();
assert_eq!(restored, level);
}
#[test]
fn trust_requirement_serde_roundtrip() {
let req = TrustRequirement::Verified;
let json = serde_json::to_string(&req).unwrap();
assert_eq!(json, "\"verified\"");
let restored: TrustRequirement = serde_json::from_str(&json).unwrap();
assert_eq!(restored, req);
}
}