Skip to main content

iso_bmff/isobmff/
cenc_box.rs

1//! Track encryption boxes (`tenc` / `senc`) — parse only (ISO/IEC 23001-7).
2
3#![forbid(unsafe_code)]
4
5use iso_cenc::Subsample;
6use smallvec::SmallVec;
7
8/// Default encryption parameters from `tenc`.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct TrackEncryption {
11    /// Non-zero when samples are protected by default.
12    pub is_protected: bool,
13    /// Per-sample IV size in bytes (0 ⇒ constant IV).
14    pub per_sample_iv_size: u8,
15    /// Key ID (16 bytes).
16    pub kid: [u8; 16],
17    /// Constant IV when `per_sample_iv_size == 0` (8 or 16 bytes).
18    pub constant_iv: SmallVec<[u8; 16]>,
19}
20
21/// One sample's IV + optional subsample map from `senc`.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct SencSample {
24    /// Per-sample IV bytes (`per_sample_iv_size` long), or empty if constant IV.
25    pub iv: SmallVec<[u8; 16]>,
26    /// Empty ⇒ whole sample protected.
27    pub subsamples: SmallVec<[Subsample; 4]>,
28}
29
30/// Parse `tenc` `FullBox` payload.
31#[must_use]
32pub fn parse_tenc(body: &[u8]) -> Option<TrackEncryption> {
33    if body.len() < 20 {
34        return None;
35    }
36    let mut pos = 4;
37    // `body[0]` is FullBox version; v1 crypt/skip nibbles are not parsed yet.
38    pos += 1;
39    if pos + 18 > body.len() {
40        return None;
41    }
42    let is_protected = body[pos] != 0;
43    let per_sample_iv_size = body[pos + 1];
44    let mut kid = [0u8; 16];
45    kid.copy_from_slice(&body[pos + 2..pos + 18]);
46    pos += 18;
47    let mut constant_iv = SmallVec::new();
48    if is_protected && per_sample_iv_size == 0 {
49        if pos >= body.len() {
50            return None;
51        }
52        let iv_size = body[pos] as usize;
53        pos += 1;
54        if pos + iv_size > body.len() || (iv_size != 8 && iv_size != 16) {
55            return None;
56        }
57        constant_iv.extend_from_slice(&body[pos..pos + iv_size]);
58    }
59    Some(TrackEncryption {
60        is_protected,
61        per_sample_iv_size,
62        kid,
63        constant_iv,
64    })
65}
66
67/// Parse `senc` `FullBox` payload given the track's per-sample IV size.
68#[must_use]
69pub fn parse_senc(body: &[u8], per_sample_iv_size: u8) -> Vec<SencSample> {
70    if body.len() < 8 {
71        return Vec::new();
72    }
73    let flags = u32::from_be_bytes([body[0], body[1], body[2], body[3]]);
74    let use_subsamples = flags & 0x0000_0002 != 0;
75    let count = u32::from_be_bytes([body[4], body[5], body[6], body[7]]) as usize;
76    let iv_size = usize::from(per_sample_iv_size);
77    let mut out = Vec::with_capacity(count);
78    let mut pos = 8;
79    for _ in 0..count {
80        let mut iv = SmallVec::new();
81        if iv_size > 0 {
82            if pos + iv_size > body.len() {
83                break;
84            }
85            iv.extend_from_slice(&body[pos..pos + iv_size]);
86            pos += iv_size;
87        }
88        let mut subsamples = SmallVec::new();
89        if use_subsamples {
90            if pos + 2 > body.len() {
91                break;
92            }
93            let n = u16::from_be_bytes([body[pos], body[pos + 1]]) as usize;
94            pos += 2;
95            for _ in 0..n {
96                if pos + 6 > body.len() {
97                    return out;
98                }
99                let clear = u16::from_be_bytes([body[pos], body[pos + 1]]);
100                let protected = u32::from_be_bytes([
101                    body[pos + 2],
102                    body[pos + 3],
103                    body[pos + 4],
104                    body[pos + 5],
105                ]);
106                subsamples.push(Subsample {
107                    clear_bytes: clear,
108                    protected_bytes: protected,
109                });
110                pos += 6;
111            }
112        }
113        out.push(SencSample { iv, subsamples });
114    }
115    out
116}