use crate::AacsError;
pub const REPORT_KEY_OPCODE: u8 = 0xA4;
pub const SEND_KEY_OPCODE: u8 = 0xA3;
pub const READ_DISC_STRUCTURE_OPCODE: u8 = 0xAD;
pub const SEND_DISC_STRUCTURE_OPCODE: u8 = 0xBF;
pub const MMC_CDB_LEN: usize = 12;
pub const KEY_CLASS_CSS: u8 = 0x00;
pub const KEY_CLASS_AACS: u8 = 0x02;
pub const KF_REPORT_AACS_AGID: u8 = 0x00;
pub const KF_REPORT_AACS_DRIVE_CERT_CHAL: u8 = 0x01;
pub const KF_REPORT_AACS_DRIVE_KEY: u8 = 0x02;
pub const KF_REPORT_AACS_BINDING_NONCE_GEN: u8 = 0x20;
pub const KF_REPORT_AACS_BINDING_NONCE_READ: u8 = 0x21;
pub const KF_REPORT_AACS_DRIVE_CERT: u8 = 0x38;
pub const KF_REPORT_AACS_INVALIDATE_AGID: u8 = 0x3F;
pub const KF_SEND_AACS_HOST_CERT_CHAL: u8 = 0x01;
pub const KF_SEND_AACS_HOST_KEY: u8 = 0x02;
pub const KF_SEND_AACS_INVALIDATE_AGID: u8 = 0x3F;
pub const FORMAT_AACS_VOLUME_ID: u8 = 0x80;
pub const FORMAT_AACS_MEDIA_SERIAL: u8 = 0x81;
pub const FORMAT_AACS_MEDIA_ID: u8 = 0x82;
pub const FORMAT_AACS_MEDIA_KEY_BLOCK: u8 = 0x83;
pub const FORMAT_AACS_DATA_KEYS: u8 = 0x84;
pub const FORMAT_AACS_BUS_ENCRYPTION_SECTOR_EXTENTS: u8 = 0x85;
pub const FORMAT_AACS_WRITE_DATA_KEY: u8 = 0x84;
pub const MEDIA_TYPE_BD: u8 = 0x01;
pub const MEDIA_TYPE_DVD: u8 = 0x00;
pub const HOST_NONCE_LEN: usize = 20;
pub const DRIVE_NONCE_LEN: usize = 20;
pub const HOST_CERT_LEN: usize = 92;
pub const DRIVE_CERT_LEN: usize = 92;
pub const EC_POINT_LEN: usize = 40;
pub const EC_SIG_LEN: usize = 40;
pub const VOLUME_ID_LEN: usize = 16;
pub const ID_MAC_LEN: usize = 16;
pub const BINDING_NONCE_LEN: usize = 16;
pub const BINDING_NONCE_MAC_LEN: usize = 16;
pub const DATA_KEY_LEN: usize = 16;
pub const BUS_ENCRYPTION_SECTOR_EXTENT_LEN: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReportKey {
pub key_class: u8,
pub key_format: u8,
pub agid: u8,
pub lba_or_starting_offset: u32,
pub block_count_function: u8,
pub allocation_length: u16,
pub control: u8,
}
impl ReportKey {
pub fn aacs_agid() -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_AGID,
agid: 0,
lba_or_starting_offset: 0,
block_count_function: 0,
allocation_length: 8,
control: 0,
}
}
pub fn aacs_drive_cert_challenge(agid: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_DRIVE_CERT_CHAL,
agid: agid & 0x03,
lba_or_starting_offset: 0,
block_count_function: 0,
allocation_length: 116,
control: 0,
}
}
pub fn aacs_drive_key(agid: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_DRIVE_KEY,
agid: agid & 0x03,
lba_or_starting_offset: 0,
block_count_function: 0,
allocation_length: 84,
control: 0,
}
}
pub fn aacs_drive_cert() -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_DRIVE_CERT,
agid: 0,
lba_or_starting_offset: 0,
block_count_function: 0,
allocation_length: 96,
control: 0,
}
}
pub fn aacs_binding_nonce_gen(agid: u8, starting_lba: u32, block_count: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_BINDING_NONCE_GEN,
agid: agid & 0x03,
lba_or_starting_offset: starting_lba,
block_count_function: block_count,
allocation_length: 36,
control: 0,
}
}
pub fn aacs_binding_nonce_read(agid: u8, starting_lba: u32, block_count: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_BINDING_NONCE_READ,
agid: agid & 0x03,
lba_or_starting_offset: starting_lba,
block_count_function: block_count,
allocation_length: 36,
control: 0,
}
}
pub fn aacs_invalidate_agid(agid: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_INVALIDATE_AGID,
agid: agid & 0x03,
lba_or_starting_offset: 0,
block_count_function: 0,
allocation_length: 0,
control: 0,
}
}
pub fn cdb(&self) -> [u8; MMC_CDB_LEN] {
let mut cdb = [0u8; MMC_CDB_LEN];
cdb[0] = REPORT_KEY_OPCODE;
cdb[1] = 0;
cdb[2] = (self.lba_or_starting_offset >> 24) as u8;
cdb[3] = (self.lba_or_starting_offset >> 16) as u8;
cdb[4] = (self.lba_or_starting_offset >> 8) as u8;
cdb[5] = self.lba_or_starting_offset as u8;
cdb[6] = self.block_count_function;
cdb[7] = self.key_class;
cdb[8] = (self.allocation_length >> 8) as u8;
cdb[9] = self.allocation_length as u8;
cdb[10] = ((self.agid & 0x03) << 6) | (self.key_format & 0x3F);
cdb[11] = self.control;
cdb
}
pub fn parse_cdb(cdb: &[u8; MMC_CDB_LEN]) -> Result<Self, AacsError> {
if cdb[0] != REPORT_KEY_OPCODE {
return Err(AacsError::InvalidValue {
what: "REPORT_KEY opcode",
value: cdb[0] as u64,
});
}
Ok(Self {
key_class: cdb[7],
key_format: cdb[10] & 0x3F,
agid: (cdb[10] >> 6) & 0x03,
lba_or_starting_offset: ((cdb[2] as u32) << 24)
| ((cdb[3] as u32) << 16)
| ((cdb[4] as u32) << 8)
| (cdb[5] as u32),
block_count_function: cdb[6],
allocation_length: ((cdb[8] as u16) << 8) | (cdb[9] as u16),
control: cdb[11],
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SendKey {
pub key_class: u8,
pub key_format: u8,
pub agid: u8,
pub parameter_list_length: u16,
pub control: u8,
}
impl SendKey {
pub fn aacs_host_cert_challenge(agid: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_SEND_AACS_HOST_CERT_CHAL,
agid: agid & 0x03,
parameter_list_length: 116,
control: 0,
}
}
pub fn aacs_host_key(agid: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_SEND_AACS_HOST_KEY,
agid: agid & 0x03,
parameter_list_length: 84,
control: 0,
}
}
pub fn aacs_invalidate_agid(agid: u8) -> Self {
Self {
key_class: KEY_CLASS_AACS,
key_format: KF_SEND_AACS_INVALIDATE_AGID,
agid: agid & 0x03,
parameter_list_length: 0,
control: 0,
}
}
pub fn cdb(&self) -> [u8; MMC_CDB_LEN] {
let mut cdb = [0u8; MMC_CDB_LEN];
cdb[0] = SEND_KEY_OPCODE;
cdb[1] = 0;
cdb[2] = 0;
cdb[3] = 0;
cdb[4] = 0;
cdb[5] = 0;
cdb[6] = 0;
cdb[7] = self.key_class;
cdb[8] = (self.parameter_list_length >> 8) as u8;
cdb[9] = self.parameter_list_length as u8;
cdb[10] = ((self.agid & 0x03) << 6) | (self.key_format & 0x3F);
cdb[11] = self.control;
cdb
}
pub fn parse_cdb(cdb: &[u8; MMC_CDB_LEN]) -> Result<Self, AacsError> {
if cdb[0] != SEND_KEY_OPCODE {
return Err(AacsError::InvalidValue {
what: "SEND_KEY opcode",
value: cdb[0] as u64,
});
}
Ok(Self {
key_class: cdb[7],
key_format: cdb[10] & 0x3F,
agid: (cdb[10] >> 6) & 0x03,
parameter_list_length: ((cdb[8] as u16) << 8) | (cdb[9] as u16),
control: cdb[11],
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReadDiscStructure {
pub media_type: u8,
pub address: u32,
pub layer_number: u8,
pub format: u8,
pub allocation_length: u16,
pub agid: u8,
pub control: u8,
}
impl ReadDiscStructure {
pub fn aacs_volume_id(agid: u8) -> Self {
Self {
media_type: MEDIA_TYPE_BD,
address: 0,
layer_number: 0,
format: FORMAT_AACS_VOLUME_ID,
allocation_length: 36,
agid: agid & 0x03,
control: 0,
}
}
pub fn aacs_media_serial(agid: u8) -> Self {
Self {
media_type: MEDIA_TYPE_BD,
address: 0,
layer_number: 0,
format: FORMAT_AACS_MEDIA_SERIAL,
allocation_length: 36,
agid: agid & 0x03,
control: 0,
}
}
pub fn aacs_media_id(agid: u8) -> Self {
Self {
media_type: MEDIA_TYPE_BD,
address: 0,
layer_number: 0,
format: FORMAT_AACS_MEDIA_ID,
allocation_length: 36,
agid: agid & 0x03,
control: 0,
}
}
pub fn aacs_media_key_block_pack(agid: u8, pack_number: u32, layer: u8) -> Self {
Self {
media_type: MEDIA_TYPE_BD,
address: pack_number,
layer_number: layer,
format: FORMAT_AACS_MEDIA_KEY_BLOCK,
allocation_length: 32 * 1024 + 4,
agid: agid & 0x03,
control: 0,
}
}
pub fn aacs_data_keys(agid: u8) -> Self {
Self {
media_type: MEDIA_TYPE_BD,
address: 0,
layer_number: 0,
format: FORMAT_AACS_DATA_KEYS,
allocation_length: 36,
agid: agid & 0x03,
control: 0,
}
}
pub fn aacs_bus_encryption_sector_extents() -> Self {
Self {
media_type: MEDIA_TYPE_BD,
address: 0,
layer_number: 0,
format: FORMAT_AACS_BUS_ENCRYPTION_SECTOR_EXTENTS,
allocation_length: 12 + 256 * BUS_ENCRYPTION_SECTOR_EXTENT_LEN as u16,
agid: 0,
control: 0,
}
}
pub fn cdb(&self) -> [u8; MMC_CDB_LEN] {
let mut cdb = [0u8; MMC_CDB_LEN];
cdb[0] = READ_DISC_STRUCTURE_OPCODE;
cdb[1] = self.media_type & 0x0F;
cdb[2] = (self.address >> 24) as u8;
cdb[3] = (self.address >> 16) as u8;
cdb[4] = (self.address >> 8) as u8;
cdb[5] = self.address as u8;
cdb[6] = self.layer_number;
cdb[7] = self.format;
cdb[8] = (self.allocation_length >> 8) as u8;
cdb[9] = self.allocation_length as u8;
cdb[10] = (self.agid & 0x03) << 6;
cdb[11] = self.control;
cdb
}
pub fn parse_cdb(cdb: &[u8; MMC_CDB_LEN]) -> Result<Self, AacsError> {
if cdb[0] != READ_DISC_STRUCTURE_OPCODE {
return Err(AacsError::InvalidValue {
what: "READ_DISC_STRUCTURE opcode",
value: cdb[0] as u64,
});
}
Ok(Self {
media_type: cdb[1] & 0x0F,
address: ((cdb[2] as u32) << 24)
| ((cdb[3] as u32) << 16)
| ((cdb[4] as u32) << 8)
| (cdb[5] as u32),
layer_number: cdb[6],
format: cdb[7],
allocation_length: ((cdb[8] as u16) << 8) | (cdb[9] as u16),
agid: (cdb[10] >> 6) & 0x03,
control: cdb[11],
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SendDiscStructure {
pub media_type: u8,
pub format: u8,
pub parameter_list_length: u16,
pub agid: u8,
pub control: u8,
}
impl SendDiscStructure {
pub fn aacs_write_data_key(agid: u8) -> Self {
Self {
media_type: MEDIA_TYPE_BD,
format: FORMAT_AACS_WRITE_DATA_KEY,
parameter_list_length: 20,
agid: agid & 0x03,
control: 0,
}
}
pub fn cdb(&self) -> [u8; MMC_CDB_LEN] {
let mut cdb = [0u8; MMC_CDB_LEN];
cdb[0] = SEND_DISC_STRUCTURE_OPCODE;
cdb[1] = self.media_type & 0x0F;
cdb[7] = self.format;
cdb[8] = (self.parameter_list_length >> 8) as u8;
cdb[9] = self.parameter_list_length as u8;
cdb[10] = (self.agid & 0x03) << 6;
cdb[11] = self.control;
cdb
}
pub fn parse_cdb(cdb: &[u8; MMC_CDB_LEN]) -> Result<Self, AacsError> {
if cdb[0] != SEND_DISC_STRUCTURE_OPCODE {
return Err(AacsError::InvalidValue {
what: "SEND_DISC_STRUCTURE opcode",
value: cdb[0] as u64,
});
}
Ok(Self {
media_type: cdb[1] & 0x0F,
format: cdb[7],
parameter_list_length: ((cdb[8] as u16) << 8) | (cdb[9] as u16),
agid: (cdb[10] >> 6) & 0x03,
control: cdb[11],
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AgidResponse {
pub agid: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DriveCertChallengeResponse {
pub drive_nonce: [u8; DRIVE_NONCE_LEN],
pub drive_cert: [u8; DRIVE_CERT_LEN],
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DriveKeyResponse {
pub dv: [u8; EC_POINT_LEN],
pub dsig: [u8; EC_SIG_LEN],
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DriveCertResponse {
pub drive_cert: [u8; DRIVE_CERT_LEN],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VolumeIdResponse {
pub volume_id: [u8; VOLUME_ID_LEN],
pub mac: [u8; ID_MAC_LEN],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MediaSerialNumberResponse {
pub pmsn: [u8; VOLUME_ID_LEN],
pub mac: [u8; ID_MAC_LEN],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MediaIdentifierResponse {
pub media_id: [u8; VOLUME_ID_LEN],
pub mac: [u8; ID_MAC_LEN],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BindingNonceResponse {
pub binding_nonce: [u8; BINDING_NONCE_LEN],
pub mac: [u8; BINDING_NONCE_MAC_LEN],
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MkbPackResponse {
pub total_packs: u8,
pub pack_data: Vec<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DataKeysResponse {
pub read_data_key_encrypted: [u8; DATA_KEY_LEN],
pub write_data_key_encrypted: [u8; DATA_KEY_LEN],
}
impl DataKeysResponse {
pub fn decrypt_read_data_key(&self, bus_key: &[u8; 16]) -> [u8; DATA_KEY_LEN] {
crate::aes::aes_128_ecb_decrypt(bus_key, &self.read_data_key_encrypted)
}
pub fn decrypt_write_data_key(&self, bus_key: &[u8; 16]) -> [u8; DATA_KEY_LEN] {
crate::aes::aes_128_ecb_decrypt(bus_key, &self.write_data_key_encrypted)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BusEncryptionSectorExtent {
pub start_lba: u32,
pub lba_count: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BusEncryptionSectorExtentsResponse {
pub maximum: u16,
pub extents: Vec<BusEncryptionSectorExtent>,
}
fn read_u16_be(buf: &[u8], what: &'static str) -> Result<u16, AacsError> {
if buf.len() < 2 {
return Err(AacsError::Truncated(what));
}
Ok(((buf[0] as u16) << 8) | (buf[1] as u16))
}
pub fn parse_report_key_agid(buf: &[u8]) -> Result<AgidResponse, AacsError> {
let length = read_u16_be(buf, "REPORT_KEY AGID header")?;
if length != 0x0006 {
return Err(AacsError::InvalidValue {
what: "REPORT_KEY AGID length",
value: length as u64,
});
}
if buf.len() < 8 {
return Err(AacsError::Truncated("REPORT_KEY AGID payload"));
}
Ok(AgidResponse {
agid: (buf[7] >> 6) & 0x03,
})
}
pub fn parse_report_key_drive_cert_chal(
buf: &[u8],
) -> Result<DriveCertChallengeResponse, AacsError> {
let length = read_u16_be(buf, "REPORT_KEY Drive Cert Challenge header")?;
if length != 0x0072 {
return Err(AacsError::InvalidValue {
what: "REPORT_KEY Drive Cert Challenge length",
value: length as u64,
});
}
if buf.len() < 116 {
return Err(AacsError::Truncated(
"REPORT_KEY Drive Cert Challenge payload",
));
}
let mut drive_nonce = [0u8; DRIVE_NONCE_LEN];
drive_nonce.copy_from_slice(&buf[4..4 + DRIVE_NONCE_LEN]);
let mut drive_cert = [0u8; DRIVE_CERT_LEN];
drive_cert.copy_from_slice(&buf[24..24 + DRIVE_CERT_LEN]);
Ok(DriveCertChallengeResponse {
drive_nonce,
drive_cert,
})
}
pub fn parse_report_key_drive_key(buf: &[u8]) -> Result<DriveKeyResponse, AacsError> {
let length = read_u16_be(buf, "REPORT_KEY Drive Key header")?;
if length != 0x0052 {
return Err(AacsError::InvalidValue {
what: "REPORT_KEY Drive Key length",
value: length as u64,
});
}
if buf.len() < 84 {
return Err(AacsError::Truncated("REPORT_KEY Drive Key payload"));
}
let mut dv = [0u8; EC_POINT_LEN];
dv.copy_from_slice(&buf[4..4 + EC_POINT_LEN]);
let mut dsig = [0u8; EC_SIG_LEN];
dsig.copy_from_slice(&buf[44..44 + EC_SIG_LEN]);
Ok(DriveKeyResponse { dv, dsig })
}
pub fn parse_report_key_drive_cert(buf: &[u8]) -> Result<DriveCertResponse, AacsError> {
let length = read_u16_be(buf, "REPORT_KEY Drive Cert header")?;
if length != 0x005E {
return Err(AacsError::InvalidValue {
what: "REPORT_KEY Drive Cert length",
value: length as u64,
});
}
if buf.len() < 96 {
return Err(AacsError::Truncated("REPORT_KEY Drive Cert payload"));
}
let mut drive_cert = [0u8; DRIVE_CERT_LEN];
drive_cert.copy_from_slice(&buf[4..4 + DRIVE_CERT_LEN]);
Ok(DriveCertResponse { drive_cert })
}
pub fn parse_volume_id_response(buf: &[u8]) -> Result<VolumeIdResponse, AacsError> {
let length = read_u16_be(buf, "Volume ID response header")?;
if length != 0x0022 {
return Err(AacsError::InvalidValue {
what: "Volume ID response length",
value: length as u64,
});
}
if buf.len() < 36 {
return Err(AacsError::Truncated("Volume ID response payload"));
}
let mut volume_id = [0u8; VOLUME_ID_LEN];
volume_id.copy_from_slice(&buf[4..4 + VOLUME_ID_LEN]);
let mut mac = [0u8; ID_MAC_LEN];
mac.copy_from_slice(&buf[20..20 + ID_MAC_LEN]);
Ok(VolumeIdResponse { volume_id, mac })
}
pub fn parse_media_serial_response(buf: &[u8]) -> Result<MediaSerialNumberResponse, AacsError> {
let length = read_u16_be(buf, "PMSN response header")?;
if length != 0x0022 {
return Err(AacsError::InvalidValue {
what: "PMSN response length",
value: length as u64,
});
}
if buf.len() < 36 {
return Err(AacsError::Truncated("PMSN response payload"));
}
let mut pmsn = [0u8; VOLUME_ID_LEN];
pmsn.copy_from_slice(&buf[4..4 + VOLUME_ID_LEN]);
let mut mac = [0u8; ID_MAC_LEN];
mac.copy_from_slice(&buf[20..20 + ID_MAC_LEN]);
Ok(MediaSerialNumberResponse { pmsn, mac })
}
pub fn parse_media_id_response(buf: &[u8]) -> Result<MediaIdentifierResponse, AacsError> {
let length = read_u16_be(buf, "Media ID response header")?;
if length != 0x0022 {
return Err(AacsError::InvalidValue {
what: "Media ID response length",
value: length as u64,
});
}
if buf.len() < 36 {
return Err(AacsError::Truncated("Media ID response payload"));
}
let mut media_id = [0u8; VOLUME_ID_LEN];
media_id.copy_from_slice(&buf[4..4 + VOLUME_ID_LEN]);
let mut mac = [0u8; ID_MAC_LEN];
mac.copy_from_slice(&buf[20..20 + ID_MAC_LEN]);
Ok(MediaIdentifierResponse { media_id, mac })
}
pub fn parse_report_key_binding_nonce(buf: &[u8]) -> Result<BindingNonceResponse, AacsError> {
let length = read_u16_be(buf, "Binding Nonce response header")?;
if length != 0x0022 {
return Err(AacsError::InvalidValue {
what: "Binding Nonce response length",
value: length as u64,
});
}
if buf.len() < 36 {
return Err(AacsError::Truncated("Binding Nonce response payload"));
}
let mut binding_nonce = [0u8; BINDING_NONCE_LEN];
binding_nonce.copy_from_slice(&buf[4..4 + BINDING_NONCE_LEN]);
let mut mac = [0u8; BINDING_NONCE_MAC_LEN];
mac.copy_from_slice(&buf[20..20 + BINDING_NONCE_MAC_LEN]);
Ok(BindingNonceResponse { binding_nonce, mac })
}
pub fn parse_data_keys_response(buf: &[u8]) -> Result<DataKeysResponse, AacsError> {
let length = read_u16_be(buf, "Data Keys response header")?;
if length != 0x0022 {
return Err(AacsError::InvalidValue {
what: "Data Keys response length",
value: length as u64,
});
}
if buf.len() < 36 {
return Err(AacsError::Truncated("Data Keys response payload"));
}
let mut read_data_key_encrypted = [0u8; DATA_KEY_LEN];
read_data_key_encrypted.copy_from_slice(&buf[4..4 + DATA_KEY_LEN]);
let mut write_data_key_encrypted = [0u8; DATA_KEY_LEN];
write_data_key_encrypted.copy_from_slice(&buf[20..20 + DATA_KEY_LEN]);
Ok(DataKeysResponse {
read_data_key_encrypted,
write_data_key_encrypted,
})
}
pub fn parse_bus_encryption_sector_extents_response(
buf: &[u8],
) -> Result<BusEncryptionSectorExtentsResponse, AacsError> {
let length = read_u16_be(buf, "Bus-Encryption Sector Extents response header")? as usize;
if length < 2 {
return Err(AacsError::InvalidValue {
what: "Bus-Encryption Sector Extents response length",
value: length as u64,
});
}
if buf.len() < 2 + length {
return Err(AacsError::Truncated(
"Bus-Encryption Sector Extents response payload",
));
}
let extent_section_len = length - 2;
if extent_section_len % BUS_ENCRYPTION_SECTOR_EXTENT_LEN != 0 {
return Err(AacsError::Truncated(
"Bus-Encryption Sector Extents response extent record stride",
));
}
let wire_max = buf[3];
let maximum = if wire_max == 0 { 256 } else { wire_max as u16 };
let extent_count = extent_section_len / BUS_ENCRYPTION_SECTOR_EXTENT_LEN;
let mut extents = Vec::with_capacity(extent_count);
for i in 0..extent_count {
let base = 4 + i * BUS_ENCRYPTION_SECTOR_EXTENT_LEN;
let start_lba = ((buf[base + 8] as u32) << 24)
| ((buf[base + 9] as u32) << 16)
| ((buf[base + 10] as u32) << 8)
| (buf[base + 11] as u32);
let lba_count = ((buf[base + 12] as u32) << 24)
| ((buf[base + 13] as u32) << 16)
| ((buf[base + 14] as u32) << 8)
| (buf[base + 15] as u32);
extents.push(BusEncryptionSectorExtent {
start_lba,
lba_count,
});
}
Ok(BusEncryptionSectorExtentsResponse { maximum, extents })
}
pub fn parse_mkb_pack_response(buf: &[u8]) -> Result<MkbPackResponse, AacsError> {
let length = read_u16_be(buf, "MKB pack response header")? as usize;
if length < 2 {
return Err(AacsError::InvalidValue {
what: "MKB pack response length",
value: length as u64,
});
}
let body_len = length - 2;
if buf.len() < 4 + body_len {
return Err(AacsError::Truncated("MKB pack response payload"));
}
let total_packs = buf[3];
let pack_data = buf[4..4 + body_len].to_vec();
Ok(MkbPackResponse {
total_packs,
pack_data,
})
}
pub fn build_send_key_host_cert_chal(
host_nonce: &[u8; HOST_NONCE_LEN],
host_cert: &[u8; HOST_CERT_LEN],
) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + HOST_NONCE_LEN + HOST_CERT_LEN);
out.extend_from_slice(&[0x00, 0x72, 0x00, 0x00]);
out.extend_from_slice(host_nonce);
out.extend_from_slice(host_cert);
debug_assert_eq!(out.len(), 116);
out
}
pub fn build_send_key_host_key(hv: &[u8; EC_POINT_LEN], hsig: &[u8; EC_SIG_LEN]) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + EC_POINT_LEN + EC_SIG_LEN);
out.extend_from_slice(&[0x00, 0x52, 0x00, 0x00]);
out.extend_from_slice(hv);
out.extend_from_slice(hsig);
debug_assert_eq!(out.len(), 84);
out
}
pub fn parse_send_key_host_cert_chal(
buf: &[u8],
) -> Result<([u8; HOST_NONCE_LEN], [u8; HOST_CERT_LEN]), AacsError> {
let length = read_u16_be(buf, "SEND_KEY Host Cert Challenge header")?;
if length != 0x0072 {
return Err(AacsError::InvalidValue {
what: "SEND_KEY Host Cert Challenge length",
value: length as u64,
});
}
if buf.len() < 116 {
return Err(AacsError::Truncated("SEND_KEY Host Cert Challenge payload"));
}
let mut host_nonce = [0u8; HOST_NONCE_LEN];
host_nonce.copy_from_slice(&buf[4..4 + HOST_NONCE_LEN]);
let mut host_cert = [0u8; HOST_CERT_LEN];
host_cert.copy_from_slice(&buf[24..24 + HOST_CERT_LEN]);
Ok((host_nonce, host_cert))
}
pub fn parse_send_key_host_key(
buf: &[u8],
) -> Result<([u8; EC_POINT_LEN], [u8; EC_SIG_LEN]), AacsError> {
let length = read_u16_be(buf, "SEND_KEY Host Key header")?;
if length != 0x0052 {
return Err(AacsError::InvalidValue {
what: "SEND_KEY Host Key length",
value: length as u64,
});
}
if buf.len() < 84 {
return Err(AacsError::Truncated("SEND_KEY Host Key payload"));
}
let mut hv = [0u8; EC_POINT_LEN];
hv.copy_from_slice(&buf[4..4 + EC_POINT_LEN]);
let mut hsig = [0u8; EC_SIG_LEN];
hsig.copy_from_slice(&buf[44..44 + EC_SIG_LEN]);
Ok((hv, hsig))
}
pub fn build_send_disc_structure_write_data_key(
write_data_key_encrypted: &[u8; DATA_KEY_LEN],
) -> Vec<u8> {
let mut out = Vec::with_capacity(4 + DATA_KEY_LEN);
out.extend_from_slice(&[0x00, 0x12, 0x00, 0x00]);
out.extend_from_slice(write_data_key_encrypted);
debug_assert_eq!(out.len(), 20);
out
}
pub fn parse_send_disc_structure_write_data_key(
buf: &[u8],
) -> Result<[u8; DATA_KEY_LEN], AacsError> {
let length = read_u16_be(buf, "SEND_DISC_STRUCTURE Write Data Key header")?;
if length != 0x0012 {
return Err(AacsError::InvalidValue {
what: "SEND_DISC_STRUCTURE Write Data Key length",
value: length as u64,
});
}
if buf.len() < 4 + DATA_KEY_LEN {
return Err(AacsError::Truncated(
"SEND_DISC_STRUCTURE Write Data Key payload",
));
}
let mut kwd = [0u8; DATA_KEY_LEN];
kwd.copy_from_slice(&buf[4..4 + DATA_KEY_LEN]);
Ok(kwd)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataDirection {
None,
FromDevice,
ToDevice,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScsiResponse {
pub status: u8,
pub data: Vec<u8>,
}
impl ScsiResponse {
pub fn good(data: Vec<u8>) -> Self {
Self { status: 0x00, data }
}
}
pub trait DriveCommand {
fn execute(
&mut self,
cdb: &[u8; MMC_CDB_LEN],
direction: DataDirection,
data_out: &[u8],
allocation_length: u16,
) -> Result<ScsiResponse, AacsError>;
}
#[cfg(any(test, feature = "test-util"))]
#[derive(Debug, Clone)]
pub struct MockDrive {
pub agid_to_return: u8,
pub drive_nonce: [u8; DRIVE_NONCE_LEN],
pub drive_cert: [u8; DRIVE_CERT_LEN],
pub drive_dv: [u8; EC_POINT_LEN],
pub drive_dsig: [u8; EC_SIG_LEN],
pub volume_id: [u8; VOLUME_ID_LEN],
pub volume_id_mac: [u8; ID_MAC_LEN],
pub media_serial_number: [u8; VOLUME_ID_LEN],
pub media_serial_mac: [u8; ID_MAC_LEN],
pub media_identifier: [u8; VOLUME_ID_LEN],
pub media_id_mac: [u8; ID_MAC_LEN],
pub binding_nonce: [u8; BINDING_NONCE_LEN],
pub binding_nonce_mac: [u8; BINDING_NONCE_MAC_LEN],
pub last_binding_nonce_op: Option<(u8, u32, u8)>,
pub read_data_key: [u8; DATA_KEY_LEN],
pub write_data_key: [u8; DATA_KEY_LEN],
pub last_data_keys_read: bool,
pub last_write_data_key_sent: Option<[u8; DATA_KEY_LEN]>,
pub max_bus_encryption_sector_extents: u16,
pub bus_encryption_sector_extents: Vec<BusEncryptionSectorExtent>,
pub last_host_cert_chal: Option<Vec<u8>>,
pub last_host_key: Option<Vec<u8>>,
pub agid_invalidated: bool,
pub auth: Option<crate::ake::DriveAuthState>,
}
#[cfg(any(test, feature = "test-util"))]
impl Default for MockDrive {
fn default() -> Self {
Self {
agid_to_return: 0,
drive_nonce: [0u8; DRIVE_NONCE_LEN],
drive_cert: [0u8; DRIVE_CERT_LEN],
drive_dv: [0u8; EC_POINT_LEN],
drive_dsig: [0u8; EC_SIG_LEN],
volume_id: [0u8; VOLUME_ID_LEN],
volume_id_mac: [0u8; ID_MAC_LEN],
media_serial_number: [0u8; VOLUME_ID_LEN],
media_serial_mac: [0u8; ID_MAC_LEN],
media_identifier: [0u8; VOLUME_ID_LEN],
media_id_mac: [0u8; ID_MAC_LEN],
binding_nonce: [0u8; BINDING_NONCE_LEN],
binding_nonce_mac: [0u8; BINDING_NONCE_MAC_LEN],
last_binding_nonce_op: None,
read_data_key: [0u8; DATA_KEY_LEN],
write_data_key: [0u8; DATA_KEY_LEN],
last_data_keys_read: false,
last_write_data_key_sent: None,
max_bus_encryption_sector_extents: 1,
bus_encryption_sector_extents: Vec::new(),
last_host_cert_chal: None,
last_host_key: None,
agid_invalidated: false,
auth: None,
}
}
}
#[cfg(any(test, feature = "test-util"))]
impl MockDrive {
pub fn with_test_fixture() -> Self {
let mut drive_cert = [0u8; DRIVE_CERT_LEN];
drive_cert[0] = 0x01;
drive_cert[2] = 0x00;
drive_cert[3] = 0x5C;
drive_cert[4] = 0x01;
drive_cert[5] = 0x02;
drive_cert[6] = 0x03;
drive_cert[7] = 0x04;
drive_cert[8] = 0x05;
drive_cert[9] = 0x06;
for (i, b) in drive_cert.iter_mut().enumerate().skip(10) {
*b = i as u8;
}
let mut drive_nonce = [0u8; DRIVE_NONCE_LEN];
for (i, b) in drive_nonce.iter_mut().enumerate() {
*b = 0xA0 | (i as u8);
}
let mut drive_dv = [0u8; EC_POINT_LEN];
for (i, b) in drive_dv.iter_mut().enumerate() {
*b = 0xC0 ^ (i as u8);
}
let mut drive_dsig = [0u8; EC_SIG_LEN];
for (i, b) in drive_dsig.iter_mut().enumerate() {
*b = 0xE0 ^ (i as u8);
}
let mut volume_id = [0u8; VOLUME_ID_LEN];
for (i, b) in volume_id.iter_mut().enumerate() {
*b = 0xB0 | (i as u8);
}
let mut volume_id_mac = [0u8; ID_MAC_LEN];
for (i, b) in volume_id_mac.iter_mut().enumerate() {
*b = 0x40 ^ (i as u8);
}
let mut media_serial_number = [0u8; VOLUME_ID_LEN];
for (i, b) in media_serial_number.iter_mut().enumerate() {
*b = 0x70 | (i as u8);
}
let mut media_serial_mac = [0u8; ID_MAC_LEN];
for (i, b) in media_serial_mac.iter_mut().enumerate() {
*b = 0x50 ^ (i as u8);
}
let mut media_identifier = [0u8; VOLUME_ID_LEN];
for (i, b) in media_identifier.iter_mut().enumerate() {
*b = 0x30 | (i as u8);
}
let mut media_id_mac = [0u8; ID_MAC_LEN];
for (i, b) in media_id_mac.iter_mut().enumerate() {
*b = 0x60 ^ (i as u8);
}
let mut binding_nonce = [0u8; BINDING_NONCE_LEN];
for (i, b) in binding_nonce.iter_mut().enumerate() {
*b = 0x20 | (i as u8);
}
let mut binding_nonce_mac = [0u8; BINDING_NONCE_MAC_LEN];
for (i, b) in binding_nonce_mac.iter_mut().enumerate() {
*b = 0x10 ^ (i as u8);
}
let mut read_data_key = [0u8; DATA_KEY_LEN];
for (i, b) in read_data_key.iter_mut().enumerate() {
*b = 0x80 | (i as u8);
}
let mut write_data_key = [0u8; DATA_KEY_LEN];
for (i, b) in write_data_key.iter_mut().enumerate() {
*b = 0x90 | (i as u8);
}
Self {
agid_to_return: 1,
drive_nonce,
drive_cert,
drive_dv,
drive_dsig,
volume_id,
volume_id_mac,
media_serial_number,
media_serial_mac,
media_identifier,
media_id_mac,
binding_nonce,
binding_nonce_mac,
last_binding_nonce_op: None,
read_data_key,
write_data_key,
last_data_keys_read: false,
last_write_data_key_sent: None,
max_bus_encryption_sector_extents: 4,
bus_encryption_sector_extents: vec![
BusEncryptionSectorExtent {
start_lba: 0x0001_0000,
lba_count: 0x0000_2000,
},
BusEncryptionSectorExtent {
start_lba: 0x0080_0000,
lba_count: 0x0000_4000,
},
],
last_host_cert_chal: None,
last_host_key: None,
agid_invalidated: false,
auth: None,
}
}
}
#[cfg(any(test, feature = "test-util"))]
impl DriveCommand for MockDrive {
fn execute(
&mut self,
cdb: &[u8; MMC_CDB_LEN],
direction: DataDirection,
data_out: &[u8],
_allocation_length: u16,
) -> Result<ScsiResponse, AacsError> {
match cdb[0] {
REPORT_KEY_OPCODE => {
let rk = ReportKey::parse_cdb(cdb)?;
if rk.key_class != KEY_CLASS_AACS {
return Err(AacsError::InvalidValue {
what: "MockDrive REPORT_KEY Key Class",
value: rk.key_class as u64,
});
}
match rk.key_format {
KF_REPORT_AACS_AGID => {
let mut out = vec![0u8; 8];
out[0] = 0x00;
out[1] = 0x06;
out[7] = (self.agid_to_return & 0x03) << 6;
Ok(ScsiResponse::good(out))
}
KF_REPORT_AACS_DRIVE_CERT_CHAL => {
let (nonce, cert): ([u8; DRIVE_NONCE_LEN], [u8; DRIVE_CERT_LEN]) =
match &self.auth {
Some(a) => (a.drive_nonce, a.drive_cert),
None => (self.drive_nonce, self.drive_cert),
};
let mut out = Vec::with_capacity(116);
out.extend_from_slice(&[0x00, 0x72, 0x00, 0x00]);
out.extend_from_slice(&nonce);
out.extend_from_slice(&cert);
Ok(ScsiResponse::good(out))
}
KF_REPORT_AACS_DRIVE_KEY => {
let (dv, dsig): ([u8; EC_POINT_LEN], [u8; EC_SIG_LEN]) = match &self.auth {
Some(a) => a.drive_key_response()?,
None => (self.drive_dv, self.drive_dsig),
};
let mut out = Vec::with_capacity(84);
out.extend_from_slice(&[0x00, 0x52, 0x00, 0x00]);
out.extend_from_slice(&dv);
out.extend_from_slice(&dsig);
Ok(ScsiResponse::good(out))
}
KF_REPORT_AACS_DRIVE_CERT => {
let mut out = Vec::with_capacity(96);
out.extend_from_slice(&[0x00, 0x5E, 0x00, 0x00]);
out.extend_from_slice(&self.drive_cert);
Ok(ScsiResponse::good(out))
}
KF_REPORT_AACS_BINDING_NONCE_GEN | KF_REPORT_AACS_BINDING_NONCE_READ => {
self.last_binding_nonce_op = Some((
rk.key_format,
rk.lba_or_starting_offset,
rk.block_count_function,
));
let mac: [u8; BINDING_NONCE_MAC_LEN] = match &self.auth {
Some(a) if a.bus_key.is_some() => {
crate::aes::aes_128_cmac(&a.bus_key.unwrap(), &self.binding_nonce)
}
_ => self.binding_nonce_mac,
};
let mut out = Vec::with_capacity(36);
out.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
out.extend_from_slice(&self.binding_nonce);
out.extend_from_slice(&mac);
Ok(ScsiResponse::good(out))
}
KF_REPORT_AACS_INVALIDATE_AGID => {
self.agid_invalidated = true;
Ok(ScsiResponse::good(Vec::new()))
}
other => Err(AacsError::InvalidValue {
what: "MockDrive REPORT_KEY Key Format",
value: other as u64,
}),
}
}
SEND_KEY_OPCODE => {
let sk = SendKey::parse_cdb(cdb)?;
if sk.key_class != KEY_CLASS_AACS {
return Err(AacsError::InvalidValue {
what: "MockDrive SEND_KEY Key Class",
value: sk.key_class as u64,
});
}
if direction != DataDirection::ToDevice
&& sk.key_format != KF_SEND_AACS_INVALIDATE_AGID
{
return Err(AacsError::InvalidValue {
what: "MockDrive SEND_KEY data direction",
value: 0,
});
}
match sk.key_format {
KF_SEND_AACS_HOST_CERT_CHAL => {
let (hn, hcert) = parse_send_key_host_cert_chal(data_out)?;
if let Some(auth) = self.auth.as_mut() {
auth.accept_host_cert_challenge(&hn, &hcert)?;
}
self.last_host_cert_chal = Some(data_out.to_vec());
Ok(ScsiResponse::good(Vec::new()))
}
KF_SEND_AACS_HOST_KEY => {
let (hv, hsig) = parse_send_key_host_key(data_out)?;
if let Some(auth) = self.auth.as_mut() {
auth.accept_host_key(&hv, &hsig)?;
}
self.last_host_key = Some(data_out.to_vec());
Ok(ScsiResponse::good(Vec::new()))
}
KF_SEND_AACS_INVALIDATE_AGID => {
self.agid_invalidated = true;
Ok(ScsiResponse::good(Vec::new()))
}
other => Err(AacsError::InvalidValue {
what: "MockDrive SEND_KEY Key Format",
value: other as u64,
}),
}
}
READ_DISC_STRUCTURE_OPCODE => {
let rds = ReadDiscStructure::parse_cdb(cdb)?;
match rds.format {
FORMAT_AACS_VOLUME_ID => {
let mac: [u8; ID_MAC_LEN] = match &self.auth {
Some(a) if a.bus_key.is_some() => {
crate::aes::aes_128_cmac(&a.bus_key.unwrap(), &self.volume_id)
}
_ => self.volume_id_mac,
};
let mut out = Vec::with_capacity(36);
out.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
out.extend_from_slice(&self.volume_id);
out.extend_from_slice(&mac);
Ok(ScsiResponse::good(out))
}
FORMAT_AACS_MEDIA_SERIAL => {
let mac: [u8; ID_MAC_LEN] = match &self.auth {
Some(a) if a.bus_key.is_some() => crate::aes::aes_128_cmac(
&a.bus_key.unwrap(),
&self.media_serial_number,
),
_ => self.media_serial_mac,
};
let mut out = Vec::with_capacity(36);
out.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
out.extend_from_slice(&self.media_serial_number);
out.extend_from_slice(&mac);
Ok(ScsiResponse::good(out))
}
FORMAT_AACS_MEDIA_ID => {
let mac: [u8; ID_MAC_LEN] = match &self.auth {
Some(a) if a.bus_key.is_some() => crate::aes::aes_128_cmac(
&a.bus_key.unwrap(),
&self.media_identifier,
),
_ => self.media_id_mac,
};
let mut out = Vec::with_capacity(36);
out.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
out.extend_from_slice(&self.media_identifier);
out.extend_from_slice(&mac);
Ok(ScsiResponse::good(out))
}
FORMAT_AACS_DATA_KEYS => {
let (krd_wrapped, kwd_wrapped) = match &self.auth {
Some(a) if a.bus_key.is_some() => {
let bk = a.bus_key.unwrap();
let krd = crate::aes::aes_128_ecb_encrypt(&bk, &self.read_data_key);
let kwd =
crate::aes::aes_128_ecb_encrypt(&bk, &self.write_data_key);
(krd, kwd)
}
Some(_) => {
return Err(AacsError::InvalidValue {
what: "READ_DISC_STRUCTURE Format 0x84 without Bus Key",
value: 0,
});
}
None => (self.read_data_key, self.write_data_key),
};
self.last_data_keys_read = true;
let mut out = Vec::with_capacity(36);
out.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
out.extend_from_slice(&krd_wrapped);
out.extend_from_slice(&kwd_wrapped);
Ok(ScsiResponse::good(out))
}
FORMAT_AACS_BUS_ENCRYPTION_SECTOR_EXTENTS => {
let extent_count = self.bus_encryption_sector_extents.len();
let length = (extent_count * BUS_ENCRYPTION_SECTOR_EXTENT_LEN) + 2;
let mut out = Vec::with_capacity(2 + length);
out.push((length >> 8) as u8);
out.push(length as u8);
out.push(0);
debug_assert!(
(1..=256).contains(&self.max_bus_encryption_sector_extents),
"Maximum Number of Bus-Encryption Sector Extents must be 1..=256"
);
let wire_max = if self.max_bus_encryption_sector_extents == 256 {
0u8
} else {
self.max_bus_encryption_sector_extents as u8
};
out.push(wire_max);
for extent in &self.bus_encryption_sector_extents {
out.extend_from_slice(&[0u8; 8]);
out.push((extent.start_lba >> 24) as u8);
out.push((extent.start_lba >> 16) as u8);
out.push((extent.start_lba >> 8) as u8);
out.push(extent.start_lba as u8);
out.push((extent.lba_count >> 24) as u8);
out.push((extent.lba_count >> 16) as u8);
out.push((extent.lba_count >> 8) as u8);
out.push(extent.lba_count as u8);
}
Ok(ScsiResponse::good(out))
}
other => Err(AacsError::InvalidValue {
what: "MockDrive READ_DISC_STRUCTURE Format",
value: other as u64,
}),
}
}
SEND_DISC_STRUCTURE_OPCODE => {
let sds = SendDiscStructure::parse_cdb(cdb)?;
if direction != DataDirection::ToDevice {
return Err(AacsError::InvalidValue {
what: "MockDrive SEND_DISC_STRUCTURE data direction",
value: 0,
});
}
match sds.format {
FORMAT_AACS_WRITE_DATA_KEY => {
let wire_kwd = parse_send_disc_structure_write_data_key(data_out)?;
let kwd_plain = match &self.auth {
Some(a) if a.bus_key.is_some() => {
crate::aes::aes_128_ecb_decrypt(&a.bus_key.unwrap(), &wire_kwd)
}
Some(_) => {
return Err(AacsError::InvalidValue {
what: "SEND_DISC_STRUCTURE Format 0x84 without Bus Key",
value: 0,
});
}
None => wire_kwd,
};
self.write_data_key = kwd_plain;
self.last_write_data_key_sent = Some(wire_kwd);
Ok(ScsiResponse::good(Vec::new()))
}
other => Err(AacsError::InvalidValue {
what: "MockDrive SEND_DISC_STRUCTURE Format",
value: other as u64,
}),
}
}
other => Err(AacsError::InvalidValue {
what: "MockDrive unsupported opcode",
value: other as u64,
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn report_key_cdb_layout_matches_mmc6_table_513() {
let rk = ReportKey::aacs_drive_cert_challenge(2);
let cdb = rk.cdb();
assert_eq!(cdb[0], 0xA4, "opcode must be 0xA4");
assert_eq!(cdb[7], 0x02, "Key Class AACS");
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x74);
assert_eq!(cdb[10], 0x81);
assert_eq!(cdb[11], 0x00, "default Control byte");
let parsed = ReportKey::parse_cdb(&cdb).unwrap();
assert_eq!(parsed, rk);
}
#[test]
fn send_key_cdb_layout_matches_mmc6_table_599() {
let sk = SendKey::aacs_host_cert_challenge(3);
let cdb = sk.cdb();
assert_eq!(cdb[0], 0xA3);
assert_eq!(cdb[7], 0x02);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x74);
assert_eq!(cdb[10], 0xC1);
let parsed = SendKey::parse_cdb(&cdb).unwrap();
assert_eq!(parsed, sk);
}
#[test]
fn read_disc_structure_cdb_layout_matches_mmc6_table_381() {
let rds = ReadDiscStructure::aacs_volume_id(1);
let cdb = rds.cdb();
assert_eq!(cdb[0], 0xAD);
assert_eq!(cdb[1] & 0x0F, 0x01);
assert_eq!(cdb[7], 0x80);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x24);
assert_eq!(cdb[10], 0x40);
let parsed = ReadDiscStructure::parse_cdb(&cdb).unwrap();
assert_eq!(parsed, rds);
}
#[test]
fn rejects_wrong_opcode_in_parse_cdb() {
let mut cdb = [0u8; MMC_CDB_LEN];
cdb[0] = 0xFF;
assert!(ReportKey::parse_cdb(&cdb).is_err());
assert!(SendKey::parse_cdb(&cdb).is_err());
assert!(ReadDiscStructure::parse_cdb(&cdb).is_err());
}
#[test]
fn agid_field_packing_round_trip() {
for agid in 0..=3u8 {
let rk = ReportKey {
key_class: KEY_CLASS_AACS,
key_format: KF_REPORT_AACS_DRIVE_KEY,
agid,
lba_or_starting_offset: 0,
block_count_function: 0,
allocation_length: 84,
control: 0,
};
let cdb = rk.cdb();
assert_eq!(cdb[10] >> 6, agid);
let parsed = ReportKey::parse_cdb(&cdb).unwrap();
assert_eq!(parsed.agid, agid);
}
}
#[test]
fn media_serial_cdb_uses_format_0x81() {
let rds = ReadDiscStructure::aacs_media_serial(2);
let cdb = rds.cdb();
assert_eq!(cdb[0], READ_DISC_STRUCTURE_OPCODE);
assert_eq!(cdb[7], FORMAT_AACS_MEDIA_SERIAL);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x24);
assert_eq!(cdb[10] >> 6, 2);
}
#[test]
fn media_id_cdb_uses_format_0x82() {
let rds = ReadDiscStructure::aacs_media_id(3);
let cdb = rds.cdb();
assert_eq!(cdb[0], READ_DISC_STRUCTURE_OPCODE);
assert_eq!(cdb[7], FORMAT_AACS_MEDIA_ID);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x24);
assert_eq!(cdb[10] >> 6, 3);
}
#[test]
fn media_serial_response_parser_round_trip() {
let pmsn = [0xAA; VOLUME_ID_LEN];
let mac = [0x55; ID_MAC_LEN];
let mut wire = Vec::with_capacity(36);
wire.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
wire.extend_from_slice(&pmsn);
wire.extend_from_slice(&mac);
let parsed = parse_media_serial_response(&wire).unwrap();
assert_eq!(parsed.pmsn, pmsn);
assert_eq!(parsed.mac, mac);
}
#[test]
fn media_id_response_parser_round_trip() {
let mid = [0x33; VOLUME_ID_LEN];
let mac = [0xCC; ID_MAC_LEN];
let mut wire = Vec::with_capacity(36);
wire.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
wire.extend_from_slice(&mid);
wire.extend_from_slice(&mac);
let parsed = parse_media_id_response(&wire).unwrap();
assert_eq!(parsed.media_id, mid);
assert_eq!(parsed.mac, mac);
}
#[test]
fn media_serial_parser_rejects_wrong_length_field() {
let mut wire = vec![0x00, 0x10, 0x00, 0x00];
wire.resize(36, 0);
assert!(parse_media_serial_response(&wire).is_err());
}
#[test]
fn media_id_parser_rejects_truncated_payload() {
let wire = [0x00, 0x22, 0x00, 0x00, 0xAA, 0xBB];
assert!(parse_media_id_response(&wire).is_err());
}
#[test]
fn mkb_pack_response_parser_round_trip() {
let pack_data: Vec<u8> = (0..32u8).collect();
let total_packs = 5u8;
let length: u16 = (2 + pack_data.len()) as u16;
let mut wire = vec![
(length >> 8) as u8,
(length & 0xFF) as u8,
0x00, total_packs,
];
wire.extend_from_slice(&pack_data);
let parsed = parse_mkb_pack_response(&wire).unwrap();
assert_eq!(parsed.total_packs, total_packs);
assert_eq!(parsed.pack_data, pack_data);
}
#[test]
fn mkb_pack_parser_rejects_truncated_payload() {
let wire = [0x00, 0x66, 0x00, 0x01];
assert!(parse_mkb_pack_response(&wire).is_err());
}
#[test]
fn binding_nonce_gen_cdb_encodes_lba_extent_per_4_14_2() {
let rk = ReportKey::aacs_binding_nonce_gen(2, 0x0102_0304, 0x40);
let cdb = rk.cdb();
assert_eq!(cdb[0], REPORT_KEY_OPCODE);
assert_eq!(cdb[2], 0x01);
assert_eq!(cdb[3], 0x02);
assert_eq!(cdb[4], 0x03);
assert_eq!(cdb[5], 0x04);
assert_eq!(cdb[6], 0x40);
assert_eq!(cdb[7], KEY_CLASS_AACS);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x24);
assert_eq!(cdb[10], 0xA0);
let parsed = ReportKey::parse_cdb(&cdb).unwrap();
assert_eq!(parsed, rk);
assert_eq!(parsed.key_format, KF_REPORT_AACS_BINDING_NONCE_GEN);
assert_eq!(parsed.lba_or_starting_offset, 0x0102_0304);
assert_eq!(parsed.block_count_function, 0x40);
}
#[test]
fn binding_nonce_read_cdb_uses_key_format_0x21() {
let rk = ReportKey::aacs_binding_nonce_read(3, 0, 1);
let cdb = rk.cdb();
assert_eq!(cdb[7], KEY_CLASS_AACS);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x24);
assert_eq!(cdb[10], 0xE1);
assert_eq!(cdb[6], 0x01);
let parsed = ReportKey::parse_cdb(&cdb).unwrap();
assert_eq!(parsed.key_format, KF_REPORT_AACS_BINDING_NONCE_READ);
}
#[test]
fn binding_nonce_response_parser_round_trip() {
let nonce = [0xA5; BINDING_NONCE_LEN];
let mac = [0x5A; BINDING_NONCE_MAC_LEN];
let mut wire = Vec::with_capacity(36);
wire.extend_from_slice(&[0x00, 0x22, 0x00, 0x00]);
wire.extend_from_slice(&nonce);
wire.extend_from_slice(&mac);
let parsed = parse_report_key_binding_nonce(&wire).unwrap();
assert_eq!(parsed.binding_nonce, nonce);
assert_eq!(parsed.mac, mac);
}
#[test]
fn binding_nonce_parser_rejects_wrong_length_field() {
let mut wire = vec![0x00, 0x10, 0x00, 0x00];
wire.resize(36, 0);
assert!(parse_report_key_binding_nonce(&wire).is_err());
}
#[test]
fn binding_nonce_parser_rejects_truncated_payload() {
let wire = [0x00, 0x22, 0x00, 0x00, 0xAA, 0xBB];
assert!(parse_report_key_binding_nonce(&wire).is_err());
}
#[test]
fn data_keys_cdb_uses_format_0x84() {
let rds = ReadDiscStructure::aacs_data_keys(2);
let cdb = rds.cdb();
assert_eq!(cdb[0], READ_DISC_STRUCTURE_OPCODE);
assert_eq!(cdb[1] & 0x0F, MEDIA_TYPE_BD);
assert_eq!(cdb[7], FORMAT_AACS_DATA_KEYS);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x24);
assert_eq!(cdb[10] >> 6, 2);
assert_eq!(cdb[2..6], [0u8; 4]);
assert_eq!(cdb[6], 0);
let parsed = ReadDiscStructure::parse_cdb(&cdb).unwrap();
assert_eq!(parsed, rds);
}
#[test]
fn data_keys_response_parser_round_trip() {
let mut wire = vec![0x00, 0x22, 0x00, 0x00];
let krd = [0xA5u8; DATA_KEY_LEN];
let kwd = [0x5Au8; DATA_KEY_LEN];
wire.extend_from_slice(&krd);
wire.extend_from_slice(&kwd);
assert_eq!(wire.len(), 36);
let parsed = parse_data_keys_response(&wire).unwrap();
assert_eq!(parsed.read_data_key_encrypted, krd);
assert_eq!(parsed.write_data_key_encrypted, kwd);
}
#[test]
fn data_keys_parser_rejects_wrong_length_field() {
let mut wire = vec![0x00, 0x20, 0x00, 0x00];
wire.resize(36, 0);
assert!(parse_data_keys_response(&wire).is_err());
}
#[test]
fn data_keys_parser_rejects_truncated_payload() {
let wire = [0x00, 0x22, 0x00, 0x00, 0xAA, 0xBB];
assert!(parse_data_keys_response(&wire).is_err());
}
#[test]
fn data_keys_response_decrypts_under_bus_key() {
let bus_key = [0x12u8; 16];
let krd_pt = [0x33u8; DATA_KEY_LEN];
let kwd_pt = [0x44u8; DATA_KEY_LEN];
let krd_enc = crate::aes::aes_128_ecb_encrypt(&bus_key, &krd_pt);
let kwd_enc = crate::aes::aes_128_ecb_encrypt(&bus_key, &kwd_pt);
let resp = DataKeysResponse {
read_data_key_encrypted: krd_enc,
write_data_key_encrypted: kwd_enc,
};
assert_eq!(resp.decrypt_read_data_key(&bus_key), krd_pt);
assert_eq!(resp.decrypt_write_data_key(&bus_key), kwd_pt);
}
#[test]
fn bus_encryption_sector_extents_cdb_uses_format_0x85() {
let rds = ReadDiscStructure::aacs_bus_encryption_sector_extents();
let cdb = rds.cdb();
assert_eq!(cdb[0], READ_DISC_STRUCTURE_OPCODE);
assert_eq!(cdb[1] & 0x0F, MEDIA_TYPE_BD);
assert_eq!(cdb[7], FORMAT_AACS_BUS_ENCRYPTION_SECTOR_EXTENTS);
assert_eq!(cdb[8], 0x10);
assert_eq!(cdb[9], 0x0C);
assert_eq!(cdb[10], 0x00);
assert_eq!(cdb[2..6], [0u8; 4]);
assert_eq!(cdb[6], 0);
let parsed = ReadDiscStructure::parse_cdb(&cdb).unwrap();
assert_eq!(parsed, rds);
}
#[test]
fn bus_encryption_sector_extents_empty_table_round_trip() {
let wire = [0x00, 0x02, 0x00, 0x40];
let parsed = parse_bus_encryption_sector_extents_response(&wire).unwrap();
assert_eq!(parsed.maximum, 0x40);
assert!(parsed.extents.is_empty());
}
#[test]
fn bus_encryption_sector_extents_two_extents_round_trip() {
let mut wire = vec![0x00, 0x22, 0x00, 0x04];
wire.extend_from_slice(&[0u8; 8]);
wire.extend_from_slice(&[0x00, 0x00, 0x01, 0x00]);
wire.extend_from_slice(&[0x00, 0x00, 0x00, 0x80]);
wire.extend_from_slice(&[0u8; 8]);
wire.extend_from_slice(&[0x00, 0x00, 0x10, 0x00]);
wire.extend_from_slice(&[0x00, 0x00, 0x00, 0x40]);
assert_eq!(wire.len(), 36);
let parsed = parse_bus_encryption_sector_extents_response(&wire).unwrap();
assert_eq!(parsed.maximum, 4);
assert_eq!(parsed.extents.len(), 2);
assert_eq!(parsed.extents[0].start_lba, 0x0000_0100);
assert_eq!(parsed.extents[0].lba_count, 0x0000_0080);
assert_eq!(parsed.extents[1].start_lba, 0x0000_1000);
assert_eq!(parsed.extents[1].lba_count, 0x0000_0040);
}
#[test]
fn bus_encryption_sector_extents_maximum_zero_decodes_as_256() {
let wire = [0x00, 0x02, 0x00, 0x00];
let parsed = parse_bus_encryption_sector_extents_response(&wire).unwrap();
assert_eq!(parsed.maximum, 256);
assert!(parsed.extents.is_empty());
}
#[test]
fn bus_encryption_sector_extents_parser_rejects_misaligned_stride() {
let mut wire = vec![0x00, 0x11, 0x00, 0x01];
wire.resize(2 + 17, 0);
assert!(parse_bus_encryption_sector_extents_response(&wire).is_err());
}
#[test]
fn bus_encryption_sector_extents_parser_rejects_truncated_payload() {
let wire = [0x00, 0x22, 0x00, 0x01, 0xAA, 0xBB];
assert!(parse_bus_encryption_sector_extents_response(&wire).is_err());
}
#[test]
fn bus_encryption_sector_extents_parser_rejects_truncated_header() {
let wire = [0x00];
assert!(parse_bus_encryption_sector_extents_response(&wire).is_err());
}
#[test]
fn mock_drive_bus_encryption_sector_extents_round_trip() {
let mut drive = MockDrive::with_test_fixture();
let cdb = ReadDiscStructure::aacs_bus_encryption_sector_extents().cdb();
let response = drive
.execute(&cdb, DataDirection::FromDevice, &[], 4108)
.unwrap();
assert_eq!(response.status, 0x00);
assert_eq!(response.data[0], 0x00);
assert_eq!(response.data[1], 0x22);
assert_eq!(response.data[2], 0x00);
assert_eq!(response.data[3], 0x04);
let parsed = parse_bus_encryption_sector_extents_response(&response.data).unwrap();
assert_eq!(parsed.maximum, 4);
assert_eq!(parsed.extents, drive.bus_encryption_sector_extents);
}
#[test]
fn mock_drive_bus_encryption_sector_extents_empty_table_encodes_length_2() {
let mut drive = MockDrive::with_test_fixture();
drive.bus_encryption_sector_extents.clear();
drive.max_bus_encryption_sector_extents = 7;
let cdb = ReadDiscStructure::aacs_bus_encryption_sector_extents().cdb();
let response = drive
.execute(&cdb, DataDirection::FromDevice, &[], 4108)
.unwrap();
assert_eq!(response.data[0], 0x00);
assert_eq!(response.data[1], 0x02);
assert_eq!(response.data[2], 0x00);
assert_eq!(response.data[3], 0x07);
assert_eq!(response.data.len(), 4);
let parsed = parse_bus_encryption_sector_extents_response(&response.data).unwrap();
assert_eq!(parsed.maximum, 7);
assert!(parsed.extents.is_empty());
}
#[test]
fn mock_drive_bus_encryption_sector_extents_max_256_encodes_as_zero() {
let mut drive = MockDrive::with_test_fixture();
drive.bus_encryption_sector_extents.clear();
drive.max_bus_encryption_sector_extents = 256;
let cdb = ReadDiscStructure::aacs_bus_encryption_sector_extents().cdb();
let response = drive
.execute(&cdb, DataDirection::FromDevice, &[], 4108)
.unwrap();
assert_eq!(response.data[3], 0x00);
let parsed = parse_bus_encryption_sector_extents_response(&response.data).unwrap();
assert_eq!(parsed.maximum, 256);
}
#[test]
fn mock_drive_data_keys_format_static_mode_returns_plaintext_bytes() {
let mut drive = MockDrive::with_test_fixture();
let rds = ReadDiscStructure::aacs_data_keys(1);
let cdb = rds.cdb();
let resp = drive
.execute(&cdb, DataDirection::FromDevice, &[], 36)
.unwrap();
let parsed = parse_data_keys_response(&resp.data).unwrap();
assert_eq!(parsed.read_data_key_encrypted, drive.read_data_key);
assert_eq!(parsed.write_data_key_encrypted, drive.write_data_key);
assert!(drive.last_data_keys_read);
}
#[test]
fn send_disc_structure_cdb_layout_matches_table_4_26() {
let sds = SendDiscStructure::aacs_write_data_key(2);
let cdb = sds.cdb();
assert_eq!(cdb[0], 0xBF);
assert_eq!(cdb[1] & 0x0F, MEDIA_TYPE_BD);
assert_eq!(cdb[2..7], [0x00, 0x00, 0x00, 0x00, 0x00]);
assert_eq!(cdb[7], 0x84);
assert_eq!(cdb[8], 0x00);
assert_eq!(cdb[9], 0x14);
assert_eq!(cdb[10], 0x80);
assert_eq!(cdb[11], 0x00);
let parsed = SendDiscStructure::parse_cdb(&cdb).unwrap();
assert_eq!(parsed, sds);
}
#[test]
fn send_disc_structure_parse_cdb_rejects_wrong_opcode() {
let mut cdb = [0u8; MMC_CDB_LEN];
cdb[0] = 0xAD;
assert!(SendDiscStructure::parse_cdb(&cdb).is_err());
}
#[test]
fn write_data_key_parameter_list_round_trip_matches_table_4_28() {
let mut kwd = [0u8; DATA_KEY_LEN];
for (i, b) in kwd.iter_mut().enumerate() {
*b = 0xE0 | (i as u8);
}
let wire = build_send_disc_structure_write_data_key(&kwd);
assert_eq!(wire.len(), 20);
assert_eq!(wire[..4], [0x00, 0x12, 0x00, 0x00]);
assert_eq!(wire[4..], kwd);
assert_eq!(
parse_send_disc_structure_write_data_key(&wire).unwrap(),
kwd
);
}
#[test]
fn write_data_key_parameter_list_rejects_wrong_length_field() {
let mut wire = vec![0x00, 0x22, 0x00, 0x00];
wire.resize(20, 0);
assert!(parse_send_disc_structure_write_data_key(&wire).is_err());
}
#[test]
fn write_data_key_parameter_list_rejects_truncated_payload() {
let wire = [0x00, 0x12, 0x00, 0x00, 0xAA, 0xBB];
assert!(parse_send_disc_structure_write_data_key(&wire).is_err());
}
#[test]
fn mock_drive_send_write_data_key_static_mode_stores_wire_bytes() {
let mut drive = MockDrive::with_test_fixture();
let old_krd = drive.read_data_key;
let mut new_kwd = [0u8; DATA_KEY_LEN];
for (i, b) in new_kwd.iter_mut().enumerate() {
*b = 0xF0 | (i as u8);
}
let cdb = SendDiscStructure::aacs_write_data_key(1).cdb();
let wire = build_send_disc_structure_write_data_key(&new_kwd);
let resp = drive
.execute(&cdb, DataDirection::ToDevice, &wire, 0)
.unwrap();
assert_eq!(resp.status, 0x00);
assert!(resp.data.is_empty());
assert_eq!(drive.write_data_key, new_kwd);
assert_eq!(drive.last_write_data_key_sent, Some(new_kwd));
assert_eq!(drive.read_data_key, old_krd);
}
#[test]
fn mock_drive_send_disc_structure_rejects_unknown_format() {
let mut drive = MockDrive::with_test_fixture();
let mut sds = SendDiscStructure::aacs_write_data_key(0);
sds.format = 0x87;
let wire = build_send_disc_structure_write_data_key(&[0u8; DATA_KEY_LEN]);
assert!(drive
.execute(&sds.cdb(), DataDirection::ToDevice, &wire, 0)
.is_err());
}
#[test]
fn mock_drive_send_disc_structure_rejects_wrong_direction() {
let mut drive = MockDrive::with_test_fixture();
let before = drive.write_data_key;
let cdb = SendDiscStructure::aacs_write_data_key(0).cdb();
let wire = build_send_disc_structure_write_data_key(&[0x55u8; DATA_KEY_LEN]);
assert!(drive
.execute(&cdb, DataDirection::FromDevice, &wire, 0)
.is_err());
assert_eq!(drive.write_data_key, before);
assert_eq!(drive.last_write_data_key_sent, None);
}
}