aacs: unify the SD-walk; derive_media_key_from_dk now exposes the PK
Two byte-identical copies of the subset-difference walk lived in keys.rs and variants.rs. Consolidate the pure helpers (aesg3, calc_v_mask, calc_pk_from_dk) into keys.rs (pub(super)); variants.rs imports them. Add derive_media_key_and_pk_from_dk(mkb, dks) -> Option<(mk, pk)>, which returns the intermediate Processing Key the walk already computes; derive_media_key_from_dk becomes a thin wrapper. This lets callers bank the PK on a DK boil instead of re-deriving it via a second, divergent walk (the classical-vs-variant cvalues order made that miss silently). 216 AACS tests pass (incl. a new (mk,pk) regression); precommit (1.86) green.
This commit is contained in:
+114
-5
@@ -611,7 +611,10 @@ const AESG3_SEED: [u8; 16] = [
|
|||||||
|
|
||||||
/// AACS-G3: derive a subkey from a parent key.
|
/// AACS-G3: derive a subkey from a parent key.
|
||||||
/// seed[15] += inc, then AES-DEC(key, seed) XOR seed.
|
/// seed[15] += inc, then AES-DEC(key, seed) XOR seed.
|
||||||
fn aesg3(key: &[u8; 16], inc: u8) -> [u8; 16] {
|
///
|
||||||
|
/// Shared with [`super::variants`] (its variant chain runs the same SD
|
||||||
|
/// tree); a single definition keeps the two walks byte-identical.
|
||||||
|
pub(super) fn aesg3(key: &[u8; 16], inc: u8) -> [u8; 16] {
|
||||||
let mut seed = AESG3_SEED;
|
let mut seed = AESG3_SEED;
|
||||||
seed[15] = seed[15].wrapping_add(inc);
|
seed[15] = seed[15].wrapping_add(inc);
|
||||||
let mut out = aes_ecb_decrypt(key, &seed);
|
let mut out = aes_ecb_decrypt(key, &seed);
|
||||||
@@ -621,8 +624,8 @@ fn aesg3(key: &[u8; 16], inc: u8) -> [u8; 16] {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute v_mask from a UV value.
|
/// Compute v_mask from a UV value. Shared with [`super::variants`].
|
||||||
fn calc_v_mask(uv: u32) -> u32 {
|
pub(super) fn calc_v_mask(uv: u32) -> u32 {
|
||||||
let mut v_mask: u32 = 0xFFFF_FFFF;
|
let mut v_mask: u32 = 0xFFFF_FFFF;
|
||||||
while (uv & !v_mask) == 0 && v_mask != 0 {
|
while (uv & !v_mask) == 0 && v_mask != 0 {
|
||||||
v_mask <<= 1;
|
v_mask <<= 1;
|
||||||
@@ -631,7 +634,13 @@ fn calc_v_mask(uv: u32) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Derive processing key from device key using subset-difference tree traversal.
|
/// Derive processing key from device key using subset-difference tree traversal.
|
||||||
fn calc_pk_from_dk(dk: &[u8; 16], uv: u32, v_mask: u32, dev_key_v_mask: u32) -> [u8; 16] {
|
/// Shared with [`super::variants`].
|
||||||
|
pub(super) fn calc_pk_from_dk(
|
||||||
|
dk: &[u8; 16],
|
||||||
|
uv: u32,
|
||||||
|
v_mask: u32,
|
||||||
|
dev_key_v_mask: u32,
|
||||||
|
) -> [u8; 16] {
|
||||||
// Initial derivation: left_child = aesg3(dk, 0), pk = aesg3(dk, 1), right_child = aesg3(dk, 2)
|
// Initial derivation: left_child = aesg3(dk, 0), pk = aesg3(dk, 1), right_child = aesg3(dk, 2)
|
||||||
let mut left_child = aesg3(dk, 0);
|
let mut left_child = aesg3(dk, 0);
|
||||||
let mut pk = aesg3(dk, 1);
|
let mut pk = aesg3(dk, 1);
|
||||||
@@ -675,7 +684,26 @@ fn calc_pk_from_dk(dk: &[u8; 16], uv: u32, v_mask: u32, dev_key_v_mask: u32) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Derive Media Key from MKB using device keys (subset-difference tree).
|
/// Derive Media Key from MKB using device keys (subset-difference tree).
|
||||||
|
///
|
||||||
|
/// Thin wrapper over [`derive_media_key_and_pk_from_dk`] that drops the
|
||||||
|
/// intermediate Processing Key. Callers that need the PK lineage (e.g.
|
||||||
|
/// the key service banking DK·PK·MK) should call the `_and_pk_` form.
|
||||||
pub fn derive_media_key_from_dk(mkb: &[u8], device_keys: &[DeviceKey]) -> Option<[u8; 16]> {
|
pub fn derive_media_key_from_dk(mkb: &[u8], device_keys: &[DeviceKey]) -> Option<[u8; 16]> {
|
||||||
|
derive_media_key_and_pk_from_dk(mkb, device_keys).map(|(mk, _pk)| mk)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive both the Media Key and the intermediate Processing Key from an
|
||||||
|
/// MKB using device keys (subset-difference tree).
|
||||||
|
///
|
||||||
|
/// Identical walk to [`derive_media_key_from_dk`]; this form additionally
|
||||||
|
/// returns the Processing Key `Kp` derived at the matching subset-difference
|
||||||
|
/// node — the value `calc_pk_from_dk` produces immediately before it
|
||||||
|
/// validates into the Media Key. Returns `Some((mk, pk))` for the first DK
|
||||||
|
/// that walks a uv slot whose Processing Key validates against the MKB.
|
||||||
|
pub fn derive_media_key_and_pk_from_dk(
|
||||||
|
mkb: &[u8],
|
||||||
|
device_keys: &[DeviceKey],
|
||||||
|
) -> Option<([u8; 16], [u8; 16])> {
|
||||||
let mk_dv = mkb_find_mk_dv(mkb)?;
|
let mk_dv = mkb_find_mk_dv(mkb)?;
|
||||||
let uvs = mkb_find_subdiff_records(mkb)?;
|
let uvs = mkb_find_subdiff_records(mkb)?;
|
||||||
let cvalues = mkb_find_cvalues(mkb)?;
|
let cvalues = mkb_find_cvalues(mkb)?;
|
||||||
@@ -735,7 +763,7 @@ pub fn derive_media_key_from_dk(mkb: &[u8], device_keys: &[DeviceKey]) -> Option
|
|||||||
if let Some(mk) =
|
if let Some(mk) =
|
||||||
validate_processing_key(&pk, cv, &uvs[1 + uvs_idx * 5..], &mk_dv)
|
validate_processing_key(&pk, cv, &uvs[1 + uvs_idx * 5..], &mk_dv)
|
||||||
{
|
{
|
||||||
return Some(mk);
|
return Some((mk, pk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2741,4 +2769,85 @@ mod tests {
|
|||||||
let mkb = [0x05, 0x00, 0x00, 0x04]; // type 0x05, no body
|
let mkb = [0x05, 0x00, 0x00, 0x04]; // type 0x05, no body
|
||||||
assert!(probe::mkb_record_body(&mkb, 0x05).is_none());
|
assert!(probe::mkb_record_body(&mkb, 0x05).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derive_media_key_and_pk_from_dk_returns_intermediate_pk() {
|
||||||
|
// Regression: a classical DK boil must yield the intermediate
|
||||||
|
// Processing Key, not just the Media Key. The key service banks the
|
||||||
|
// PK lineage (DK·PK·MK·VUK·UK); before the `_and_pk_` form existed it
|
||||||
|
// recovered the MK here but lost the PK silently.
|
||||||
|
//
|
||||||
|
// Build a minimal classical MKB (no 0x82/0x83) with:
|
||||||
|
// - 0x04 Subset-Difference: u_mask_shift=3, uv=0x00000002
|
||||||
|
// - 0x05 cvalues: one cvalue C planted so AES-D(Kp, C) XOR uv == mk
|
||||||
|
// - 0x86 Verify Media Key: mk_dv = AES-E(mk, magic || pad)
|
||||||
|
// and a DK with node=4, uv=2, u_mask_shift=3 so dev_key_v_mask ==
|
||||||
|
// v_mask: the calc_pk_from_dk loop is a no-op and Kp == aesg3(dk, 1).
|
||||||
|
use super::super::decrypt::aes_ecb_encrypt as enc;
|
||||||
|
|
||||||
|
let dk_bytes: [u8; 16] = [
|
||||||
|
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
|
||||||
|
0xFF, 0x00,
|
||||||
|
];
|
||||||
|
// Expected Processing Key for the no-op walk.
|
||||||
|
let expected_pk = aesg3(&dk_bytes, 1);
|
||||||
|
|
||||||
|
// Plant a known Media Key.
|
||||||
|
let mk: [u8; 16] = [
|
||||||
|
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD,
|
||||||
|
0xAE, 0xAF,
|
||||||
|
];
|
||||||
|
// uv (big-endian) = 0x00000002; validate XORs uv into mk[12..16].
|
||||||
|
let uv_bytes: [u8; 4] = [0x00, 0x00, 0x00, 0x02];
|
||||||
|
|
||||||
|
// cvalue C = AES-E(Kp, mk_raw) where mk_raw = mk with the uv XOR
|
||||||
|
// pre-undone, so the validate step XORs uv back in and recovers mk.
|
||||||
|
let mut mk_raw = mk;
|
||||||
|
for a in 0..4 {
|
||||||
|
mk_raw[12 + a] ^= uv_bytes[a];
|
||||||
|
}
|
||||||
|
let cvalue = enc(&expected_pk, &mk_raw);
|
||||||
|
|
||||||
|
// mk_dv = AES-E(mk, magic || pad); validate decrypts it under mk and
|
||||||
|
// checks the leading 8 bytes against the verify magic.
|
||||||
|
let mut plaintext_vd = [0u8; 16];
|
||||||
|
plaintext_vd[..8].copy_from_slice(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]);
|
||||||
|
plaintext_vd[8..].copy_from_slice(&[0x11; 8]);
|
||||||
|
let mk_dv = enc(&mk, &plaintext_vd);
|
||||||
|
|
||||||
|
// Assemble the MKB. Type/Version (0x10) header first.
|
||||||
|
let mut mkb = vec![
|
||||||
|
0x10, 0x00, 0x00, 0x0C, 0x48, 0x14, 0x10, 0x03, 0x00, 0x00, 0x00, 0x4D,
|
||||||
|
];
|
||||||
|
// 0x04 Subset-Difference: body = u_mask_shift(0x03) || uv(4 bytes).
|
||||||
|
mkb.extend_from_slice(&[0x04, 0x00, 0x00, 0x09]);
|
||||||
|
mkb.extend_from_slice(&[0x03]);
|
||||||
|
mkb.extend_from_slice(&uv_bytes);
|
||||||
|
// 0x05 cvalues: one 16-byte cvalue (mkb_find_cvalues prefers 0x05).
|
||||||
|
mkb.extend_from_slice(&[0x05, 0x00, 0x00, 0x14]);
|
||||||
|
mkb.extend_from_slice(&cvalue);
|
||||||
|
// 0x86 Verify Media Key: mk_dv.
|
||||||
|
mkb.extend_from_slice(&[0x86, 0x00, 0x00, 0x14]);
|
||||||
|
mkb.extend_from_slice(&mk_dv);
|
||||||
|
|
||||||
|
let dk = DeviceKey {
|
||||||
|
key: dk_bytes,
|
||||||
|
node: 4,
|
||||||
|
uv: 2,
|
||||||
|
u_mask_shift: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The new `_and_pk_` form returns BOTH the MK and the intermediate PK.
|
||||||
|
let dks = [dk];
|
||||||
|
let (got_mk, got_pk) = derive_media_key_and_pk_from_dk(&mkb, &dks)
|
||||||
|
.expect("classical DK boil must derive (mk, pk)");
|
||||||
|
assert_eq!(got_mk, mk, "recovered Media Key must match the planted MK");
|
||||||
|
assert_eq!(
|
||||||
|
got_pk, expected_pk,
|
||||||
|
"returned Processing Key must equal aesg3(dk, 1) for the no-op walk"
|
||||||
|
);
|
||||||
|
|
||||||
|
// And the thin wrapper must still return just the MK.
|
||||||
|
assert_eq!(derive_media_key_from_dk(&mkb, &dks), Some(mk));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -31,10 +31,10 @@ pub use keydb::{DeviceKey, DiscEntry, HostCert, KeyDb};
|
|||||||
pub use keys::probe;
|
pub use keys::probe;
|
||||||
pub use keys::{
|
pub use keys::{
|
||||||
AacsVersion, ContentCert, ResolveContext, ResolvedKeys, UnitKeyFile, decrypt_unit_key,
|
AacsVersion, ContentCert, ResolveContext, ResolvedKeys, UnitKeyFile, decrypt_unit_key,
|
||||||
derive_media_key_from_dk, derive_media_key_from_pk, derive_media_key_from_pk_walked,
|
derive_media_key_and_pk_from_dk, derive_media_key_from_dk, derive_media_key_from_pk,
|
||||||
derive_vuk, disc_hash, disc_hash_hex, mkb_content_len, mkb_version, parse_content_cert,
|
derive_media_key_from_pk_walked, derive_vuk, disc_hash, disc_hash_hex, mkb_content_len,
|
||||||
parse_unit_key_ro, read_mkb_from_drive, resolve_keys_v1, resolve_keys_v2, resolve_keys_v21,
|
mkb_version, parse_content_cert, parse_unit_key_ro, read_mkb_from_drive, resolve_keys_v1,
|
||||||
trim_mkb,
|
resolve_keys_v2, resolve_keys_v21, trim_mkb,
|
||||||
};
|
};
|
||||||
pub use provider::KeyProvider;
|
pub use provider::KeyProvider;
|
||||||
pub use variants::{
|
pub use variants::{
|
||||||
|
|||||||
+34
-67
@@ -177,66 +177,11 @@ fn aes_g(x1: &[u8; 16], x2: &[u8; 16]) -> [u8; 16] {
|
|||||||
|
|
||||||
// ── Subset-difference walk that exposes (Kp, uv) ──────────────────────────
|
// ── Subset-difference walk that exposes (Kp, uv) ──────────────────────────
|
||||||
|
|
||||||
/// AES-G3 seed register initial value.
|
// `calc_v_mask` and `calc_pk_from_dk` (and the AES-G3 seed step they ride
|
||||||
const AESG3_SEED: [u8; 16] = [
|
// on) are shared with the classical walk in [`super::keys`] — a single
|
||||||
0x7B, 0x10, 0x3C, 0x5D, 0xCB, 0x08, 0xC4, 0xE5, 0x1A, 0x27, 0xB0, 0x17, 0x99, 0x05, 0x3B, 0xD9,
|
// definition keeps the variant SD tree byte-identical to the classical one.
|
||||||
];
|
// (`aesg3` itself is imported separately in the test module.)
|
||||||
|
use super::keys::{calc_pk_from_dk, calc_v_mask};
|
||||||
/// AES-G3 single step: AES-G against the seed register at offset `inc`.
|
|
||||||
fn aesg3_step(key: &[u8; 16], inc: u8) -> [u8; 16] {
|
|
||||||
let mut seed = AESG3_SEED;
|
|
||||||
seed[15] = seed[15].wrapping_add(inc);
|
|
||||||
aes_g(key, &seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc_v_mask(uv: u32) -> u32 {
|
|
||||||
let mut v_mask: u32 = 0xFFFF_FFFF;
|
|
||||||
while (uv & !v_mask) == 0 && v_mask != 0 {
|
|
||||||
v_mask <<= 1;
|
|
||||||
}
|
|
||||||
v_mask
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc_pk_from_dk(dk: &[u8; 16], uv: u32, v_mask: u32, dev_key_v_mask: u32) -> [u8; 16] {
|
|
||||||
let mut left_child = aesg3_step(dk, 0);
|
|
||||||
let mut pk = aesg3_step(dk, 1);
|
|
||||||
let mut right_child = aesg3_step(dk, 2);
|
|
||||||
let mut current_v_mask = dev_key_v_mask;
|
|
||||||
|
|
||||||
// Bound the walk to the 32-level depth of a u32 subset-difference tree.
|
|
||||||
// `current_v_mask` advances via an arithmetic `>> 1` which sign-extends, so
|
|
||||||
// a disc-supplied v_mask coarser than dev_key_v_mask would otherwise drive
|
|
||||||
// current_v_mask up to 0xFFFF_FFFF and spin forever — a crafted MKB must
|
|
||||||
// not hang the rip thread (this runs before the KCD placeholder gate).
|
|
||||||
let mut steps = 0u32;
|
|
||||||
while current_v_mask != v_mask {
|
|
||||||
if steps >= 32 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
steps += 1;
|
|
||||||
let mut bit_pos: i32 = -1;
|
|
||||||
for i in (0..32).rev() {
|
|
||||||
if (current_v_mask & (1u32 << i)) == 0 {
|
|
||||||
bit_pos = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let curr_key = if bit_pos < 0 || (uv & (1u32 << bit_pos as u32)) == 0 {
|
|
||||||
left_child
|
|
||||||
} else {
|
|
||||||
right_child
|
|
||||||
};
|
|
||||||
|
|
||||||
left_child = aesg3_step(&curr_key, 0);
|
|
||||||
pk = aesg3_step(&curr_key, 1);
|
|
||||||
right_child = aesg3_step(&curr_key, 2);
|
|
||||||
|
|
||||||
current_v_mask = ((current_v_mask as i32) >> 1) as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
pk
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outcome of a subset-difference walk against an MKB. Carries the
|
/// Outcome of a subset-difference walk against an MKB. Carries the
|
||||||
/// processing key and the matching `uv` slot — both needed as inputs
|
/// processing key and the matching `uv` slot — both needed as inputs
|
||||||
@@ -271,6 +216,27 @@ fn mkb_find_mk_dv(records: &[MkbRecord]) -> Option<[u8; 16]> {
|
|||||||
|
|
||||||
/// Walk an MKB and return the first `(Kp, uv, cvalue)` that
|
/// Walk an MKB and return the first `(Kp, uv, cvalue)` that
|
||||||
/// `device_keys` covers. Returns `None` if no DK walks any uv.
|
/// `device_keys` covers. Returns `None` if no DK walks any uv.
|
||||||
|
///
|
||||||
|
/// This is the AACS-2.1 **variant** walk; the classical walk lives in
|
||||||
|
/// [`super::keys::derive_media_key_and_pk_from_dk`]. The two are kept
|
||||||
|
/// separate on purpose and select MKB records in DELIBERATELY different
|
||||||
|
/// order:
|
||||||
|
///
|
||||||
|
/// - cvalues: this variant walk tries record `0x07`-then-`0x05`; the
|
||||||
|
/// classical walk tries `0x05`-then-`0x07`. On a variant MKB the
|
||||||
|
/// small `0x07` Explicit-Subset-Difference record carries the
|
||||||
|
/// cvalue the Precursor chain consumes, whereas a classical UHD MKB
|
||||||
|
/// keeps its 1:1 cvalue table in the large `0x05` record (see the
|
||||||
|
/// note on [`super::keys::probe::mkb_cvalues`]). They must NOT be
|
||||||
|
/// unified to one order — each is correct for its own MKB shape.
|
||||||
|
/// - finders: this walk operates on parsed [`MkbRecord`]s (needed
|
||||||
|
/// because the variant chain also reads `0x82`/`0x83`); the
|
||||||
|
/// classical walk operates on raw MKB bytes. Same framing, different
|
||||||
|
/// input type.
|
||||||
|
///
|
||||||
|
/// Consequence: do NOT route the classical DK path through this function
|
||||||
|
/// — on a classical MKB the `0x07`-first selection picks the wrong (or
|
||||||
|
/// missing) cvalue and the magic check fails, so it returns `None`.
|
||||||
pub fn walk_processing_key(
|
pub fn walk_processing_key(
|
||||||
records: &[MkbRecord],
|
records: &[MkbRecord],
|
||||||
device_keys: &[DeviceKey],
|
device_keys: &[DeviceKey],
|
||||||
@@ -544,6 +510,10 @@ pub fn derive_media_key_variant(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
// These three live in `super::keys` now (consolidated SD-walk helpers);
|
||||||
|
// `use super::*` does not re-export the parent module's private `use`
|
||||||
|
// imports, so pull them in directly for the tests below.
|
||||||
|
use super::super::keys::{aesg3, calc_pk_from_dk};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn calc_pk_from_dk_terminates_on_nonconvergent_mask() {
|
fn calc_pk_from_dk_terminates_on_nonconvergent_mask() {
|
||||||
@@ -708,7 +678,7 @@ mod tests {
|
|||||||
/// agreeing with uv on bits 3+ (the u_mask=1 region). dk.uv ==
|
/// agreeing with uv on bits 3+ (the u_mask=1 region). dk.uv ==
|
||||||
/// MKB.uv and dk.u_mask_shift == MKB.u_mask_shift make
|
/// MKB.uv and dk.u_mask_shift == MKB.u_mask_shift make
|
||||||
/// `dev_key_v_mask == v_mask`, so `calc_pk_from_dk` loops zero
|
/// `dev_key_v_mask == v_mask`, so `calc_pk_from_dk` loops zero
|
||||||
/// times — Kp = aesg3_step(dk, 1).
|
/// times — Kp = aesg3(dk, 1).
|
||||||
/// - one cvalue in record 0x07 chosen so AES-D(Kp, C) ⊕ uv produces a
|
/// - one cvalue in record 0x07 chosen so AES-D(Kp, C) ⊕ uv produces a
|
||||||
/// Kmp whose byte-15 is exactly `kmp15`.
|
/// Kmp whose byte-15 is exactly `kmp15`.
|
||||||
/// - record 0x82 with a 16-byte body (acts as both Variant Data
|
/// - record 0x82 with a 16-byte body (acts as both Variant Data
|
||||||
@@ -731,12 +701,12 @@ mod tests {
|
|||||||
// Pick a known DK; with dk.uv == MKB.uv (==2) and
|
// Pick a known DK; with dk.uv == MKB.uv (==2) and
|
||||||
// dk.u_mask_shift == MKB.u_mask_shift (==3), dev_key_v_mask
|
// dk.u_mask_shift == MKB.u_mask_shift (==3), dev_key_v_mask
|
||||||
// equals the MKB's v_mask and the calc_pk_from_dk loop is a
|
// equals the MKB's v_mask and the calc_pk_from_dk loop is a
|
||||||
// no-op — Kp = aesg3_step(dk, 1).
|
// no-op — Kp = aesg3(dk, 1).
|
||||||
let dk_bytes: [u8; 16] = [
|
let dk_bytes: [u8; 16] = [
|
||||||
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
|
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
|
||||||
0xFF, 0x00,
|
0xFF, 0x00,
|
||||||
];
|
];
|
||||||
let kp = aesg3_step(&dk_bytes, 1);
|
let kp = aesg3(&dk_bytes, 1);
|
||||||
|
|
||||||
// Plant Kmp with chosen byte-15, then compute C such that
|
// Plant Kmp with chosen byte-15, then compute C such that
|
||||||
// AES-D(Kp, C) ⊕ uv == Kmp. uv=2 → low-4 bytes XOR is 00 00 00 02.
|
// AES-D(Kp, C) ⊕ uv == Kmp. uv=2 → low-4 bytes XOR is 00 00 00 02.
|
||||||
@@ -1002,10 +972,7 @@ mod tests {
|
|||||||
let (recs, dk, planted_kp, _) = synthetic_variant_setup(0x00);
|
let (recs, dk, planted_kp, _) = synthetic_variant_setup(0x00);
|
||||||
let m = walk_processing_key(&recs, &[dk]).expect("variant MKB yields a match");
|
let m = walk_processing_key(&recs, &[dk]).expect("variant MKB yields a match");
|
||||||
assert_eq!(m.uv, 2, "matched the planted uv");
|
assert_eq!(m.uv, 2, "matched the planted uv");
|
||||||
assert_eq!(
|
assert_eq!(m.kp, planted_kp, "Kp equals aesg3(dk,1) for the no-op walk");
|
||||||
m.kp, planted_kp,
|
|
||||||
"Kp equals aesg3_step(dk,1) for the no-op walk"
|
|
||||||
);
|
|
||||||
assert_eq!(m.cvalue_index, 0);
|
assert_eq!(m.cvalue_index, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user