1use crate::{
2 Mp4Parser,
3 boxes::{SchmBox, SencBox, TencBox, TrunBox},
4 data,
5 decrypt::{cipher::CencProcessor, reader::Mp4Reader},
6 error::{Error, Result},
7 parser,
8};
9use std::io::{Read, Write};
10
11#[derive(Clone, Default)]
12struct Tenc {
13 scheme_type: u32,
14 per_sample_iv_size: u8,
15 constant_iv: Option<[u8; 16]>,
16 crypt_byte_block: u8,
17 skip_byte_block: u8,
18}
19
20#[derive(Clone)]
25pub struct CencDecrypter {
26 key: [u8; 16],
27 tenc: Option<Tenc>,
28}
29
30impl CencDecrypter {
31 pub fn new(key: &str) -> Result<Self> {
39 Ok(Self {
40 key: hex::decode(key.to_ascii_lowercase().replace('-', ""))?
41 .try_into()
42 .map_err(|v: Vec<u8>| Error::InvalidKeySize(v.len()))?,
43 tenc: None,
44 })
45 }
46
47 pub fn with_init(key: &str, init: &[u8]) -> Result<Self> {
56 let mut decrypter = Self::new(key)?;
57 decrypter.tenc = Some(Self::parse_init(init)?);
58 Ok(decrypter)
59 }
60
61 fn parse_init(init: &[u8]) -> Result<Tenc> {
62 let tenc = data!(Tenc::default());
63
64 Mp4Parser::new()
65 .base_box("enca", parser::audio_sample_entry)
66 .base_box("encv", parser::visual_sample_entry)
67 .base_box("mdia", parser::children)
68 .base_box("minf", parser::children)
69 .base_box("moov", parser::children)
70 .base_box("schi", parser::children)
71 .base_box("sinf", parser::children)
72 .base_box("stbl", parser::children)
73 .full_box("stsd", parser::sample_description)
74 .base_box("trak", parser::children)
75 .full_box("schm", {
76 let tenc = tenc.clone();
77 move |mut box_| {
78 tenc.borrow_mut().scheme_type = SchmBox::new(&mut box_)?.scheme_type;
79 Ok(())
80 }
81 })
82 .full_box("tenc", {
83 let tenc = tenc.clone();
84 move |mut box_| {
85 let tenc_box = TencBox::new(&mut box_)?;
86 let t = &mut *tenc.borrow_mut();
87 t.per_sample_iv_size = tenc_box.per_sample_iv_size;
88 t.constant_iv = tenc_box.constant_iv;
89 t.crypt_byte_block = tenc_box.crypt_byte_block;
90 t.skip_byte_block = tenc_box.skip_byte_block;
91 box_.parser.stop();
92 Ok(())
93 }
94 })
95 .parse(init, true, true)?;
96
97 Ok(tenc.take())
98 }
99
100 pub fn decrypt_fragment(&self, mut input: Vec<u8>, init: Option<&[u8]>) -> Result<Vec<u8>> {
120 if input.is_empty() {
121 return Ok(input);
122 }
123
124 let tenc_own;
125 let tenc = if let Some(init) = init {
126 tenc_own = Self::parse_init(init)?;
127 &tenc_own
128 } else if let Some(cached) = &self.tenc {
129 cached
130 } else {
131 tenc_own = Self::parse_init(&input)?;
132 &tenc_own
133 };
134
135 if tenc.scheme_type == 0 {
136 return Ok(input);
137 }
138
139 #[derive(Default)]
140 struct State {
141 start: u64,
142 senc: Option<SencBox>,
143 trun: Option<TrunBox>,
144 }
145 let state = data!(State::default());
146 let iv_size = tenc.per_sample_iv_size;
147 let constant_iv = tenc.constant_iv;
148
149 Mp4Parser::new()
150 .base_box("traf", parser::children)
151 .base_box("moof", {
152 let state = state.clone();
153 move |box_| {
154 state.borrow_mut().start = box_.start;
155 parser::children(box_)
156 }
157 })
158 .full_box("senc", {
159 let state = state.clone();
160 move |mut box_| {
161 state.borrow_mut().senc =
162 Some(SencBox::new(&mut box_, iv_size, constant_iv.as_ref())?);
163 Ok(())
164 }
165 })
166 .full_box("trun", {
167 let state = state.clone();
168 move |mut box_| {
169 state.borrow_mut().trun = Some(TrunBox::new(&mut box_)?);
170 Ok(())
171 }
172 })
173 .parse(&input, true, true)?;
174
175 let state = state.take();
176 let (Some(trun), Some(senc)) = (state.trun, state.senc) else {
177 return Ok(input);
178 };
179 let mut processor = CencProcessor::new(
180 &self.key,
181 tenc.crypt_byte_block,
182 tenc.skip_byte_block,
183 tenc.scheme_type,
184 );
185 let mut offset = (state.start + trun.data_offset.unwrap_or(0) as u64) as usize;
186 let output_len = input.len();
187
188 for (trun_sample, senc_sample) in trun.sample_data.iter().zip(senc.samples.iter()) {
189 let size = trun_sample.sample_size.unwrap_or_default() as usize;
190 if size == 0 {
191 continue;
192 }
193
194 let end = offset + size;
195 if end > output_len {
196 break;
197 }
198
199 processor.decrypt_sample_inplace(&mut input[offset..end], senc_sample);
200 offset = end;
201 }
202
203 Ok(input)
204 }
205
206 pub fn decrypt_stream<R: Read, W: Write>(
224 &mut self,
225 reader: &mut R,
226 writer: &mut W,
227 init: Option<&[u8]>,
228 ) -> Result<()> {
229 let mut next = if let Some(init) = init {
230 self.tenc = Some(Self::parse_init(init)?);
231 Mp4Reader::header(reader)?
232 } else {
233 let (init, moof) = Mp4Reader::init(reader)?;
234 writer.write_all(&init)?;
235
236 if moof.is_none() {
237 std::io::copy(reader, writer)?;
238 return Ok(());
239 }
240
241 self.tenc = Some(Self::parse_init(&init)?);
242 moof
243 };
244
245 while let Some(header) = next {
246 if &header.box_type == b"moof" {
247 let mut fragment = header.data(reader)?;
248
249 while let Some(next) = Mp4Reader::header(reader)? {
250 fragment.append(&mut next.data(reader)?);
251
252 if &next.box_type == b"mdat" {
253 break;
254 }
255 }
256
257 let decrypted = self.decrypt_fragment(fragment, None)?;
258 writer.write_all(&decrypted)?;
259 } else {
260 writer.write_all(&header.data(reader)?)?;
261 }
262
263 next = Mp4Reader::header(reader)?;
264 }
265
266 writer.flush()?;
267 Ok(())
268 }
269}