#![forbid(unsafe_code)]
#![warn(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Fingerprint(pub [u8; 32]);
impl Fingerprint {
pub fn to_snap_string(self) -> String {
let mut s = String::with_capacity(65);
s.push('h');
for b in self.0 {
s.push_str(&format!("{:02x}", b));
}
s
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Tier {
Alpha,
Beta,
Gamma,
Delta,
Epsilon,
}
impl Tier {
pub const fn enforces(self) -> bool {
matches!(self, Self::Gamma | Self::Delta | Self::Epsilon)
}
pub const fn governed(self) -> bool {
matches!(self, Self::Epsilon)
}
}
pub trait Conformant {
const TIER: Tier;
const NAME: &'static str;
fn canonical_encoding(&self) -> Vec<u8>;
fn fingerprint(&self) -> Fingerprint {
Fingerprint(hemera_hash(&self.canonical_encoding()))
}
fn snapshot_instance() -> Self
where
Self: Sized;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncodingSnapshot {
pub name: String,
pub tier: Tier,
pub fingerprint: Fingerprint,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MechanismSnapshot {
pub mechanism: String,
pub scenario: String,
pub tier: Tier,
pub fingerprint: Fingerprint,
}
#[derive(Debug, Clone, Default)]
pub struct Manifest {
pub encodings: Vec<EncodingSnapshot>,
pub mechanisms: Vec<MechanismSnapshot>,
}
impl Manifest {
pub fn root(&self) -> Fingerprint {
let mut buf: Vec<u8> = Vec::new();
let mut encs = self.encodings.clone();
encs.sort_by(|a, b| a.name.cmp(&b.name));
for e in &encs {
buf.extend_from_slice(e.name.as_bytes());
buf.push(0);
buf.push(e.tier as u8);
buf.extend_from_slice(&e.fingerprint.0);
}
let mut mechs = self.mechanisms.clone();
mechs.sort_by(|a, b| {
a.mechanism
.cmp(&b.mechanism)
.then_with(|| a.scenario.cmp(&b.scenario))
});
for m in &mechs {
buf.extend_from_slice(m.mechanism.as_bytes());
buf.push(0);
buf.extend_from_slice(m.scenario.as_bytes());
buf.push(0);
buf.push(m.tier as u8);
buf.extend_from_slice(&m.fingerprint.0);
}
Fingerprint(hemera_hash(&buf))
}
}
fn hemera_hash(_bytes: &[u8]) -> [u8; 32] {
[0u8; 32]
}
#[cfg(test)]
mod tests {
use super::*;
struct Example {
n: u64,
}
impl Conformant for Example {
const TIER: Tier = Tier::Alpha;
const NAME: &'static str = "cyber_conformance::tests::Example@v1";
fn canonical_encoding(&self) -> Vec<u8> {
self.n.to_le_bytes().to_vec()
}
fn snapshot_instance() -> Self {
Example { n: 42 }
}
}
#[test]
fn fingerprint_is_deterministic() {
let a = Example::snapshot_instance().fingerprint();
let b = Example::snapshot_instance().fingerprint();
assert_eq!(a, b);
}
#[test]
fn tier_enforcement_policy() {
assert!(!Tier::Alpha.enforces());
assert!(!Tier::Beta.enforces());
assert!(Tier::Gamma.enforces());
assert!(Tier::Delta.enforces());
assert!(Tier::Epsilon.enforces());
assert!(Tier::Epsilon.governed());
assert!(!Tier::Delta.governed());
}
#[test]
fn manifest_root_is_deterministic() {
let m = Manifest {
encodings: vec![EncodingSnapshot {
name: "x".into(),
tier: Tier::Gamma,
fingerprint: Fingerprint([1u8; 32]),
}],
mechanisms: vec![],
};
assert_eq!(m.root(), m.root());
}
}