mirror of
https://github.com/freemkv/libfreemkv.git
synced 2026-10-07 18:13:10 +00:00
keyproof: trim comments to the guard limits, rustfmt, and build/test it in CI
This commit is contained in:
@@ -82,6 +82,8 @@ jobs:
|
|||||||
working-directory: libfreemkv
|
working-directory: libfreemkv
|
||||||
- run: cargo +stable clippy --all-targets -- -D warnings
|
- run: cargo +stable clippy --all-targets -- -D warnings
|
||||||
working-directory: libfreemkv
|
working-directory: libfreemkv
|
||||||
|
- run: cargo +stable clippy --all-targets --features keyproof -- -D warnings
|
||||||
|
working-directory: libfreemkv
|
||||||
|
|
||||||
test:
|
test:
|
||||||
needs: resolve-msrv
|
needs: resolve-msrv
|
||||||
@@ -107,6 +109,9 @@ jobs:
|
|||||||
# `--tests` skips doctests; the keys compile_fail doctests enforce spec LK19.
|
# `--tests` skips doctests; the keys compile_fail doctests enforce spec LK19.
|
||||||
- run: cargo test --doc
|
- run: cargo test --doc
|
||||||
working-directory: libfreemkv
|
working-directory: libfreemkv
|
||||||
|
# `keyproof` is off by default, so its oracle compiles and is tested only here.
|
||||||
|
- run: cargo test --lib --features keyproof aacs::content
|
||||||
|
working-directory: libfreemkv
|
||||||
|
|
||||||
# The memory orderings in `scsi::fd_handoff` (and `halt`) compile to the same instructions
|
# The memory orderings in `scsi::fd_handoff` (and `halt`) compile to the same instructions
|
||||||
# on x86_64, so the WRONG ones pass every run; loom enumerates the executions
|
# on x86_64, so the WRONG ones pass every run; loom enumerates the executions
|
||||||
|
|||||||
+20
-23
@@ -176,20 +176,19 @@ fn is_clean_ts(unit: &[u8]) -> bool {
|
|||||||
content == 0 || synced >= content.min(KEY_PROOF_PACKETS)
|
content == 0 || synced >= content.min(KEY_PROOF_PACKETS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does `unit_key` open this encrypted AACS aligned `unit`? Returns one bit —
|
/// Does `unit_key` open this encrypted AACS aligned `unit`? One bit, never plaintext.
|
||||||
/// key right or wrong — and NEVER plaintext. The single authoritative answer to
|
/// BD-TS 6144-byte units take the decrypt-less TS-sync check (`ts_unit_key_opens`, only
|
||||||
/// "did this candidate key prove out against a sample unit", shared by the mux's
|
/// the proof blocks are touched); Program-Stream and other lengths fall back to the full
|
||||||
/// key selection/verify and by an offline key-resolver (kdb's `/decode`). BD-TS
|
/// [`decrypt_unit`] + [`is_clean`].
|
||||||
/// 6144-byte units take the decrypt-less TS-sync fast reject ([`ts_unit_key_opens`],
|
|
||||||
/// only the proof blocks are touched); Program-Stream and non-standard lengths
|
|
||||||
/// fall back to the full [`decrypt_unit`] + [`is_clean`].
|
|
||||||
///
|
///
|
||||||
/// Gated behind the `keyproof` feature so the DEFAULT public API still cannot
|
/// Behind the `keyproof` feature so the default public API still cannot decrypt AACS: a
|
||||||
/// decrypt AACS — this is a key→content ORACLE (one bit), not a decryptor, and
|
/// key oracle, not a decryptor (KU §2.2; see `keys::public_api_cannot_decrypt_aacs`).
|
||||||
/// it is a private-consumer seam, not part of the open surface
|
|
||||||
/// (KU §2.2; see `keys::public_api_cannot_decrypt_aacs`).
|
|
||||||
#[cfg(feature = "keyproof")]
|
#[cfg(feature = "keyproof")]
|
||||||
pub fn unit_key_opens(unit: &[u8], unit_key: &[u8; 16], format: crate::disc::ContentFormat) -> bool {
|
pub fn unit_key_opens(
|
||||||
|
unit: &[u8],
|
||||||
|
unit_key: &[u8; 16],
|
||||||
|
format: crate::disc::ContentFormat,
|
||||||
|
) -> bool {
|
||||||
use crate::disc::ContentFormat;
|
use crate::disc::ContentFormat;
|
||||||
if matches!(format, ContentFormat::BdTs) && unit.len() == ALIGNED_UNIT_LEN {
|
if matches!(format, ContentFormat::BdTs) && unit.len() == ALIGNED_UNIT_LEN {
|
||||||
return ts_unit_key_opens(unit, unit_key);
|
return ts_unit_key_opens(unit, unit_key);
|
||||||
@@ -200,16 +199,13 @@ pub fn unit_key_opens(unit: &[u8], unit_key: &[u8; 16], format: crate::disc::Con
|
|||||||
is_clean(&scratch, format)
|
is_clean(&scratch, format)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// BD-TS fast path of [`unit_key_opens`]: the SAME verdict as `decrypt_unit` +
|
/// BD-TS fast path of `unit_key_opens`: the same verdict as `decrypt_unit` +
|
||||||
/// `is_clean(BdTs)`, from one AES block per source packet instead of the whole
|
/// `is_clean(BdTs)` from one AES block per source packet. CBC decrypts any block alone
|
||||||
/// 383-block CBC chain — the hot path of a 100k-candidate brute. CBC decrypts
|
/// (`P[i] = AES_dec(C[i]) ⊕ C[i-1]`), and packet heads sit at block-aligned offsets ≥ 192.
|
||||||
/// any block alone (`P[i] = AES_dec(C[i]) ⊕ C[i-1]`); packet heads sit at 192-byte
|
/// Packet 0 is skipped (its sync byte is in the clear seed), as are all-zero padding
|
||||||
/// (block-aligned) offsets ≥ 192, so `C[i-1]` is always real ciphertext. Packet 0
|
/// packets, which `decrypt_unit` zeroes. Verdict mirrors [`is_clean_ts`]:
|
||||||
/// is skipped: its sync byte lives in the clear 16-byte seed and proves nothing.
|
/// `synced >= min(content, KEY_PROOF_PACKETS)`. `dead_code` is allowed so the
|
||||||
/// `decrypt_unit` zeroes all-zero-ciphertext (padding) packets, so those are
|
/// equivalence test covers it without `keyproof`.
|
||||||
/// skipped here identically. Verdict mirrors [`is_clean_ts`]:
|
|
||||||
/// `synced >= min(content, KEY_PROOF_PACKETS)`. `#[allow(dead_code)]` so the
|
|
||||||
/// equivalence test covers it even in a build without `keyproof`.
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn ts_unit_key_opens(unit: &[u8], unit_key: &[u8; 16]) -> bool {
|
fn ts_unit_key_opens(unit: &[u8], unit_key: &[u8; 16]) -> bool {
|
||||||
use aes::cipher::{Array, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
|
use aes::cipher::{Array, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
|
||||||
@@ -217,7 +213,8 @@ fn ts_unit_key_opens(unit: &[u8], unit_key: &[u8; 16]) -> bool {
|
|||||||
const PKT: usize = BD_SOURCE_PACKET_BYTES; // 192
|
const PKT: usize = BD_SOURCE_PACKET_BYTES; // 192
|
||||||
const NPKT: usize = ALIGNED_UNIT_LEN / PKT;
|
const NPKT: usize = ALIGNED_UNIT_LEN / PKT;
|
||||||
debug_assert_eq!(unit.len(), ALIGNED_UNIT_LEN);
|
debug_assert_eq!(unit.len(), ALIGNED_UNIT_LEN);
|
||||||
let block_at = |o: usize| -> Block { Array::try_from(&unit[o..o + 16]).expect("16-byte block") };
|
let block_at =
|
||||||
|
|o: usize| -> Block { Array::try_from(&unit[o..o + 16]).expect("16-byte block") };
|
||||||
|
|
||||||
// Block key = AES-128E(unit_key, seed) ⊕ seed — `decrypt_unit`'s derivation.
|
// Block key = AES-128E(unit_key, seed) ⊕ seed — `decrypt_unit`'s derivation.
|
||||||
let mut bk = block_at(0);
|
let mut bk = block_at(0);
|
||||||
|
|||||||
@@ -1255,11 +1255,8 @@ fn ts_packet_total_for_various_lengths() {
|
|||||||
assert_eq!(ts_packet_total(&[0u8; ALIGNED_UNIT_LEN]), 32);
|
assert_eq!(ts_packet_total(&[0u8; ALIGNED_UNIT_LEN]), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The decrypt-less BD-TS fast path `ts_unit_key_opens` (the brute's hot reject,
|
// The decrypt-less BD-TS path `ts_unit_key_opens` returns the same verdict as
|
||||||
// moved down from kdb) must return the EXACT same verdict as the full
|
// `decrypt_unit` + `is_clean(BdTs)` for the right key and wrong keys alike.
|
||||||
// `decrypt_unit` + `is_clean(BdTs)`, for the RIGHT key and for wrong keys alike —
|
|
||||||
// or `unit_key_opens` would silently disagree with a real decrypt. This pins the
|
|
||||||
// two together so a change to either side can't desync them.
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ts_unit_key_opens_matches_decrypt_unit_then_is_clean() {
|
fn ts_unit_key_opens_matches_decrypt_unit_then_is_clean() {
|
||||||
let fmt = crate::disc::ContentFormat::BdTs;
|
let fmt = crate::disc::ContentFormat::BdTs;
|
||||||
@@ -1284,7 +1281,10 @@ fn ts_unit_key_opens_matches_decrypt_unit_then_is_clean() {
|
|||||||
is_clean(&u, fmt)
|
is_clean(&u, fmt)
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(full(&right), "sanity: the right key opens via the full path");
|
assert!(
|
||||||
|
full(&right),
|
||||||
|
"sanity: the right key opens via the full path"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ts_unit_key_opens(&enc, &right),
|
ts_unit_key_opens(&enc, &right),
|
||||||
full(&right),
|
full(&right),
|
||||||
|
|||||||
Reference in New Issue
Block a user