use zlicenser_protocol::fingerprint::{
compute_commitment, FuzzyExtractor, IdentifierTier, LinuxCollector,
};
fn main() {
let collector = LinuxCollector::best_effort();
let report = collector.collect_with_report();
println!("=== Hardware identifier collection report ===\n");
println!(" {:<6} {:<25} {}", "TIER", "SOURCE", "RESULT");
println!(" {}", "─".repeat(70));
for attempt in &report {
let tier_label = match attempt.tier {
IdentifierTier::High => "HIGH ",
IdentifierTier::Medium => "MEDIUM",
IdentifierTier::Low => "LOW ",
};
match &attempt.outcome {
Ok(id) => println!(
" {} {:<25} OK {}",
tier_label,
attempt.kind_hint,
display_value(&id.value),
),
Err(reason) => println!(
" {} {:<25} SKIP {}",
tier_label, attempt.kind_hint, reason,
),
}
}
let ids: Vec<_> = report.into_iter().filter_map(|a| a.outcome.ok()).collect();
println!("\nCollected {} identifier(s)\n", ids.len());
if ids.is_empty() {
eprintln!("No identifiers collected, nothing to enroll.");
std::process::exit(1);
}
let commitment = compute_commitment(&ids);
println!("Fingerprint commitment {}", hex::encode(commitment));
let threshold = (ids.len() as u8).saturating_sub(2).max(1);
println!("Enrolling with threshold {}/{}", threshold, ids.len());
let (secret, record) = FuzzyExtractor::enroll(&ids, threshold).unwrap();
println!(
"Master secret (first 8 bytes) {}\n",
hex::encode(&secret.as_bytes()[..8])
);
match FuzzyExtractor::reconstruct(&ids, &record) {
Ok(recovered) if recovered.as_bytes() == secret.as_bytes() => {
println!("Reconstruction check OK, record is valid for this machine");
}
Ok(_) => println!("Reconstruction check MISMATCH, bug in enroll/reconstruct"),
Err(e) => println!("Reconstruction check FAIL, {e}"),
}
if ids.iter().all(|id| id.tier() != IdentifierTier::High) {
println!(
"\nNote: no High-tier identifiers collected (SMBIOS / TPM).\n\
For stronger binding, grant cap_dac_read_search to your binary:\n\
\n sudo setcap cap_dac_read_search+ep <your-binary>\n"
);
}
}
fn display_value(value: &[u8]) -> String {
let is_printable = value
.iter()
.all(|&b| b.is_ascii_graphic() || b == b' ' || b == b':' || b == b'|');
if is_printable {
String::from_utf8_lossy(value).into_owned()
} else if value.len() > 16 {
format!("[{} bytes] {}…", value.len(), hex::encode(&value[..12]))
} else {
hex::encode(value)
}
}