Commit Graph
133 Commits
Author SHA1 Message Date
Matthew Jackson 6b6c3be1dc docs(changelog): 1.7.1 release notes (LibreDrive unlock fix) v1.7.1-rc3 2026-09-13 20:01:00 -07:00
Matthew Jackson 422147f6ad ci: bump actions/checkout v4->v7 in identity-guard (dependabot) v1.7.1-rc2 2026-09-13 18:30:58 -07:00
Matthew Jackson 72d5b8fb3e ld: trim profile-match comments to satisfy comment-guard caps 2026-09-13 18:24:32 -07:00
Matthew Jackson 407c7f59e3 fix(ld): bind drives whose reported product_id differs from the generic catalog id
find_by_drive_id gated the product-id-blind four-field fallback behind an empty
product_id in 1.7.0, so a drive reporting a specific product_id (e.g. an LG BU40N
reporting "BD-RE BU40N") no longer matched its catalog entry (generic "BD-RE"),
silently skipping the LibreDrive firmware unlocker — the rip fell through to the
AACS host-cert path (LD: no, AACS: yes).

Run the four-field fallback (vendor/revision/vendor_specific/firmware_date)
unconditionally but ambiguity-guarded: bind a UNIQUE catalog match regardless of
the reported product_id (the real-drive case), and require the exact product_id
only when catalogued siblings genuinely share the four-field identity — preserving
the anti-mis-bind protection the 1.7.0 gate intended. Regression test added.

Bump to 1.7.1.
v1.7.1-rc1
2026-09-13 18:09:50 -07:00
Matthew Jackson 199a3ffee6 docs(changelog): note beta Renesas + freemkv-firmware unlock support in 1.7.0
Record the preliminary Renesas / freemkv-firmware unlock routes under 1.7.0
Added, and drop the LibreDrive-microcode NOTICE line from the changelog.
v1.7.0 v1.7.0-rc8
2026-09-05 18:14:36 -07:00
Matthew Jackson aa70a3707d test: pin exact CDB bytes for the vendor-unlock paths v1.7.0-rc7 2026-09-05 12:35:10 -07:00
Matthew Jackson cb1235cca4 surface the dropped UHD read-data-key (bus-key) failure 2026-09-05 12:35:10 -07:00
Matthew Jackson 150e91b0ab redact HostCert Debug so AACS host private keys never reach a log 2026-09-05 12:35:10 -07:00
Matthew Jackson 3989100820 freemkv: Identity knock must use the fixed 64-byte data-in allocation
identify() requested a 32-byte data-in allocation while the firmware commits
KNOCK_RESP_LEN (64) bytes on every subfn. The short allocation desyncs the first
command's data-in phase and wedges the drive, so the later bare 0xAD VID read
aborts (CHECK CONDITION) — the "Raw Read GOOD but no VID" failure that left the
keyserver with vid=none. Request KNOCK_RESP_LEN like every other knock; update the
CDB-shape tests. Also sync Cargo.lock to the 1.7.0 manifest.
v1.7.0-rc4
2026-09-03 07:41:43 -07:00
Matthew Jackson c6d6a5a247 unlock: derive Default for Ld/Renesas unlockers (clippy new_without_default)
Newer stable clippy (CI) flags new() without a Default on the unit-struct
unlockers; the local pinned clippy did not. Unit structs, so derive it.
v1.7.0-rc3
2026-09-02 18:01:48 -07:00
Matthew Jackson 09dd8ef14c unlock: unify the Unlocker contract across all routes
Every unlocker now implements one method — unlock(&self, scsi, ctx) ->
Result<Option<Unlocked>> — with identical workflow and error handling,
differing only in CDBs/checks. Drops the unlock_features/unlock_bus split,
per-unlocker result state, and the drive_unlocked flag ("unlocked" is just
Some). Only a dead bus is Err; everything else falls through as Ok(None).

- VID read is best-effort everywhere via the shared crate::vid reader: a
  VID miss no longer discards an unlock that already removed the bus — the
  bug that dropped a freemkv-unlocked drive to the cert path and sent the
  keyserver vid=none.
