Compare commits

...
10 Commits
Author SHA1 Message Date
Matthew Jackson 28bc965fa1 v1.4.2: bump version (unified release)
leak-guard / leak-guard (push) Successful in 6s
CI / test (push) Failing after 5m45s
CI / lint (push) Successful in 10m18s
2026-07-15 19:37:56 -07:00
Matthew Jackson c81cc5ccaa Adapt keydb test to segregated decrypt primitives; 1.4.2 changelog
The KEYDB-gated test that feeds real key material into libfreemkv's AACS crypto
now composes decrypt_unit + is_clean (the removed decrypt_unit_try_keys wrapper's
behaviour), unchanged semantics.
2026-07-15 19:35:25 -07:00
Matthew Jackson 03765184c8 v1.4.1: bump version (unified release) 2026-07-14 14:45:36 -07:00
Matthew Jackson 827d757a7a Changelog for 1.4.1 2026-07-14 14:43:14 -07:00
Matthew Jackson e123455861 v1.4.0: bump version (unified release) 2026-07-13 19:28:58 -07:00
Matthew Jackson cf7e01f90c Changelog: 1.4.0 (Blu-ray 3D / MVC) 2026-07-13 12:47:03 -07:00
Matthew Jackson b67d290f3e v1.3.2: bump version (unified release) 2026-07-10 14:26:14 -07:00
Matthew Jackson 4f6029e4d4 1.3.2: carry UnitKey.variant_number (all sources emit 0)
Construct unit keys via UnitKey::new; ordinary content is variant 0. Inherits libfreemkv 1.3.2. No behaviour change.
2026-07-10 14:00:28 -07:00
Matthew Jackson 46fecaeef1 v1.3.1: bump version (unified release) 2026-07-10 12:35:16 -07:00
Matthew Jackson 5f973293c0 1.3.1: relicense to MIT
Relicensed from AGPL-3.0 to MIT, effective 1.3.1 (<=1.3.0 remain AGPL).
Version sync to 1.3.1.
2026-07-10 12:31:19 -07:00
8 changed files with 75 additions and 31 deletions
+32
View File
@@ -1,5 +1,37 @@
# Changelog # Changelog
## [1.4.2] — 2026-07-15
Version sync with the workspace; inherits libfreemkv 1.4.2. The keydb test that
feeds real key material into the AACS crypto was adapted to the segregated
`decrypt_unit` + `is_clean` primitives (behaviour unchanged).
## [1.4.1] — 2026-07-14
Version sync with the workspace; inherits libfreemkv 1.4.1.
## [1.4.0] — 2026-07-13
Version sync with the workspace; inherits libfreemkv 1.4.0.
## [1.3.2] — 2026-07-10
### Changed
- Unit keys carry libfreemkv's new `UnitKey.variant_number`; every source
(keydb, online, VUK-derived) emits `0` — ordinary, non-forensic content —
via the `UnitKey::new` constructor. No behaviour change. Inherits
**libfreemkv 1.3.2**.
## [1.3.1] — 2026-07-10
### Licensing
- **Relicensed to the MIT License, from 1.3.1 onwards** (releases up to and
including 1.3.0 remain under AGPL-3.0).
Version sync with the workspace; inherits libfreemkv 1.3.1.
## [1.3.0] — 2026-07-08 ## [1.3.0] — 2026-07-08
### Added ### Added
+4 -4
View File
@@ -1,9 +1,9 @@
[package] [package]
name = "freemkv-keysources" name = "freemkv-keysources"
version = "1.3.0" version = "1.4.2"
edition = "2024" edition = "2024"
rust-version = "1.86" rust-version = "1.86"
license = "AGPL-3.0-only" license = "MIT"
description = "Pluggable AACS key sources (keydb, online key service) for libfreemkv. Each source looks a disc up and hands libfreemkv its terminal Unit Keys via get_uk; the library does all derivation." description = "Pluggable AACS key sources (keydb, online key service) for libfreemkv. Each source looks a disc up and hands libfreemkv its terminal Unit Keys via get_uk; the library does all derivation."
repository = "https://github.com/freemkv/freemkv-keysources" repository = "https://github.com/freemkv/freemkv-keysources"
keywords = ["aacs", "blu-ray", "uhd", "decryption", "keydb"] keywords = ["aacs", "blu-ray", "uhd", "decryption", "keydb"]
@@ -17,7 +17,7 @@ publish = false
# The crate provides the `KeySource` trait + `Key`/`DiscInputs` types these impls fill. # The crate provides the `KeySource` trait + `Key`/`DiscInputs` types these impls fill.
# libfreemkv is git-only now; the committed [patch.crates-io] below redirects this # libfreemkv is git-only now; the committed [patch.crates-io] below redirects this
# bare version req to the libfreemkv git tag (local dev overrides it to a path). # bare version req to the libfreemkv git tag (local dev overrides it to a path).
libfreemkv = "1.3" libfreemkv = "1.4"
# OnlineSource: POST disc inputs + samples to a key service over HTTP. # OnlineSource: POST disc inputs + samples to a key service over HTTP.
ureq = { version = "2", features = ["json"] } ureq = { version = "2", features = ["json"] }
serde_json = "1" serde_json = "1"
@@ -40,4 +40,4 @@ codegen-units = 1
# overrides it with a path patch via the gitignored .cargo/config.toml (a # overrides it with a path patch via the gitignored .cargo/config.toml (a
# config-level [patch.crates-io] wins over this manifest one for the same crate). # config-level [patch.crates-io] wins over this manifest one for the same crate).
[patch.crates-io] [patch.crates-io]
libfreemkv = { git = "https://github.com/freemkv/libfreemkv", tag = "v1.3.0" } libfreemkv = { git = "https://github.com/freemkv/libfreemkv", tag = "v1.4.2" }
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Matthew Jackson & Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -4
View File
@@ -184,10 +184,7 @@ impl KeydbSource {
// 1. Terminal Unit Keys stored in the entry — directly usable, no // 1. Terminal Unit Keys stored in the entry — directly usable, no
// derivation. Preserve the keydb's CPS numbering (idx = num - 1). // derivation. Preserve the keydb's CPS numbering (idx = num - 1).
for (num, key) in &entry.unit_keys { for (num, key) in &entry.unit_keys {
keys.push(UnitKey { keys.push(UnitKey::new(num.saturating_sub(1), *key));
idx: num.saturating_sub(1),
key: *key,
});
} }
// The disc's encrypted title keys (from Unit_Key_RO.inf) — what every // The disc's encrypted title keys (from Unit_Key_RO.inf) — what every
+14 -14
View File
@@ -1412,7 +1412,7 @@ mod tests {
// //
// These exercise the parser (KeyDb::load) end-to-end against a real // These exercise the parser (KeyDb::load) end-to-end against a real
// keydb.cfg and feed its material into libfreemkv's AACS crypto // keydb.cfg and feed its material into libfreemkv's AACS crypto
// (derive_vuk / decrypt_unit_try_keys). They live here now that the // (derive_vuk, then decrypt_unit + is_clean_ts). They live here now that the
// parser lives here. All are KEYDB_PATH-env-gated and no-op in CI when // parser lives here. All are KEYDB_PATH-env-gated and no-op in CI when
// the env is unset; they must still COMPILE. // the env is unset; they must still COMPILE.
// ════════════════════════════════════════════════════════════════════ // ════════════════════════════════════════════════════════════════════
@@ -1485,20 +1485,20 @@ mod tests {
eprintln!("Found {} entries with unit keys", candidate_entries.len()); eprintln!("Found {} entries with unit keys", candidate_entries.len());
// Try each entry's unit keys // Try each entry's unit keys: apply the key, then ask whether it opened
// the unit (the segregated primitives — decrypt, then structural check).
for entry in &candidate_entries { for entry in &candidate_entries {
let keys: Vec<[u8; 16]> = entry.unit_keys.iter().map(|(_, k)| *k).collect(); for (_, key) in &entry.unit_keys {
let mut unit = original.clone(); let mut unit = original.clone();
libfreemkv::aacs::content::decrypt_unit(&mut unit, key);
if let Some(res) = libfreemkv::aacs::content::decrypt_unit_try_keys(&mut unit, &keys) { if libfreemkv::aacs::content::is_clean(&unit, libfreemkv::disc::ContentFormat::BdTs)
eprintln!( {
"SUCCESS: Decrypted with entry {} ({res:?})", eprintln!("SUCCESS: Decrypted with entry {}", entry.disc_hash);
entry.disc_hash // Count TS sync bytes
); let ts = (0..32).filter(|&i| unit[4 + i * 192] == 0x47).count();
// Count TS sync bytes eprintln!(" TS sync bytes: {}/32", ts);
let ts = (0..32).filter(|&i| unit[4 + i * 192] == 0x47).count(); return;
eprintln!(" TS sync bytes: {}/32", ts); }
return;
} }
} }
+1 -4
View File
@@ -47,10 +47,7 @@ pub(crate) fn uks_from_vuk(vuk: &[u8; 16], enc_title_keys: &[[u8; 16]]) -> Vec<U
enc_title_keys enc_title_keys
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, e)| UnitKey { .map(|(i, e)| UnitKey::new(i as u32, libfreemkv::aacs::derive::decrypt_unit_key(vuk, e)))
idx: i as u32,
key: libfreemkv::aacs::derive::decrypt_unit_key(vuk, e),
})
.collect() .collect()
} }
+1 -1
View File
@@ -306,7 +306,7 @@ impl OnlineSource {
// A terminal UK is used directly (CPS unit 0 → committed cps 1, matching // A terminal UK is used directly (CPS unit 0 → committed cps 1, matching
// the old `Key::Unit(vec![(1, uk)])`). // the old `Key::Unit(vec![(1, uk)])`).
if let Some(uk) = json.get("UK").and_then(|u| u.as_str()).and_then(parse_uk) { if let Some(uk) = json.get("UK").and_then(|u| u.as_str()).and_then(parse_uk) {
return vec![UnitKey { idx: 0, key: uk }]; return vec![UnitKey::new(0, uk)];
} }
// A VUK is derived to the terminal keys locally, via the disc's // A VUK is derived to the terminal keys locally, via the disc's
// encrypted title keys from the context — the library owns the crypto. // encrypted title keys from the context — the library owns the crypto.
+1 -4
View File
@@ -267,10 +267,7 @@ impl KeySource for ScriptedSource {
} }
fn uk(b: u8) -> UnitKey { fn uk(b: u8) -> UnitKey {
UnitKey { UnitKey::new(0, [b; 16])
idx: 0,
key: [b; 16],
}
} }
#[test] #[test]