Skip to content

v0.1.1

Choose a tag to compare

@MagicalTux MagicalTux released this 23 May 06:24
· 26 commits to master since this release
cbd8f63

Other

  • Phase C: Drive-Host AKE + ECDSA-secp160r1 + Bus Key (AACS Common 0.953 §4.3)
  • Phase B — SCSI MMC drive command layer (REPORT_KEY / SEND_KEY / READ_DISC_STRUCTURE)
  • parse |-leader DK / PK / HC / DC / DISCID-scoped records (Phase A)
  • disc_id is SHA-1(Unit_Key_RO.inf), not SHA-1(Volume_ID)
  • disc_id_for_volume_id — SHA-1(volume_id) for KEYDB.cfg lookup
  • fmt + tests: align integration tests with permissive parse
  • tolerate sector-padding zeros after the End-of-MKB record

Added — Phase C: Drive-Host Authentication & Key Exchange (AKE)

New ec, ecdsa, and ake modules implementing the AACS Common
Final 0.953 §4.3 "AACS Drive Authentication Algorithm" (Figure 4-9)
end-to-end on top of the Phase B MMC layer. All cryptography is
clean-room from the spec's published math (Table 2-1 curve parameters,
§2.3 ECDSA, §2.1.5 SHA-1, §2.1.6 CMAC); no external crypto-library
source (RustCrypto / OpenSSL / libaacs / …) was consulted. The
openssl CLI was used only as an opaque test-vector oracle, and the
ECDSA path is additionally cross-checked bit-exact against an
independent Python big-int reference.

  • ec module — a 160-bit big-integer (U160, five u32 limbs)
    and short-Weierstrass curve over GF(p) for the AACS curve
    (Table 2-1: a = -3, prime p, base point G, order n). Affine
    add/double/is_on_curve plus a Jacobian-coordinate
    mul_scalar (single final inversion). 40-byte EC-point encoding
    x(20) || y(20).
  • ecdsa moduleAACS_Sign / AACS_Verify (X9.62 / FIPS
    186-2) with a clean-room FIPS 180-2 SHA-1 digest; 40-byte r || s
    signatures. A deterministic k helper (AES-H based, not RFC 6979)
    makes the synthetic test handshake reproducible. SHA-1 validated
    against the FIPS 180-2 abc / empty / two-block vectors; the full
    sign/verify against an independent Python reference vector.
  • aes::aes_128_cmac — AES-128-CMAC (NIST SP 800-38B), validated
    against the SP 800-38B Appendix D.1 example MACs (empty / 1-block /
    partial / full-message).
  • ake module:
    • Certificate::parse + verify_signature for the 92-byte Drive
      (Table 4-1) / Host (Table 4-2) certificates
      (Type Flags Length ID Reserved PubKey(40) Sig(40)), with the
      Cert_sig = bytes 52..91 signed over Cert = bytes 0..51.
    • build_signed_certificate to mint synthetic LA-signed certs.
    • host_authenticate — the §4.3 Host state machine driving any
      DriveCommand transport through AGID → Host Cert Challenge →
      Drive Cert Challenge → Drive Key → Host Key → Bus Key.
    • DriveAuthState — the §4.3 drive side (verify Host Cert + Hsig,
      sign Dsig over Hn || Dv, derive Dk·Hv), wired into the
      Phase B MockDrive via its new auth field.
    • bus_key_from_pointBK = [x-coordinate of shared point] lsb_128 (§4.3 steps 28/29).
    • read_verified_volume_id — §4.4 Volume ID transfer with
      Dm = CMAC(BK, Volume_ID) host-side verification.
  • New AacsError variants:
    DriveCertSignatureInvalid, HostCertSignatureInvalid,
    DriveSignatureInvalid, HostSignatureInvalid, VolumeIdMacInvalid.
  • New integration suite tests/synth_phasec_ake.rs (5 tests): full
    synthetic-cert AKE round-trip with matching Host/Drive Bus Keys,
    §4.4 Volume ID verification, and rejection of a wrong-LA Drive cert,
    a wrong-LA Host cert, and a tampered Volume ID MAC.

Note on the Bus Key KDF. The task brief mentioned a possible
"AES-G / SHA-1 KDF" for the Bus Key; AACS Common Final 0.953 §4.3
steps 28/29 in fact define the Bus Key directly as the least
significant 128 bits of the x-coordinate of the shared ECDH point
(Dk·Hv / Hk·Dv) — no AES-G/SHA-1 post-derivation. This module
implements per the spec; the §4.4+ ID transfers then key AES-CMAC
under that Bus Key.

Added — Phase B: SCSI MMC drive-command wire layer