- AACS: host certs injected at construction (AacsUnlocker::new(certs)), not
  passed per-call; a successful cert AKE is a real unlock.
- Renesas/Pioneer: the vendor open enables raw reads (LibreDrive parity),
  then reads the VID via the same 0xAD reader — no host cert, matching the
  firmware routes.
- Rename the MT1959 unlocker to LD (matches the ld/ module); the MediaTek
  MT1959 platform driver keeps its name.
- Consumer assembles its own dispatch list from the exported types; no
  central factory. Adds a uniform dead-bus contract test over all unlockers.
2026-09-02 17:53:17 -07:00
Matthew Jackson 417d4598ca ci: identity-guard — block unrelated-corp / non-canonical committer emails 2026-09-02 14:46:22 -07:00
Matthew Jackson 74936ad892 release: 1.7.0
Fixed
- ld/profile: find_by_drive_id no longer mis-binds a drive whose reported
  product_id is real but uncatalogued to a sibling model's profile (and its wrong
  LibreDrive microcode); the product-id-blind loose match is gated on an empty id.

Docs / CI
- Third-party NOTICE for the LibreDrive microcode bundled in profiles.json;
  cargo-deny gate (#15) and audience-based comment-guard (#17).
v1.7.0-rc2 v1.7.0-rc1
2026-09-02 12:27:57 -07:00
Matthew Jackson 9b9f142313 v1.6.14: bump version (unified release) v1.6.14 v1.6.14-rc1 2026-08-31 23:04:22 +00:00
Matthew Jackson 7eee823db7 docs(changelog): 1.6.14 entry v1.6.13-rc4 2026-08-31 15:32:40 -07:00
Matthew Jackson d8f1d2f3a7 refactor(renesas): move renesis.rs into src/renesas/ and normalize spelling
Matches the ld/ platform-folder convention (MediaTek lives in src/ld/); the
module/struct spelling is normalized renesis->renesas to match the drives it
detects. Pure move + identifier rename, no behavior change.
2026-08-31 12:54:30 -07:00
Matthew Jackson 56688764ea chore(msrv): floor to 1.88, pin CI on the floor, allow uninlined_format_args
Declared rust-version and the ci.yml/qa.yml toolchain pins both sit at 1.88 (the
edition-2024 floor once zip 8 and let-chains are accounted for), so CI builds on
exactly the MSRV: an accidental bump to a newer toolchain fails CI by design and
moving the floor takes a deliberate pin edit. The uninlined_format_args allow
reconciles 1.88's clippy with the allow-by-default it became in 1.97+.
v1.6.13-rc3 v1.6.13-rc2
2026-08-31 11:06:35 -07:00
Matthew Jackson e7107040bf chore: rustfmt the Renesas vendor CDB constants 2026-08-31 08:57:19 -07:00
Matthew Jackson b88feff027 freemkv-unlock: full_unlock orchestration + lean/raw VID + variant harness 2026-08-31 08:56:42 -07:00
Matthew Jackson 1016a294db renesas: Pioneer/Renesas vendor open sequence (read → knock → read) 2026-08-31 08:56:42 -07:00
dependabot[bot] 958b15f806 build(deps): bump codecov/codecov-action from 5 to 7
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5 to 7.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5...v7)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-30 21:52:46 -07:00
Matthew Jackson 508b533f78 Release 1.6.13: version align v1.6.13 v1.6.13-rc1 2026-08-27 21:22:11 -07:00
Matthew Jackson 052ab804e2 v1.6.12: bump version (unified release) v1.6.12 v1.6.12-rc1 v1.6.12-rc2 2026-08-27 22:57:25 +00:00
Matthew Jackson 0964097e78 changelog: 1.6.12 (unreleased) v1.6.11-rc2 2026-08-27 12:18:39 -07:00
Matthew Jackson 3b8bcc2164 v1.6.11: bump version (unified release) v1.6.11 v1.6.11-rc1 2026-08-25 23:16:54 -07:00
Matthew Jackson 7128fdf71f docs: neutralize external references and named techniques (opsec) v1.6.9-rc5 2026-08-25 22:18:31 -07:00
Matthew Jackson 9ad1eb9811 test: round-3 coverage on platform shim, ld/aacs error paths, css/mt1959 branches
Targets the files round-1/2 left out of scope: ld/platform/mod.rs (the
PlatformDriver default trait method, previously 0%), ld/error.rs Display
impl (now 100%), aacs/mod.rs Default + full unlock_bus success path
(exposes handshake::tests::DriveEmu/dummy_cert as pub(crate) for reuse),
css/mod.rs (DvdUnlocker::default, the disc-key-refusal non-fatal path,
drive-challenge/Key2-send failure branches), aacs/handshake.rs
(run_cert_handshake's per-cert backoff sleep + retry), and mt1959
mod.rs/variant_a.rs (MODE_B dispatch, a non-transport pass-1 probe
sense, WRITE_BUFFER's 24-bit length guard, variant_a's full upload+
verify+double-unlock success path).

Total coverage 95.65% -> 96.50% (regions), 95.33% -> 96.61% (lines).
2026-08-25 22:13:19 -07:00
Matthew Jackson 7ea1a4eea7 docs: 1.6.11 changelog 2026-08-25 22:08:48 -07:00
Matthew Jackson b3d1b99460 test: round-2 coverage on css bus-auth, mt1959 retry/probe, aacs handshake
Extends the fake ScsiTransport in css::mod to run the full CSS bus-auth
happy path (variant brute-force, drive-challenge/Key2 exchange, best-effort
disc-key REPORT KEY, DvdUnlocker::unlock_bus success) instead of only its
failure branches.

Covers mt1959's run_init retry loop (signature-mismatch abort, generic
do_unlock failure with firmware-reload fallback, firmware-reload transport
fault), run_probe's disc-type detection (capacity sense fallback, UHD vs BD
init address, pass-2 early-end on a rejected probe), and the PlatformDriver
guard clauses.

