From 150e91b0ab097995f28ac7bd90a85793b8cfa87f Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Sat, 5 Sep 2026 12:35:10 -0700 Subject: [PATCH] redact HostCert Debug so AACS host private keys never reach a log --- src/lib.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index dbfc31e..16b84c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ pub enum DiscKind { /// A host certificate for the AACS cert handshake (raw; the consumer collects /// these from its key sources and passes them in). -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct HostCert { /// AACS 1.0 host private key (20 bytes). pub private_key: [u8; 20], @@ -73,6 +73,20 @@ pub struct HostCert { pub certificate_v2: Option>, } +// Manual Debug: `private_key`/`private_key_v2` are the raw host private keys — +// they must never reach a log in plaintext; presence (Some/None) of the v2 key +// stays observable. Mirrors the redacting Debug on AacsAuth/Unlocked. +impl std::fmt::Debug for HostCert { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("HostCert") + .field("private_key", &"[redacted]") + .field("certificate", &self.certificate) + .field("private_key_v2", &self.private_key_v2.map(|_| "[redacted]")) + .field("certificate_v2", &self.certificate_v2) + .finish() + } +} + /// Per-attempt context the consumer hands to EVERY unlocker, uniformly: the /// drive identity and the mounted disc's kind. These are the shared facts the /// dispatch loop knows; anything an individual unlocker needs beyond them (the @@ -250,4 +264,29 @@ mod tests { // Presence (Some/None) stays observable. assert!(s.contains("Some"), "must still show a key WAS present: {s}"); } + + // `private_key`/`private_key_v2` are the raw host private keys; `Debug` must + // NOT print those bytes. Restoring `#[derive(Debug)]` renders the byte + // arrays (e.g. 0xAB → 171 / 0xCD → 205), turning this test red. + #[test] + fn host_cert_debug_redacts_private_keys() { + let c = HostCert { + private_key: [0xAB; 20], + certificate: vec![0x01, 0x02], + private_key_v2: Some([0xCD; 32]), + certificate_v2: None, + }; + let s = format!("{c:?}"); + assert!(!s.contains("171"), "private_key bytes must not appear: {s}"); + assert!( + !s.contains("205"), + "private_key_v2 bytes must not appear: {s}" + ); + assert!(s.contains("[redacted]"), "must mark redaction: {s}"); + // Presence of the v2 key stays observable. + assert!( + s.contains("Some"), + "must still show the v2 key WAS present: {s}" + ); + } }