libfreemkv 0.31.9: ~3x fewer AES ops in the subset-difference PK walk
calc_pk_from_dk derived all three children (left/pk/right) at every tree level but used only the one it descended into; the Processing Key only matters at the final node. Derive just the descended child per level + the PK once at the end. Bit-for-bit identical; speeds every DK->MK derivation (disc decryption + unpositioned-DK recovery). 60.8s -> 22.9s on a UHD worst-case recovery scan.
This commit is contained in:
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.31.9 (2026-06-17)
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Subset-difference PK walk (`calc_pk_from_dk`): at each tree level derive only
|
||||||
|
the child actually descended into (not both siblings) and compute the
|
||||||
|
Processing Key once at the final node — ~3x fewer AES block ops per walk.
|
||||||
|
Bit-for-bit identical output; speeds every Device-Key → Media-Key derivation
|
||||||
|
(disc decryption AND unpositioned-DK recovery).
|
||||||
|
|
||||||
|
|
||||||
## 0.31.8 (2026-06-17)
|
## 0.31.8 (2026-06-17)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libfreemkv"
|
name = "libfreemkv"
|
||||||
version = "0.31.8"
|
version = "0.31.9"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.86"
|
rust-version = "1.86"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
|
|||||||
+12
-12
@@ -616,10 +616,13 @@ pub(super) fn calc_pk_from_dk(
|
|||||||
v_mask: u32,
|
v_mask: u32,
|
||||||
dev_key_v_mask: u32,
|
dev_key_v_mask: u32,
|
||||||
) -> [u8; 16] {
|
) -> [u8; 16] {
|
||||||
// Initial derivation: left_child = aesg3(dk, 0), pk = aesg3(dk, 1), right_child = aesg3(dk, 2)
|
// Descend from the device node to the record node, following the record's
|
||||||
let mut left_child = aesg3(dk, 0);
|
// `uv` bits. At each level only the child we descend INTO is needed (the
|
||||||
let mut pk = aesg3(dk, 1);
|
// sibling is computed but never used), and the Processing Key is the
|
||||||
let mut right_child = aesg3(dk, 2);
|
// `aesg3(.,1)` of the FINAL node — so we derive ONE child per level and the
|
||||||
|
// PK once at the end, instead of left/pk/right at every level. Identical
|
||||||
|
// result, ~3x fewer block ops. (left child = `aesg3(node,0)`, right = `,2`.)
|
||||||
|
let mut node = *dk;
|
||||||
let mut current_v_mask = dev_key_v_mask;
|
let mut current_v_mask = dev_key_v_mask;
|
||||||
|
|
||||||
// The subset-difference tree is at most 32 levels deep (u32 mask), so the
|
// The subset-difference tree is at most 32 levels deep (u32 mask), so the
|
||||||
@@ -642,20 +645,17 @@ pub(super) fn calc_pk_from_dk(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let curr_key = if bit_pos < 0 || (uv & (1u32 << bit_pos as u32)) == 0 {
|
let inc = if bit_pos < 0 || (uv & (1u32 << bit_pos as u32)) == 0 {
|
||||||
left_child
|
0 // left child
|
||||||
} else {
|
} else {
|
||||||
right_child
|
2 // right child
|
||||||
};
|
};
|
||||||
|
node = aesg3(&node, inc);
|
||||||
left_child = aesg3(&curr_key, 0);
|
|
||||||
pk = aesg3(&curr_key, 1);
|
|
||||||
right_child = aesg3(&curr_key, 2);
|
|
||||||
|
|
||||||
current_v_mask = ((current_v_mask as i32) >> 1) as u32;
|
current_v_mask = ((current_v_mask as i32) >> 1) as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pk
|
aesg3(&node, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive Media Key from MKB using device keys (subset-difference tree).
|
/// Derive Media Key from MKB using device keys (subset-difference tree).
|
||||||
|
|||||||
Reference in New Issue
Block a user