Support multiple host certs, expose handshake_error on AacsState
- host_cert: Option → host_certs: Vec (try all until one succeeds) - handshake_error field shows why auth failed (e.g. cert rejected/revoked)
This commit is contained in:
@@ -1190,7 +1190,7 @@ mod tests {
|
||||
if !keydb_path.exists() { return; }
|
||||
|
||||
let db = crate::aacs::KeyDb::load(&keydb_path).unwrap();
|
||||
if let Some(hc) = &db.host_cert {
|
||||
if let Some(hc) = db.host_certs.first() {
|
||||
let valid = verify_cert(&hc.certificate);
|
||||
eprintln!("Host cert verification: {}", if valid { "PASS" } else { "FAIL" });
|
||||
// Note: our cert is revoked but should still have valid LA signature
|
||||
|
||||
+7
-5
@@ -27,7 +27,7 @@ pub struct KeyDb {
|
||||
/// Processing keys (pre-computed media keys for specific MKB versions)
|
||||
pub processing_keys: Vec<[u8; 16]>,
|
||||
/// Host certificate + private key for SCSI authentication
|
||||
pub host_cert: Option<HostCert>,
|
||||
pub host_certs: Vec<HostCert>,
|
||||
/// Per-disc VUK entries indexed by disc hash (hex lowercase)
|
||||
pub disc_entries: HashMap<String, DiscEntry>,
|
||||
}
|
||||
@@ -105,7 +105,7 @@ impl KeyDb {
|
||||
let mut db = KeyDb {
|
||||
device_keys: Vec::new(),
|
||||
processing_keys: Vec::new(),
|
||||
host_cert: None,
|
||||
host_certs: Vec::new(),
|
||||
disc_entries: HashMap::new(),
|
||||
};
|
||||
|
||||
@@ -135,7 +135,7 @@ impl KeyDb {
|
||||
|
||||
// Host Certificate (AACS 2.0)
|
||||
if line.starts_with("| HC2") {
|
||||
if let Some(ref mut hc) = db.host_cert {
|
||||
if let Some(hc) = db.host_certs.last_mut() {
|
||||
if let Some((pk, cert)) = Self::parse_host_cert_v2(line) {
|
||||
hc.private_key_v2 = Some(pk);
|
||||
hc.certificate_v2 = Some(cert);
|
||||
@@ -146,7 +146,9 @@ impl KeyDb {
|
||||
|
||||
// Host Certificate (AACS 1.0)
|
||||
if line.starts_with("| HC") {
|
||||
db.host_cert = Self::parse_host_cert(line);
|
||||
if let Some(hc) = Self::parse_host_cert(line) {
|
||||
db.host_certs.push(hc);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1342,7 +1344,7 @@ mod tests {
|
||||
|
||||
assert_eq!(db.device_keys.len(), 4);
|
||||
assert_eq!(db.processing_keys.len(), 3);
|
||||
assert!(db.host_cert.is_some());
|
||||
assert!(!db.host_certs.is_empty());
|
||||
assert!(db.disc_entries.len() > 170000);
|
||||
|
||||
// Look up Dune: Part Two
|
||||
|
||||
+14
-2
@@ -322,6 +322,8 @@ pub struct AacsState {
|
||||
pub read_data_key: Option<[u8; 16]>,
|
||||
/// Volume ID (16 bytes) -- from SCSI handshake
|
||||
pub volume_id: [u8; 16],
|
||||
/// Handshake error code if authentication failed (None = no HC, or success)
|
||||
pub handshake_error: Option<crate::error::Error>,
|
||||
}
|
||||
|
||||
/// How AACS keys were resolved.
|
||||
@@ -567,11 +569,13 @@ impl Disc {
|
||||
// AACS SCSI handshake — get Volume ID (and read data key for AACS 2.0)
|
||||
let mut volume_id = [0u8; 16];
|
||||
let mut read_data_key = None;
|
||||
let mut handshake_error = None;
|
||||
|
||||
if let Some(ref hc) = keydb.host_cert {
|
||||
if let Ok(mut auth) = aacs::handshake::aacs_authenticate(
|
||||
for hc in &keydb.host_certs {
|
||||
match aacs::handshake::aacs_authenticate(
|
||||
session, &hc.private_key, &hc.certificate,
|
||||
) {
|
||||
Ok(mut auth) => {
|
||||
// Read Volume ID (needed for MK → VUK derivation)
|
||||
if let Ok(vid) = aacs::handshake::read_volume_id(session, &mut auth) {
|
||||
volume_id = vid;
|
||||
@@ -581,6 +585,13 @@ impl Disc {
|
||||
if let Ok((rdk, _wdk)) = aacs::handshake::read_data_keys(session, &mut auth) {
|
||||
read_data_key = Some(rdk);
|
||||
}
|
||||
|
||||
handshake_error = None;
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
handshake_error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,6 +620,7 @@ impl Disc {
|
||||
unit_keys: resolved.unit_keys,
|
||||
read_data_key,
|
||||
volume_id,
|
||||
handshake_error,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user