Covers aacs/handshake's EC math edge cases (mod_inv/ec_add/ec_double/ec_mul
defensive branches, off-curve pubkey rejection), the Debug redaction impls,
the AACS 1.0 type-0x01 cert-verify gate, the P-256 thin wrapper + short-cert
guard + step-6 bad-signature rejection, and drives run_cert_handshake to its
full Ok(CertHandshake) success path via an extended drive emulator.
2026-08-25 21:48:47 -07:00
Matthew Jackson 781b498e4e test: cover aacs/css error paths + aes block
Adds unit tests for aacs::error::Error and css::error::Error (code(),
scsi_sense/is_transport_failure branches, From<ScsiError> conversions),
a FIPS-197 known-answer test for aacs::aes_ecb_decrypt, and scripted
fake-transport tests covering the authenticate_with_agid / read_disc_key
error branches in css::mod. Raises line coverage 91.16% -> 92.59%.
2026-08-25 20:52:50 -07:00
Matthew Jackson bdd2129932 docs: standardize README badges (License, CI, codecov) v1.6.9-rc4 2026-08-25 20:32:32 -07:00
Matthew Jackson bb313c7ece ci(coverage): authenticate Codecov upload with org CODECOV_TOKEN 2026-08-25 19:33:00 -07:00
Matthew Jackson 985b400d70 ci(coverage): add --ignore-run-fail so instrumentation flake never blocks upload 2026-08-25 19:05:06 -07:00
Matthew Jackson dd73c5d3a1 ci: add non-required Codecov coverage job
Add an additive, non-required coverage job to CI that runs cargo
llvm-cov and uploads lcov to Codecov (tokenless, fail_ci_if_error
false). Mirrors the test job's environment and pinned 1.90.0 toolchain;
--locked is omitted to match this crate's gitignored-lockfile policy.
Adds codecov.yml (informational, comment off) and a README badge.
2026-08-25 18:54:57 -07:00
Matthew Jackson 9cfcb5830c lower MSRV to 1.90, trim two stray comments in aacs handshake
Bisected the true floor: 1.85 fails to compile (needs is_multiple_of,
stabilized 1.87, and let-chains, stabilized 1.88), 1.90 builds/clippy/tests
clean. Repin Cargo.toml rust-version and every CI dtolnay/rust-toolchain to
1.90.0, and note the change in CHANGELOG.md. Also removed a redundant
"// Hash the data" comment and a stale duplicate doc line on
generate_host_key_pair_p256 found during a pass over the crate's comments.
v1.6.9-rc3
2026-08-25 10:14:16 -07:00
dependabot[bot] 2228703250 build(deps): bump actions/checkout from 5 to 7 (#10)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 10:04:30 -07:00
Matthew Jackson c9e94b6bd6 CHANGELOG: 1.6.10 (unified release; driven by libfreemkv TrueHD discontinuity fix) v1.6.9-rc1 2026-08-23 16:30:29 -07:00
Matthew Jackson 7b03c0dc65 v1.6.9: bump version (unified release) v1.6.9 2026-08-22 08:34:13 -07:00
Matthew Jackson a03cb454f6 changelog: 1.6.9 unified-release entry 2026-08-22 08:24:57 -07:00
Matthew Jackson 367faf6726 v1.6.8: bump version (unified release) v1.6.8 v1.6.8-rc1 2026-08-21 12:54:35 -07:00
Matthew Jackson 57637a22a3 docs: 1.6.8 changelog (unified release) 2026-08-21 12:47:34 -07:00
Matthew Jackson 991ce55042 ci: require a CHANGELOG entry for the crate version on main
Adds a 'changelog' job that fails when Cargo.toml's version has no matching
'## [X.Y.Z]' heading in CHANGELOG.md, hard on main and PRs into main, warning
on dev/qa — so the unified release can't ship a crate with a blank changelog.
2026-08-21 11:23:43 -07:00
Matthew Jackson 78d26cf8e0 v1.6.7: bump version (unified release) v1.6.7 2026-08-21 09:31:17 -07:00
Matthew Jackson c12e8ef748 docs: 1.6.7 changelog (unified release, autorip-driven) 2026-08-21 09:23:55 -07:00
Matthew Jackson 1f644afe80 v1.6.6: bump version (unified release) v1.6.6 2026-08-20 20:03:31 -07:00
Matthew Jackson 423366308b docs: 1.6.6 changelog 2026-08-20 19:56:40 -07:00
Matthew Jackson 759390af96 v1.6.5: bump version (unified release) v1.6.5 v1.6.5-rc1 2026-08-20 06:34:47 +00:00
Matthew Jackson 7bbcc46f12 docs: 1.6.5 changelog v1.6.4-rc4 2026-08-19 21:20:54 -07:00
Matthew Jackson eaa158c379 Wire the AACS 2.0 (P-256) host-cert handshake: live and cert-agnostic
The 2.0 host-cert handshake is now live and cert-agnostic (works when a
cert is supplied), with the same chain-of-trust hardening as 1.0; the
SECURITY note is resolved by fixing, not by leaving dead.

- Remove the module-level #[allow(dead_code)]; the P-256/v2 AKE is now
  reachable from run_cert_handshake.
- run_cert_handshake selects the P-256 AKE per host cert: the 1.0 cert is
  tried first (an AACS 2.0 drive accepts it for the VID); if the drive
  refuses it AND the supplied HostCert carries AACS 2.0 (P-256)
  credentials, fall back to the native P-256 AKE. No possession/feature
  gate — it consumes whatever the HostCert carries, exactly as the 1.0
  path consumes hc.certificate. A 1.0-attempt transport fault
  short-circuits the fallback so a dead bus is not hammered.
- Chain-of-trust hardening on the now-live v2 path, mirroring 1.0:
  drive-cert TYPE gate (only 0x11 accepted, else rejected), cert verify
  is FATAL (verify_cert_p256 under the real 2.0 LA anchor), ECDSA-P256
  point validation on every attacker-supplied point (point_on_curve in
  ecdsa_verify_p256 + compute_bus_key_p256), and AGID release on every
  exit.
- The 2.0 cert byte layout/signed-range is documented as PROVISIONAL: no
  genuine 2.0 host/drive AKE cert exists to pin it, and the draft spec
  implies extra Host-ID/paired-DKS fields. A wrong signed range fails
  CLOSED (fatal verify rejects), never trusts. An #[ignore] slot awaits a
  real cert.
- Tests: synthetic test-LA-signed 2.0 certs prove accept + bus-key
  derivation, bad-signature fatal reject, unknown-type reject, and
  off-curve/identity rejection (each red-before-green per guard); a
  genuine AACS 2.0 content cert verifies under the published CC key,
  proving the P-256/SHA-256 primitive against real material.
v1.6.4-rc3
2026-08-18 23:21:15 -07:00
Matthew Jackson 74fe85cdd2 harden AACS/firmware unlock against forged trust + dead-bus-as-success, and land the real on-curve AACS LA anchors
Two coupled classes. The chain-of-trust hardening makes a genuine AACS
LA public key load-bearing (the new ECDSA public-key validation rejects
every off-curve key), so the anchor replacement ships in the same commit
— with the old off-curve constants and the new guard, verify_cert would
reject every certificate.

=== Chain of trust (forged-trust class) ===
- aacs_authenticate: reject an unknown drive-cert type instead of falling
  through. The if/else-if had no else, so a type that was neither 0x01
  (AACS 1.0) nor 0x11 (AACS 2.0) skipped LA verification yet still ran the
  step-6 key-signature check against the drive's OWN key lifted from a cert
  that was never LA-verified — a rogue bridge could inject an attacker-chosen
  bus key. Reject at the cert-type gate, before any key is trusted.
- ecdsa_verify / ecdsa_verify_p256: validate the public key Q (on-curve,
  in-field, not the identity) before use. A Q of (0,0) collapsed verification
  to a form forgeable with no private key.
- verify_cert_p256: guard on the real 132-byte P-256 cert length (was 138,
  which the 132-byte caller never satisfied, so it returned false for 100%
  of AACS 2.0 drives) and read the signature at the real offsets.

=== Failure-that-looks-like-success class (dead bus reported as unlocked) ===
- run_cert_handshake read_data_keys arm: classify a transport fault like its
  read_volume_id sibling. A bus that died during the data-key read returned
  Ok(volume_id: Some, read_data_key: None) — a dead bus rendered as success.
- LibreDrive::firmware_unlock: a transport fault in probe_disc now aborts with
  Transport. For the 140/206 profiles without a read_vid_cdb the bus is never
  touched again, so this swallowed fault was the only dead-bus signal and
  warn-and-continue reported Ok(drive_unlocked: true) on a dead bus.
- mt1959 run_probe pass-1 and pass-2 speed-probe loops: preserve the transport
  classification instead of flattening to UnlockFailed (pass-1) or silently
  returning Ok(()) (pass-2 bare break).

=== AGID + key-material redaction ===
- aacs2_authenticate_p256: release the AGID on every fallible exit (the v1
  twin was fixed; the v2 path still leaked).
- Unlocked, DriveProfile: hand-written redacting Debug. Unlocked is the public
  carrier for bus_key/vid; DriveProfile (reachable via the public catalog)
  carried the drive firmware image and per-drive vendor CDBs — a derived Debug
  printed all of it. This crate is unpublished precisely because of that
  firmware.

=== Validated AACS Licensing Administrator anchors ===
Replace the compiled-in AACS 1.0 and 2.0 LA public keys, which were
OFF-CURVE (so the new Q-validation rejected every cert signed by them),
with the real published keys. Both confirmed on-curve; the 1.0 key also
verifies a genuine LA-signed host certificate. The native P-256 handshake
stays #[allow(dead_code)] and is NOT wired — a security note documents the
cert-type gate + fatal verification that must be added first, once the 2.0
wire layout is confirmed against a real drive cert.

Tests: every behavioural fix is pinned with a red-before-green test naming
its mutation, including transport-abort coverage for validate, both firmware
upload variants, both probe passes, and the probe-disc dead-bus path; the
anchor landing is pinned by an on-curve assertion and a genuine-cert verify
(both go red if an anchor reverts to the old off-curve value).
2026-08-18 22:56:30 -07:00