1 /*****************************************************************
2 |
3 | AP4 - Stream Cipher
4 |
5 | Copyright 2002-2008 Axiomatic Systems, LLC
6 |
7 |
8 | This file is part of Bento4/AP4 (MP4 Atom Processing Library).
9 |
10 | Unless you have obtained Bento4 under a difference license,
11 | this version of Bento4 is Bento4|GPL.
12 | Bento4|GPL is free software; you can redistribute it and/or modify
13 | it under the terms of the GNU General Public License as published by
14 | the Free Software Foundation; either version 2, or (at your option)
15 | any later version.
16 |
17 | Bento4|GPL is distributed in the hope that it will be useful,
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | GNU General Public License for more details.
21 |
22 | You should have received a copy of the GNU General Public License
23 | along with Bento4|GPL; see the file COPYING. If not, write to the
24 | Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 | 02111-1307, USA.
26 |
27 ****************************************************************/
28
29 /*----------------------------------------------------------------------
30 | includes
31 +---------------------------------------------------------------------*/
32 #include "Ap4StreamCipher.h"
33 #include "Ap4Utils.h"
34
35 /*----------------------------------------------------------------------
36 | AP4_CtrStreamCipher::AP4_CtrStreamCipher
37 +---------------------------------------------------------------------*/
AP4_CtrStreamCipher(AP4_BlockCipher * block_cipher,AP4_Size counter_size)38 AP4_CtrStreamCipher::AP4_CtrStreamCipher(AP4_BlockCipher* block_cipher,
39 AP4_Size counter_size) :
40 m_StreamOffset(0),
41 m_CounterSize(counter_size),
42 m_CacheValid(false),
43 m_BlockCipher(block_cipher)
44 {
45 if (m_CounterSize > 16) m_CounterSize = 16;
46
47 // reset the stream offset
48 AP4_SetMemory(m_IV, 0, AP4_CIPHER_BLOCK_SIZE);
49 SetStreamOffset(0);
50 SetIV(NULL);
51 }
52
53 /*----------------------------------------------------------------------
54 | AP4_CtrStreamCipher::~AP4_CtrStreamCipher
55 +---------------------------------------------------------------------*/
~AP4_CtrStreamCipher()56 AP4_CtrStreamCipher::~AP4_CtrStreamCipher()
57 {
58 delete m_BlockCipher;
59 }
60
61 /*----------------------------------------------------------------------
62 | AP4_CtrStreamCipher::SetIV
63 +---------------------------------------------------------------------*/
64 AP4_Result
SetIV(const AP4_UI08 * iv)65 AP4_CtrStreamCipher::SetIV(const AP4_UI08* iv)
66 {
67 if (iv) {
68 AP4_CopyMemory(m_IV, iv, AP4_CIPHER_BLOCK_SIZE);
69 } else {
70 AP4_SetMemory(m_IV, 0, AP4_CIPHER_BLOCK_SIZE);
71 }
72
73 // for the stream offset back to 0
74 m_CacheValid = false;
75 return SetStreamOffset(0);
76 }
77
78 /*----------------------------------------------------------------------
79 | AP4_CtrStreamCipher::SetStreamOffset
80 +---------------------------------------------------------------------*/
81 AP4_Result
SetStreamOffset(AP4_UI64 offset,AP4_Cardinal * preroll)82 AP4_CtrStreamCipher::SetStreamOffset(AP4_UI64 offset,
83 AP4_Cardinal* preroll)
84 {
85 // do nothing if we're already at that offset
86 if (offset == m_StreamOffset) return AP4_SUCCESS;
87
88 // update the offset
89 m_CacheValid = false;
90 m_StreamOffset = offset;
91
92 // no preroll in CTR mode
93 if (preroll != NULL) *preroll = 0;
94
95 return AP4_SUCCESS;
96 }
97
98 /*----------------------------------------------------------------------
99 | AP4_CtrStreamCipher::ComputeCounter
100 +---------------------------------------------------------------------*/
101 void
ComputeCounter(AP4_UI64 stream_offset,AP4_UI08 counter_block[AP4_CIPHER_BLOCK_SIZE])102 AP4_CtrStreamCipher::ComputeCounter(AP4_UI64 stream_offset,
103 AP4_UI08 counter_block[AP4_CIPHER_BLOCK_SIZE])
104 {
105 // setup counter offset bytes
106 AP4_UI64 counter_offset = stream_offset/AP4_CIPHER_BLOCK_SIZE;
107 AP4_UI08 counter_offset_bytes[8];
108 AP4_BytesFromUInt64BE(counter_offset_bytes, counter_offset);
109
110 // compute the new counter
111 unsigned int carry = 0;
112 for (unsigned int i=0; i<m_CounterSize; i++) {
113 unsigned int o = AP4_CIPHER_BLOCK_SIZE-1-i;
114 unsigned int x = m_IV[o];
115 unsigned int y = (i<8)?counter_offset_bytes[7-i]:0;
116 unsigned int sum = x+y+carry;
117 counter_block[o] = (AP4_UI08)(sum&0xFF);
118 carry = ((sum >= 0x100)?1:0);
119 }
120 for (unsigned int i=m_CounterSize; i<AP4_CIPHER_BLOCK_SIZE; i++) {
121 unsigned int o = AP4_CIPHER_BLOCK_SIZE-1-i;
122 counter_block[o] = m_IV[o];
123 }
124 }
125
126 /*----------------------------------------------------------------------
127 | AP4_CtrStreamCipher::ProcessBuffer
128 +---------------------------------------------------------------------*/
129 AP4_Result
ProcessBuffer(const AP4_UI08 * in,AP4_Size in_size,AP4_UI08 * out,AP4_Size * out_size,bool)130 AP4_CtrStreamCipher::ProcessBuffer(const AP4_UI08* in,
131 AP4_Size in_size,
132 AP4_UI08* out,
133 AP4_Size* out_size /* = NULL */,
134 bool /* is_last_buffer */)
135 {
136 if (m_BlockCipher == NULL) return AP4_ERROR_INVALID_STATE;
137
138 if (out_size != NULL && *out_size < in_size) {
139 *out_size = in_size;
140 return AP4_ERROR_BUFFER_TOO_SMALL;
141 }
142
143 // in CTR mode, the output is the same size as the input
144 if (out_size != NULL) *out_size = in_size;
145
146 // deal with inputs not aligned on block boundaries
147 if (m_StreamOffset%AP4_CIPHER_BLOCK_SIZE) {
148 unsigned int cache_offset = (unsigned int)(m_StreamOffset%AP4_CIPHER_BLOCK_SIZE);
149 if (!m_CacheValid) {
150 // fill the cache
151 AP4_UI08 block[AP4_CIPHER_BLOCK_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
152 AP4_UI08 counter_block[AP4_CIPHER_BLOCK_SIZE];
153 ComputeCounter(m_StreamOffset-cache_offset, counter_block);
154 AP4_Result result = m_BlockCipher->Process(block, AP4_CIPHER_BLOCK_SIZE, m_CacheBlock, counter_block);
155 if (AP4_FAILED(result)) {
156 if (out_size) *out_size = 0;
157 return result;
158 }
159 m_CacheValid = true;
160 }
161 unsigned int partial = AP4_CIPHER_BLOCK_SIZE-cache_offset;
162 if (partial > in_size) partial = in_size;
163 for (unsigned int i=0; i<partial; i++) {
164 out[i] = in[i]^m_CacheBlock[i+cache_offset];
165 }
166
167 // advance to the end of the partial block
168 m_StreamOffset += partial;
169 in += partial;
170 out += partial;
171 in_size -= partial;
172 }
173
174 // process all the remaining bytes in the buffer
175 if (in_size) {
176 // the cache won't be valid anymore
177 m_CacheValid = false;
178
179 // compute the counter
180 AP4_UI08 counter_block[AP4_CIPHER_BLOCK_SIZE];
181 ComputeCounter(m_StreamOffset, counter_block);
182
183 // process the data
184 AP4_Result result = m_BlockCipher->Process(in, in_size, out, counter_block);
185 if (AP4_FAILED(result)) {
186 if (out_size) *out_size = 0;
187 return result;
188 }
189 m_StreamOffset += in_size;
190 return result;
191 }
192
193 return AP4_SUCCESS;
194 }
195
196 /*----------------------------------------------------------------------
197 | AP4_CbcStreamCipher::AP4_CbcStreamCipher
198 +---------------------------------------------------------------------*/
AP4_CbcStreamCipher(AP4_BlockCipher * block_cipher)199 AP4_CbcStreamCipher::AP4_CbcStreamCipher(AP4_BlockCipher* block_cipher) :
200 m_StreamOffset(0),
201 m_OutputSkip(0),
202 m_InBlockFullness(0),
203 m_ChainBlockFullness(AP4_CIPHER_BLOCK_SIZE),
204 m_BlockCipher(block_cipher),
205 m_Eos(false)
206 {
207 AP4_SetMemory(m_Iv, 0, AP4_CIPHER_BLOCK_SIZE);
208 AP4_SetMemory(m_ChainBlock, 0, AP4_CIPHER_BLOCK_SIZE);
209 }
210
211 /*----------------------------------------------------------------------
212 | AP4_CbcStreamCipher::~AP4_CbcStreamCipher
213 +---------------------------------------------------------------------*/
~AP4_CbcStreamCipher()214 AP4_CbcStreamCipher::~AP4_CbcStreamCipher()
215 {
216 delete m_BlockCipher;
217 }
218
219 /*----------------------------------------------------------------------
220 | AP4_CbcStreamCipher::SetIV
221 +---------------------------------------------------------------------*/
222 AP4_Result
SetIV(const AP4_UI08 * iv)223 AP4_CbcStreamCipher::SetIV(const AP4_UI08* iv)
224 {
225 AP4_CopyMemory(m_Iv, iv, AP4_CIPHER_BLOCK_SIZE);
226 m_StreamOffset = 0;
227 m_Eos = false;
228 AP4_CopyMemory(m_ChainBlock, m_Iv, AP4_CIPHER_BLOCK_SIZE);
229 m_ChainBlockFullness = AP4_CIPHER_BLOCK_SIZE;
230 m_InBlockFullness = 0;
231 m_OutputSkip = 0;
232 return AP4_SUCCESS;
233 }
234
235 /*----------------------------------------------------------------------
236 | AP4_CbcStreamCipher::SetStreamOffset
237 +---------------------------------------------------------------------*/
238 AP4_Result
SetStreamOffset(AP4_UI64 offset,AP4_Cardinal * preroll)239 AP4_CbcStreamCipher::SetStreamOffset(AP4_UI64 offset,
240 AP4_Cardinal* preroll)
241 {
242 // does not make sense for encryption
243 if (m_BlockCipher->GetDirection() == AP4_BlockCipher::ENCRYPT) {
244 return AP4_ERROR_NOT_SUPPORTED;
245 }
246
247 // check params
248 if (preroll == NULL) return AP4_ERROR_INVALID_PARAMETERS;
249
250 // reset the end of stream flag
251 m_Eos = false;
252
253 // invalidate the chain block
254 m_ChainBlockFullness = 0;
255
256 // flush cached data
257 m_InBlockFullness = 0;
258
259 // compute the preroll
260 if (offset < AP4_CIPHER_BLOCK_SIZE) {
261 AP4_CopyMemory(m_ChainBlock, m_Iv, AP4_CIPHER_BLOCK_SIZE);
262 m_ChainBlockFullness = AP4_CIPHER_BLOCK_SIZE;
263 *preroll = (AP4_Cardinal)offset;
264 } else {
265 *preroll = (AP4_Cardinal) ((offset%AP4_CIPHER_BLOCK_SIZE) + AP4_CIPHER_BLOCK_SIZE);
266 }
267
268 m_StreamOffset = offset-*preroll;
269 m_OutputSkip = (AP4_Size)(offset%AP4_CIPHER_BLOCK_SIZE);
270 return AP4_SUCCESS;
271 }
272
273
274 /*----------------------------------------------------------------------
275 | AP4_CbcStreamCipher::EncryptBuffer
276 +---------------------------------------------------------------------*/
277 AP4_Result
EncryptBuffer(const AP4_UI08 * in,AP4_Size in_size,AP4_UI08 * out,AP4_Size * out_size,bool is_last_buffer)278 AP4_CbcStreamCipher::EncryptBuffer(const AP4_UI08* in,
279 AP4_Size in_size,
280 AP4_UI08* out,
281 AP4_Size* out_size,
282 bool is_last_buffer)
283 {
284 // we don't do much checking here because this method is only called
285 // from ProcessBuffer(), which does all the parameter checking
286
287 // compute how many blocks we span
288 AP4_UI64 start_block = (m_StreamOffset-m_InBlockFullness)/AP4_CIPHER_BLOCK_SIZE;
289 AP4_UI64 end_block = (m_StreamOffset+in_size)/AP4_CIPHER_BLOCK_SIZE;
290 AP4_UI32 blocks_needed = (AP4_UI32)(end_block-start_block);
291
292 // compute how many blocks we will need to produce
293 if (is_last_buffer) {
294 ++blocks_needed;
295 }
296 if (*out_size < blocks_needed*AP4_CIPHER_BLOCK_SIZE) {
297 *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE;
298 return AP4_ERROR_BUFFER_TOO_SMALL;
299 }
300 *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE;
301
302 // finish any incomplete block from a previous call
303 unsigned int offset = (unsigned int)(m_StreamOffset%AP4_CIPHER_BLOCK_SIZE);
304 AP4_ASSERT(m_InBlockFullness == offset);
305 if (offset) {
306 unsigned int chunk = AP4_CIPHER_BLOCK_SIZE-offset;
307 if (chunk > in_size) chunk = in_size;
308 for (unsigned int x=0; x<chunk; x++) {
309 m_InBlock[x+offset] = in[x];
310 }
311 in += chunk;
312 in_size -= chunk;
313 m_StreamOffset += chunk;
314 m_InBlockFullness += chunk;
315 if (offset+chunk == AP4_CIPHER_BLOCK_SIZE) {
316 // we have filled the input block, encrypt it
317 AP4_Result result = m_BlockCipher->Process(m_InBlock, AP4_CIPHER_BLOCK_SIZE, out, m_ChainBlock);
318 AP4_CopyMemory(m_ChainBlock, out, AP4_CIPHER_BLOCK_SIZE);
319 m_InBlockFullness = 0;
320 if (AP4_FAILED(result)) {
321 *out_size = 0;
322 return result;
323 }
324 out += AP4_CIPHER_BLOCK_SIZE;
325 }
326 }
327
328 // encrypt the whole blocks
329 unsigned int block_count = in_size/AP4_CIPHER_BLOCK_SIZE;
330 if (block_count) {
331 AP4_ASSERT(m_InBlockFullness == 0);
332 AP4_UI32 blocks_size = block_count*AP4_CIPHER_BLOCK_SIZE;
333 AP4_Result result = m_BlockCipher->Process(in, blocks_size, out, m_ChainBlock);
334 AP4_CopyMemory(m_ChainBlock, out+blocks_size-AP4_CIPHER_BLOCK_SIZE, AP4_CIPHER_BLOCK_SIZE);
335 if (AP4_FAILED(result)) {
336 *out_size = 0;
337 return result;
338 }
339 in += blocks_size;
340 out += blocks_size;
341 in_size -= blocks_size;
342 m_StreamOffset += blocks_size;
343 }
344
345 // deal with what's left
346 if (in_size) {
347 AP4_ASSERT(in_size < AP4_CIPHER_BLOCK_SIZE);
348 for (unsigned int x=0; x<in_size; x++) {
349 m_InBlock[x+m_InBlockFullness] = in[x];
350 }
351 m_InBlockFullness += in_size;
352 m_StreamOffset += in_size;
353 }
354
355 // pad if needed
356 if (is_last_buffer) {
357 AP4_ASSERT(m_InBlockFullness == m_StreamOffset%AP4_CIPHER_BLOCK_SIZE);
358 AP4_UI08 pad_byte = AP4_CIPHER_BLOCK_SIZE-(AP4_UI08)(m_StreamOffset%AP4_CIPHER_BLOCK_SIZE);
359 for (unsigned int x=AP4_CIPHER_BLOCK_SIZE-pad_byte; x<AP4_CIPHER_BLOCK_SIZE; x++) {
360 m_InBlock[x] = pad_byte;
361 }
362 AP4_Result result = m_BlockCipher->Process(m_InBlock, AP4_CIPHER_BLOCK_SIZE, out, m_ChainBlock);
363 AP4_CopyMemory(m_ChainBlock, out, AP4_CIPHER_BLOCK_SIZE);
364 m_InBlockFullness = 0;
365 if (AP4_FAILED(result)) {
366 *out_size = 0;
367 return result;
368 }
369 }
370
371 return AP4_SUCCESS;
372 }
373
374 /*----------------------------------------------------------------------
375 | AP4_CbcStreamCipher::DecryptBuffer
376 +---------------------------------------------------------------------*/
377 AP4_Result
DecryptBuffer(const AP4_UI08 * in,AP4_Size in_size,AP4_UI08 * out,AP4_Size * out_size,bool is_last_buffer)378 AP4_CbcStreamCipher::DecryptBuffer(const AP4_UI08* in,
379 AP4_Size in_size,
380 AP4_UI08* out,
381 AP4_Size* out_size,
382 bool is_last_buffer)
383 {
384 // we don't do much checking here because this method is only called
385 // from ProcessBuffer(), which does all the parameter checking
386
387 // deal chain-block buffering
388 if (m_ChainBlockFullness != AP4_CIPHER_BLOCK_SIZE) {
389 unsigned int needed = AP4_CIPHER_BLOCK_SIZE-m_ChainBlockFullness;
390 unsigned int chunk = (in_size > needed) ? needed : in_size;
391 AP4_CopyMemory(&m_ChainBlock[m_ChainBlockFullness], in, chunk);
392 in_size -= chunk;
393 in += chunk;
394 m_ChainBlockFullness += chunk;
395 m_StreamOffset += chunk;
396 if (m_ChainBlockFullness != AP4_CIPHER_BLOCK_SIZE) {
397 // not enough to continue
398 *out_size = 0;
399 return AP4_SUCCESS;
400 }
401 }
402 AP4_ASSERT(m_ChainBlockFullness == AP4_CIPHER_BLOCK_SIZE);
403
404 // compute how many blocks we span
405 AP4_UI64 start_block = (m_StreamOffset-m_InBlockFullness)/AP4_CIPHER_BLOCK_SIZE;
406 AP4_UI64 end_block = (m_StreamOffset+in_size)/AP4_CIPHER_BLOCK_SIZE;
407 AP4_UI32 blocks_needed = (AP4_UI32)(end_block-start_block);
408
409 // compute how many blocks we will need to produce
410 if (*out_size < blocks_needed*AP4_CIPHER_BLOCK_SIZE) {
411 *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE;
412 return AP4_ERROR_BUFFER_TOO_SMALL;
413 }
414 *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE;
415 if (blocks_needed && m_OutputSkip) *out_size -= m_OutputSkip;
416
417 // shortcut
418 if (in_size == 0) return AP4_SUCCESS;
419
420 // deal with in-block buffering
421 // NOTE: if we have bytes to skip on output, always use the in-block buffer for
422 // the first block, even if we're aligned, this makes the code simpler
423 AP4_ASSERT(m_InBlockFullness < AP4_CIPHER_BLOCK_SIZE);
424 if (m_OutputSkip || m_InBlockFullness) {
425 unsigned int needed = AP4_CIPHER_BLOCK_SIZE-m_InBlockFullness;
426 unsigned int chunk = (in_size > needed) ? needed : in_size;
427 AP4_CopyMemory(&m_InBlock[m_InBlockFullness], in, chunk);
428 in_size -= chunk;
429 in += chunk;
430 m_InBlockFullness += chunk;
431 m_StreamOffset += chunk;
432 if (m_InBlockFullness != AP4_CIPHER_BLOCK_SIZE) {
433 // not enough to continue
434 *out_size = 0;
435 return AP4_SUCCESS;
436 }
437 AP4_UI08 out_block[AP4_CIPHER_BLOCK_SIZE];
438 AP4_Result result = m_BlockCipher->Process(m_InBlock, AP4_CIPHER_BLOCK_SIZE, out_block, m_ChainBlock);
439 m_InBlockFullness = 0;
440 if (AP4_FAILED(result)) {
441 *out_size = 0;
442 return result;
443 }
444 AP4_CopyMemory(m_ChainBlock, m_InBlock, AP4_CIPHER_BLOCK_SIZE);
445 if (m_OutputSkip) {
446 AP4_ASSERT(m_OutputSkip < AP4_CIPHER_BLOCK_SIZE);
447 AP4_CopyMemory(out, &out_block[m_OutputSkip], AP4_CIPHER_BLOCK_SIZE-m_OutputSkip);
448 out += AP4_CIPHER_BLOCK_SIZE-m_OutputSkip;
449 m_OutputSkip = 0;
450 } else {
451 AP4_CopyMemory(out, out_block, AP4_CIPHER_BLOCK_SIZE);
452 out += AP4_CIPHER_BLOCK_SIZE;
453 }
454 }
455 AP4_ASSERT(m_InBlockFullness == 0);
456 AP4_ASSERT(m_OutputSkip == 0);
457
458 // process full blocks
459 unsigned int block_count = in_size/AP4_CIPHER_BLOCK_SIZE;
460 if (block_count) {
461 AP4_UI32 blocks_size = block_count*AP4_CIPHER_BLOCK_SIZE;
462 AP4_Result result = m_BlockCipher->Process(in, blocks_size, out, m_ChainBlock);
463 AP4_CopyMemory(m_ChainBlock, in+blocks_size-AP4_CIPHER_BLOCK_SIZE, AP4_CIPHER_BLOCK_SIZE);
464 if (AP4_FAILED(result)) {
465 *out_size = 0;
466 return result;
467 }
468 in += blocks_size;
469 out += blocks_size;
470 in_size -= blocks_size;
471 m_StreamOffset += blocks_size;
472 }
473
474 // buffer partial block leftovers
475 if (in_size) {
476 AP4_ASSERT(in_size < AP4_CIPHER_BLOCK_SIZE);
477 AP4_CopyMemory(m_InBlock, in, in_size);
478 m_InBlockFullness = in_size;
479 m_StreamOffset += in_size;
480 }
481
482 // deal with padding
483 if (is_last_buffer) {
484 AP4_UI08 pad_byte = *(out-1);
485 if (pad_byte > AP4_CIPHER_BLOCK_SIZE ||
486 *out_size < pad_byte) {
487 *out_size = 0;
488 return AP4_ERROR_INVALID_FORMAT;
489 }
490 *out_size -= pad_byte;
491 }
492
493 return AP4_SUCCESS;
494 }
495
496 /*----------------------------------------------------------------------
497 | AP4_CbcStreamCipher::ProcessBuffer
498 +---------------------------------------------------------------------*/
499 AP4_Result
ProcessBuffer(const AP4_UI08 * in,AP4_Size in_size,AP4_UI08 * out,AP4_Size * out_size,bool is_last_buffer)500 AP4_CbcStreamCipher::ProcessBuffer(const AP4_UI08* in,
501 AP4_Size in_size,
502 AP4_UI08* out,
503 AP4_Size* out_size,
504 bool is_last_buffer)
505 {
506 // check the parameters
507 if (out_size == NULL) return AP4_ERROR_INVALID_PARAMETERS;
508
509 // check the state
510 if (m_BlockCipher == NULL || m_Eos) {
511 *out_size = 0;
512 return AP4_ERROR_INVALID_STATE;
513 }
514 if (is_last_buffer) m_Eos = true;
515
516 if (m_BlockCipher->GetDirection() == AP4_BlockCipher::ENCRYPT) {
517 return EncryptBuffer(in, in_size, out, out_size, is_last_buffer);
518 } else {
519 return DecryptBuffer(in, in_size, out, out_size, is_last_buffer);
520 }
521 }
522
523 /*----------------------------------------------------------------------
524 | AP4_PatternStreamCipher
525 +---------------------------------------------------------------------*/
AP4_PatternStreamCipher(AP4_StreamCipher * cipher,AP4_UI08 crypt_byte_block,AP4_UI08 skip_byte_block)526 AP4_PatternStreamCipher::AP4_PatternStreamCipher(AP4_StreamCipher* cipher,
527 AP4_UI08 crypt_byte_block,
528 AP4_UI08 skip_byte_block) :
529 m_Cipher(cipher),
530 m_CryptByteBlock(crypt_byte_block),
531 m_SkipByteBlock(skip_byte_block),
532 m_StreamOffset(0)
533 {
534 }
535
536 /*----------------------------------------------------------------------
537 | AP4_PatternStreamCipher::~AP4_PatternStreamCipher
538 +---------------------------------------------------------------------*/
~AP4_PatternStreamCipher()539 AP4_PatternStreamCipher::~AP4_PatternStreamCipher()
540 {
541 delete m_Cipher;
542 }
543
544 /*----------------------------------------------------------------------
545 | AP4_PatternStreamCipher::SetStreamOffset
546 +---------------------------------------------------------------------*/
547 AP4_Result
SetStreamOffset(AP4_UI64,AP4_Cardinal *)548 AP4_PatternStreamCipher::SetStreamOffset(AP4_UI64 /*offset*/,
549 AP4_Cardinal* /*preroll*/)
550 {
551 return AP4_ERROR_NOT_SUPPORTED;
552 }
553
554 /*----------------------------------------------------------------------
555 | AP4_PatternStreamCipher
556 +---------------------------------------------------------------------*/
557 AP4_Result
ProcessBuffer(const AP4_UI08 * in,AP4_Size in_size,AP4_UI08 * out,AP4_Size * out_size,bool)558 AP4_PatternStreamCipher::ProcessBuffer(const AP4_UI08* in,
559 AP4_Size in_size,
560 AP4_UI08* out,
561 AP4_Size* out_size,
562 bool /* is_last_buffer */)
563 {
564 // set default return values
565 *out_size = 0;
566
567 // check that the range is block-aligned (required by the spec for pattern encryption)
568 if (m_StreamOffset % 16) return AP4_ERROR_INVALID_FORMAT;
569
570 // compute where we are in the pattern
571 unsigned int pattern_span = m_CryptByteBlock+m_SkipByteBlock;
572 unsigned int block_position = (unsigned int)(m_StreamOffset/16);
573 unsigned int pattern_position = block_position % pattern_span;
574
575 // process the range
576 while (*out_size < in_size) {
577 unsigned int crypt_size = 0;
578 unsigned int skip_size = m_SkipByteBlock*16;
579 if (pattern_position < m_CryptByteBlock) {
580 // in the encrypted part
581 crypt_size = (m_CryptByteBlock-pattern_position)*16;
582 } else {
583 // in the skipped part
584 skip_size = (pattern_span-pattern_position)*16;
585 }
586
587 // clip
588 AP4_Size remain = in_size-*out_size;
589 if (crypt_size > remain) {
590 crypt_size = 16*(remain/16);
591 skip_size = remain-crypt_size;
592 }
593 if (crypt_size+skip_size > remain) {
594 skip_size = remain-crypt_size;
595 }
596
597 // encrypted part
598 if (crypt_size) {
599 AP4_Size in_chunk_size = crypt_size;
600 AP4_Size out_chunk_size = crypt_size;
601
602 AP4_Result result = m_Cipher->ProcessBuffer(in, in_chunk_size, out, &out_chunk_size);
603 if (AP4_FAILED(result)) return result;
604 // check that we got back what we expectected
605 if (out_chunk_size != in_chunk_size) {
606 return AP4_ERROR_INTERNAL;
607 }
608 in += crypt_size;
609 out += crypt_size;
610 *out_size += crypt_size;
611 m_StreamOffset += crypt_size;
612 }
613
614 // skipped part
615 if (skip_size) {
616 AP4_CopyMemory(out, in, skip_size);
617 in += skip_size;
618 out += skip_size;
619 *out_size += skip_size;
620 m_StreamOffset += skip_size;
621 }
622
623 // we're now at the start of a new pattern
624 pattern_position = 0;
625 }
626
627 return AP4_SUCCESS;
628 }
629
630 /*----------------------------------------------------------------------
631 | AP4_PatternStreamCipher
632 +---------------------------------------------------------------------*/
633 AP4_Result
SetIV(const AP4_UI08 * iv)634 AP4_PatternStreamCipher::SetIV(const AP4_UI08* iv)
635 {
636 m_StreamOffset = 0;
637 return m_Cipher->SetIV(iv);
638 }
639
640 /*----------------------------------------------------------------------
641 | AP4_PatternStreamCipher
642 +---------------------------------------------------------------------*/
643 const AP4_UI08*
GetIV()644 AP4_PatternStreamCipher::GetIV()
645 {
646 return m_Cipher->GetIV();
647 }
648