Skip to main content

krabka_audit/operator_keys/
fingerprint.rs

1//! The fingerprint of a configured approver set.
2//!
3//! Every break-glass audit event carries this hash, so an auditor sees which
4//! approver set a broker held when it made a decision. The hash covers the
5//! members alone, and not the order an operator wrote them in.
6
7use {
8    sha2::{Digest as _, Sha256},
9    std::collections::BTreeSet,
10};
11
12/// SHA-256 hex fingerprint of a configured approver set.
13///
14/// The approver set comes from each broker's own `broker.toml`, so two brokers
15/// can legitimately disagree during a rolling config change. Every break-glass
16/// audit event records this fingerprint, which makes the disagreement visible
17/// after the fact.
18///
19/// The input is sorted and de-duplicated first, and each name is
20/// length-prefixed, so the fingerprint depends on the members alone: not on the
21/// order an operator wrote them in, and not on where one name ends and the next
22/// begins.
23#[must_use]
24pub fn approver_set_fingerprint(approvers: &[String]) -> String {
25    let unique: BTreeSet<&str> = approvers.iter().map(String::as_str).collect();
26    let mut hasher = Sha256::new();
27    for approver in unique {
28        let len = u32::try_from(approver.len()).unwrap_or(u32::MAX);
29        hasher.update(len.to_be_bytes());
30        hasher.update(approver.as_bytes());
31    }
32    hex::encode(hasher.finalize())
33}