css: redact CssState Debug (test-guarded)

CssState is reachable via the public Disc.css field; #[derive(Debug)] leaked the
raw CSS title key on any {:?} of a Disc. Manual Debug prints crack_span only.
This commit is contained in:
Matthew Jackson
2026-07-17 21:00:34 -07:00
parent e308c5b825
commit 98000869b2
+30 -1
View File
@@ -29,7 +29,7 @@ use crate::sector::SectorSource;
const CSS_LOCKED_BAIL: u32 = 64; const CSS_LOCKED_BAIL: u32 = 64;
/// CSS decryption state for a DVD title. /// CSS decryption state for a DVD title.
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct CssState { pub struct CssState {
/// 5-byte CSS title key (from SCSI auth or the crack fallback). /// 5-byte CSS title key (from SCSI auth or the crack fallback).
pub title_key: [u8; 5], pub title_key: [u8; 5],
@@ -43,6 +43,18 @@ pub struct CssState {
pub crack_span: Option<(u32, u32)>, pub crack_span: Option<(u32, u32)>,
} }
// Redacting `Debug`: `CssState` is reachable via the public `Disc.css` field, so
// a `{:?}` on a `Disc` would otherwise print the raw CSS title key. Print only
// the (non-secret) crack span. Guarded by `css_state_debug_is_redacted`.
impl std::fmt::Debug for CssState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CssState")
.field("title_key", &"<redacted>")
.field("crack_span", &self.crack_span)
.finish()
}
}
/// Recover the CSS title key with no keys, by scanning scrambled sectors and /// Recover the CSS title key with no keys, by scanning scrambled sectors and
/// running the Stevenson known-plaintext attack (see the [`stevenson`] module). /// running the Stevenson known-plaintext attack (see the [`stevenson`] module).
/// ///
@@ -364,6 +376,23 @@ mod tests {
use super::*; use super::*;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
/// `CssState` is reachable via the public `Disc.css` field, so a `{:?}` on a
/// `Disc` must not print the raw CSS title key. Sentinel byte 213 (0xD5);
/// `crack_span` is non-secret and none of its values are 213.
#[test]
fn css_state_debug_is_redacted() {
let s = CssState {
title_key: [0xD5; 5],
crack_span: Some((10, 20)),
};
let dbg = format!("{s:?}");
assert!(
!dbg.contains("213"),
"CssState Debug leaked the title key: {dbg}"
);
assert!(dbg.contains("redacted"), "CssState Debug missing marker: {dbg}");
}
// ── is_scrambled ─────────────────────────────────────────────────────── // ── is_scrambled ───────────────────────────────────────────────────────
/// is_scrambled returns false for any buffer shorter than one sector, /// is_scrambled returns false for any buffer shorter than one sector,