#![cfg(feature = "sample-aes")]
use transmux::sample_aes::{
self, BLOCK_LEN, ExtXKey, H264_CLEAR_PREFIX_LEN, H264_MIN_ENCRYPTED_NAL_LEN, KEY_LEN,
aac_decrypt_frame, aac_encrypt_frame, ac3_decrypt_frame, ac3_encrypt_frame,
aes128_decrypt_segment, aes128_encrypt_segment, format_iv, h264_decrypt_nal, h264_encrypt_nal,
iv_from_sequence_number,
};
const KEY: [u8; KEY_LEN] = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
];
const IV: [u8; BLOCK_LEN] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
];
#[test]
fn aes128_cbc_known_answer_vector() {
const PT: [u8; 16] = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17,
0x2a,
];
const CT: [u8; 16] = [
0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19,
0x7d,
];
let ct = aes128_encrypt_segment(&KEY, &IV, &PT);
assert_eq!(ct.len(), 32, "one block + one full pad block");
assert_eq!(&ct[..16], &CT, "first ciphertext block != NIST vector");
let pt = aes128_decrypt_segment(&KEY, &IV, &ct).unwrap();
assert_eq!(pt, PT, "KAT decrypt did not recover plaintext");
}
#[test]
fn h264_sample_pattern_and_round_trip() {
let mut nal = vec![0x65u8];
nal.extend(core::iter::repeat_n(0xAAu8, 199));
assert_eq!(nal.len(), 200);
let enc = h264_encrypt_nal(&KEY, &IV, &nal);
assert_eq!(&enc[..H264_CLEAR_PREFIX_LEN], &nal[..H264_CLEAR_PREFIX_LEN]);
assert_ne!(&enc[32..48], &nal[32..48], "block1 must be encrypted");
assert_eq!(&enc[48..192], &nal[48..192], "skip region must be clear");
assert_eq!(&enc[192..200], &nal[192..200], "trailing <16 clear");
let dec = h264_decrypt_nal(&KEY, &IV, &enc);
assert_eq!(dec, nal, "H.264 round-trip mismatch");
let mut short = vec![0x65u8];
short.extend((0u8..40).map(|i| i.wrapping_mul(9)));
assert!(short.len() <= H264_MIN_ENCRYPTED_NAL_LEN);
assert_eq!(
h264_encrypt_nal(&KEY, &IV, &short),
short,
"short NAL untouched"
);
let mut sps = vec![0x67u8];
sps.extend(core::iter::repeat_n(0x11u8, 80));
assert_eq!(h264_encrypt_nal(&KEY, &IV, &sps), sps, "SPS untouched");
let mut emu = vec![0x65u8];
for _ in 0..30 {
emu.extend_from_slice(&[0xAA, 0x00, 0x00, 0x03, 0x01, 0x9C]);
}
assert!(emu.len() > H264_MIN_ENCRYPTED_NAL_LEN);
let enc_emu = h264_encrypt_nal(&KEY, &IV, &emu);
let dec_emu = h264_decrypt_nal(&KEY, &IV, &enc_emu);
assert_eq!(dec_emu, emu, "emulation-prevention NAL round-trip mismatch");
}
#[test]
fn aac_frame_round_trip_clear_leader() {
let mut frame = vec![0xFF, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00];
frame.extend((0u8..100).map(|i| i.wrapping_mul(3)));
let enc = aac_encrypt_frame(&KEY, &IV, &frame).unwrap();
assert_eq!(enc.len(), frame.len(), "AAC has no padding");
let clear = 7 + 16;
assert_eq!(&enc[..clear], &frame[..clear], "leader clear");
assert_ne!(&enc[clear..], &frame[clear..], "body encrypted");
let dec = aac_decrypt_frame(&KEY, &IV, &enc).unwrap();
assert_eq!(dec, frame, "AAC round-trip mismatch");
}
#[test]
fn ac3_frame_round_trip() {
let mut frame = vec![0x0B, 0x77]; frame.extend((0u8..90).map(|i| i.wrapping_add(5)));
let enc = ac3_encrypt_frame(&KEY, &IV, &frame);
assert_eq!(&enc[..16], &frame[..16], "16-byte leader clear");
assert_eq!(ac3_decrypt_frame(&KEY, &IV, &enc), frame);
}
#[test]
fn aes128_full_segment_padded_round_trip() {
let segment: Vec<u8> = (0u16..300).map(|i| i as u8).collect(); let ct = aes128_encrypt_segment(&KEY, &IV, &segment);
assert_eq!(ct.len() % BLOCK_LEN, 0, "ciphertext block-padded");
assert_eq!(ct.len(), 304, "300 → 304 (next multiple of 16)");
assert_ne!(&ct[..segment.len()], &segment[..], "actually encrypted");
let pt = aes128_decrypt_segment(&KEY, &IV, &ct).unwrap();
assert_eq!(pt, segment, "full-segment round-trip mismatch");
assert!(aes128_decrypt_segment(&KEY, &IV, &[0u8; 17]).is_err());
}
#[test]
fn ext_x_key_exact_strings() {
let sae = ExtXKey::fairplay_sample_aes("skd://asset-42");
assert_eq!(
sae.to_tag(),
"#EXT-X-KEY:METHOD=SAMPLE-AES,URI=\"skd://asset-42\",\
KEYFORMAT=\"com.apple.streamingkeydelivery\",KEYFORMATVERSIONS=\"1\""
);
let aes = ExtXKey::aes128(
"https://keyserver.example.com/key",
[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
],
);
assert_eq!(
aes.to_tag(),
"#EXT-X-KEY:METHOD=AES-128,URI=\"https://keyserver.example.com/key\",\
IV=0x00000000000000000000000000000001"
);
assert_eq!(format!("{aes}"), aes.to_tag());
let iv = iv_from_sequence_number(7);
assert_eq!(iv, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7]);
assert_eq!(format_iv(&iv), "0x00000000000000000000000000000007");
assert_eq!(
sample_aes::HlsEncryptionMethod::SampleAes.name(),
"SAMPLE-AES"
);
assert_eq!(sample_aes::HlsEncryptionMethod::Aes128.name(), "AES-128");
}