New mmc module implementing the byte-level encoding/parsing for the
three SCSI MMC commands an AACS host needs to converse with a Licensed
Drive. Wire formats are taken from the publicly-hosted T10 working
drafts now staged at docs/container/aacs/mmc/ (MMC-6 r02g, SPC-3 r23)
cross-referenced against AACS Common Final 0.953 §4.1–§4.14. No
external library source (libaacs / libbluray / etc.) was consulted.

  • Typed CDB constructors, each emitting a 12-byte [u8; 12] block
    per MMC-6 Tables 381 / 513 / 599:
    • ReportKey::{aacs_agid, aacs_drive_cert_challenge, aacs_drive_key, aacs_drive_cert, aacs_invalidate_agid}.
    • SendKey::{aacs_host_cert_challenge, aacs_host_key, aacs_invalidate_agid}.
    • ReadDiscStructure::{aacs_volume_id, aacs_media_serial, aacs_media_key_block_pack}.
    • parse_cdb() inverse for each, used by the synthetic mock drive
      and by tests.
  • AACS sub-payload codecs for the AKE round-trip:
    • parse_report_key_agid / parse_report_key_drive_cert_chal /
      parse_report_key_drive_key / parse_report_key_drive_cert
      drive-to-host responses per AACS Common Tables 4-7, 4-8, 4-9 and
      MMC-6 Table 531.
    • parse_volume_id_response — 36-byte READ DISC STRUCTURE
      Format 0x80 reply per AACS Common Table 4-15
      ([u16=0x0022][rsvd:u16][Volume ID:16][MAC:16]).
    • build_send_key_host_cert_chal / build_send_key_host_key and
      their parse_* inverses — host-to-drive parameter lists per
      AACS Common Tables 4-24, 4-25 (Hn || Cert_h and Hv || Hsig
      respectively).
  • DriveCommand trait abstracting the SCSI pass-through surface
    so platform-specific back-ends (macOS IOSCSITaskDeviceInterface,
    Linux SG_IO, Windows IOCTL_SCSI_PASS_THROUGH_DIRECT) can be
    written against a single contract once Phase C lands. Carries a
    DataDirection enum + ScsiResponse { status, data }.
  • MockDrive in-process fixture implementing DriveCommand,
    populated with a deterministic synthetic Drive Certificate /
    Volume ID / nonces so tests can assert exact byte layouts.

Public re-exports added to lib.rs (mmc module + the typed
structures and parsers).

Documentation gaps surfaced

  • The workspace docs/container/aacs/mmc/README.md "AACS commands at
    a glance" section confuses two different command surfaces: it lists
    AACS Volume ID under "REPORT KEY Key Class=0x02 Key Format 0x12",
    but per MMC-6 Table 525 the REPORT KEY Key Class 0x02 Key Format
    table only defines 0x00 / 0x01 / 0x02 / 0x20 / 0x21 / 0x38 / 0x3F.
    The Volume Identifier in fact ships via READ_DISC_STRUCTURE
    Format Code 0x80 (MMC-6 Table 384 / AACS Common Table 4-15). This
    Phase B implementation follows the spec tables; the README would
    benefit from a follow-up edit clarifying which list belongs to
    which command.

Tests

  • 5 new mmc unit tests pinning the CDB byte layouts (opcode, Key
    Class, Allocation/Parameter-List Length packing, AGID bit packing).
  • 13 new tests/synth_phaseb_mmc.rs integration tests round-tripping
    the AGID / Drive Cert Challenge / Drive Key / Drive Cert /
    Host Cert Challenge / Host Key / Volume ID / Invalidate-AGID flows
    through MockDrive.

Out of scope (deferred to Phase C/D)

  • ECDSA-secp160r1 sign/verify primitives needed for the cryptographic
    half of the AKE (Common spec §4.3 steps 9, 16, 18, 25, 27).
  • AES_G / SHA-1-based Bus Key derivation from Hk*Dv / Dk*Hv.
  • Actual hardware transport: macOS IOKit, Linux SG_IO, Windows IOCTL
    back-ends implementing DriveCommand against a real /dev/sr0 /
    IOSCSITaskDeviceInterface / IOCTL_SCSI_PASS_THROUGH_DIRECT.
  • Phase D: wiring the AKE + Bus-Key-protected reads into
    oxideav-bluray for unencrypted-at-rest disc playback.

Added — Phase A: KEYDB.cfg |-leader header records

KeyDb::parse now recognises the |-leader record form documented in
docs/container/aacs/keydb-cfg-format.md, in addition to the
pre-existing per-disc <DISC_ID>=V <VUK> lines. New record types:

  • | DK | Device Key (DEVICE_KEY + DEVICE_NODE + KEY_UV +
    KEY_U_MASK_SHIFT) — pins a key into the AACS Subset-Difference
    tree. Surfaced via KeyDb::device_keys().
  • | PK | Processing Key (16-byte AES-128) — the SD-tree walk output
    for a specific MKB. Surfaced via KeyDb::processing_keys().
  • | HC | Host Certificate + private key — 20-byte ECDSA-secp160r1
    scalar + variable-length signed cert. Parser validates the embedded
    length field (cert offset 2..4, big-endian) against the actual
    buffer length per AACS Common Final 0.953 §A.1; exposes host_id(),
    cert_type(), declared_length(). Surfaced via
    KeyDb::host_certs().
  • | DC | Drive Certificate + private key (drive side of the
    Drive-Host auth). Surfaced via KeyDb::drive_certs().
  • | DISCID | introduces a per-disc record-set scope; subsequent
    | VID |, | VUK |, | MEK |, | TK |, | KCD | rows attach to
    it. Surfaced as DiscRecords via KeyDb::disc_records() /
    KeyDb::disc_record(&id).

KeyDb::vuk_for_disc now also looks through the DISCID-scoped
record map, so both legacy and |-leader files yield the same lookup
behaviour.

New AacsError::HeaderParseError(String) variant for malformed
|-leader lines.

Legacy per-disc lines continue to parse byte-identically (a dedicated
legacy_only_file_unchanged unit test pins this).