mirror of
https://github.com/axiomatic-systems/Bento4.git
synced 2026-01-19 00:05:12 +08:00
1198 lines
48 KiB
C++
1198 lines
48 KiB
C++
/*****************************************************************
|
|
|
|
|
| AP4 - OMA DCF Support
|
|
|
|
|
| Copyright 2002-2008 Axiomatic Systems, LLC
|
|
|
|
|
|
|
|
| This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
|
|
|
|
| Unless you have obtained Bento4 under a difference license,
|
|
| this version of Bento4 is Bento4|GPL.
|
|
| Bento4|GPL is free software; you can redistribute it and/or modify
|
|
| it under the terms of the GNU General Public License as published by
|
|
| the Free Software Foundation; either version 2, or (at your option)
|
|
| any later version.
|
|
|
|
|
| Bento4|GPL is distributed in the hope that it will be useful,
|
|
| but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| GNU General Public License for more details.
|
|
|
|
|
| You should have received a copy of the GNU General Public License
|
|
| along with Bento4|GPL; see the file COPYING. If not, write to the
|
|
| Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
| 02111-1307, USA.
|
|
|
|
|
****************************************************************/
|
|
|
|
/*----------------------------------------------------------------------
|
|
| includes
|
|
+---------------------------------------------------------------------*/
|
|
#include "Ap4SchmAtom.h"
|
|
#include "Ap4StsdAtom.h"
|
|
#include "Ap4Sample.h"
|
|
#include "Ap4StreamCipher.h"
|
|
#include "Ap4IsfmAtom.h"
|
|
#include "Ap4FrmaAtom.h"
|
|
#include "Ap4IkmsAtom.h"
|
|
#include "Ap4IsfmAtom.h"
|
|
#include "Ap4IsltAtom.h"
|
|
#include "Ap4Utils.h"
|
|
#include "Ap4TrakAtom.h"
|
|
#include "Ap4OdafAtom.h"
|
|
#include "Ap4OmaDcf.h"
|
|
#include "Ap4OhdrAtom.h"
|
|
#include "Ap4OddaAtom.h"
|
|
#include "Ap4OdheAtom.h"
|
|
#include "Ap4FtypAtom.h"
|
|
#include "Ap4GrpiAtom.h"
|
|
#include "Ap4HdlrAtom.h"
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDrmInfo Dynamic Cast Anchor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_DEFINE_DYNAMIC_CAST_ANCHOR(AP4_OmaDrmInfo)
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfAtomDecrypter::DecryptAtoms
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfAtomDecrypter::DecryptAtoms(AP4_AtomParent& atoms,
|
|
AP4_Processor::ProgressListener* /*listener*/,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_ProtectionKeyMap& key_map)
|
|
{
|
|
// default factory
|
|
if (block_cipher_factory == NULL) {
|
|
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
}
|
|
|
|
unsigned int index = 1;
|
|
for (AP4_List<AP4_Atom>::Item* item = atoms.GetChildren().FirstItem();
|
|
item;
|
|
item = item->GetNext()) {
|
|
AP4_Atom* atom = item->GetData();
|
|
if (atom->GetType() != AP4_ATOM_TYPE_ODRM) continue;
|
|
|
|
// check that we have the key
|
|
const AP4_DataBuffer* key = key_map.GetKey(index++);
|
|
if (key == NULL) return AP4_ERROR_INVALID_PARAMETERS;
|
|
|
|
// check that we have all the atoms we need
|
|
AP4_ContainerAtom* odrm = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
|
|
if (odrm == NULL) continue; // not enough info
|
|
AP4_OdheAtom* odhe = AP4_DYNAMIC_CAST(AP4_OdheAtom, odrm->GetChild(AP4_ATOM_TYPE_ODHE));
|
|
if (odhe == NULL) continue; // not enough info
|
|
AP4_OddaAtom* odda = AP4_DYNAMIC_CAST(AP4_OddaAtom, odrm->GetChild(AP4_ATOM_TYPE_ODDA));;
|
|
if (odda == NULL) continue; // not enough info
|
|
AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, odhe->GetChild(AP4_ATOM_TYPE_OHDR));
|
|
if (ohdr == NULL) continue; // not enough info
|
|
|
|
// do nothing if the atom is not encrypted
|
|
if (ohdr->GetEncryptionMethod() == AP4_OMA_DCF_ENCRYPTION_METHOD_NULL) {
|
|
continue;
|
|
}
|
|
|
|
// create the byte stream
|
|
AP4_ByteStream* cipher_stream = NULL;
|
|
AP4_Result result = CreateDecryptingStream(*odrm,
|
|
key->GetData(),
|
|
key->GetDataSize(),
|
|
block_cipher_factory,
|
|
cipher_stream);
|
|
if (AP4_SUCCEEDED(result)) {
|
|
// replace the odda atom's payload with the decrypted stream
|
|
odda->SetEncryptedPayload(*cipher_stream, ohdr->GetPlaintextLength());
|
|
cipher_stream->Release();
|
|
|
|
// the atom will now be in the clear
|
|
ohdr->SetEncryptionMethod(AP4_OMA_DCF_ENCRYPTION_METHOD_NULL);
|
|
ohdr->SetPaddingScheme(AP4_OMA_DCF_PADDING_SCHEME_NONE);
|
|
}
|
|
}
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfAtomDecrypter::CreateDecryptingStream
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfAtomDecrypter::CreateDecryptingStream(
|
|
AP4_ContainerAtom& odrm,
|
|
const AP4_UI08* key,
|
|
AP4_Size key_size,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_ByteStream*& stream)
|
|
{
|
|
// default return values
|
|
stream = NULL;
|
|
|
|
AP4_OdheAtom* odhe = AP4_DYNAMIC_CAST(AP4_OdheAtom, odrm.GetChild(AP4_ATOM_TYPE_ODHE));
|
|
if (odhe == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
AP4_OddaAtom* odda = AP4_DYNAMIC_CAST(AP4_OddaAtom, odrm.GetChild(AP4_ATOM_TYPE_ODDA));;
|
|
if (odda == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, odhe->GetChild(AP4_ATOM_TYPE_OHDR));
|
|
if (ohdr == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
|
|
// default cipher factory
|
|
if (block_cipher_factory == NULL) {
|
|
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
}
|
|
|
|
// shortcut for non-encrypted files
|
|
if (ohdr->GetEncryptionMethod() == AP4_OMA_DCF_ENCRYPTION_METHOD_NULL) {
|
|
stream = &odda->GetEncryptedPayload();
|
|
stream->AddReference();
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
// if this is part of a group, use the group key to obtain the content
|
|
// key (note that the field called GroupKey in the spec is actually not
|
|
// the group key but the content key encrypted with the group key...
|
|
AP4_GrpiAtom* grpi = AP4_DYNAMIC_CAST(AP4_GrpiAtom, ohdr->GetChild(AP4_ATOM_TYPE_GRPI));
|
|
AP4_UI08* key_buffer = NULL;
|
|
if (grpi) {
|
|
// sanity check on the encrypted key size
|
|
if (grpi->GetGroupKey().GetDataSize() < 32) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
|
|
// create a block cipher to decrypt the content key
|
|
AP4_BlockCipher* block_cipher = NULL;
|
|
AP4_Result result;
|
|
|
|
// create a stream cipher from the block cipher
|
|
AP4_StreamCipher* stream_cipher = NULL;
|
|
switch (ohdr->GetEncryptionMethod()) {
|
|
case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC:
|
|
result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::DECRYPT,
|
|
AP4_BlockCipher::CBC,
|
|
NULL,
|
|
key,
|
|
key_size,
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return result;
|
|
stream_cipher = new AP4_CbcStreamCipher(block_cipher);
|
|
break;
|
|
|
|
case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR: {
|
|
AP4_BlockCipher::CtrParams ctr_params;
|
|
ctr_params.counter_size = 16;
|
|
result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::DECRYPT,
|
|
AP4_BlockCipher::CTR,
|
|
&ctr_params,
|
|
key,
|
|
key_size,
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return result;
|
|
stream_cipher = new AP4_CtrStreamCipher(block_cipher, 16);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// set the IV
|
|
stream_cipher->SetIV(grpi->GetGroupKey().GetData());
|
|
|
|
// decrypt the content key
|
|
AP4_Size key_buffer_size = grpi->GetGroupKey().GetDataSize(); // worst case
|
|
key_buffer = new AP4_UI08[key_buffer_size];
|
|
result = stream_cipher->ProcessBuffer(grpi->GetGroupKey().GetData()+16,
|
|
grpi->GetGroupKey().GetDataSize()-16,
|
|
key_buffer,
|
|
&key_buffer_size,
|
|
true);
|
|
delete stream_cipher; // this will also delete the block cipher
|
|
if (AP4_FAILED(result)) {
|
|
delete[] key_buffer;
|
|
return result;
|
|
}
|
|
|
|
// point to the new key value
|
|
key = key_buffer;
|
|
key_size = key_buffer_size;
|
|
}
|
|
|
|
AP4_OmaDcfCipherMode mode;
|
|
switch (ohdr->GetEncryptionMethod()) {
|
|
case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC:
|
|
mode = AP4_OMA_DCF_CIPHER_MODE_CBC;
|
|
break;
|
|
case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR:
|
|
mode = AP4_OMA_DCF_CIPHER_MODE_CTR;
|
|
break;
|
|
default:
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
AP4_Result result;
|
|
result = CreateDecryptingStream(mode,
|
|
odda->GetEncryptedPayload(),
|
|
ohdr->GetPlaintextLength(),
|
|
key, key_size,
|
|
block_cipher_factory,
|
|
stream);
|
|
|
|
// cleanup
|
|
delete[] key_buffer;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfAtomDecrypter::CreateDecryptingStream
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfAtomDecrypter::CreateDecryptingStream(
|
|
AP4_OmaDcfCipherMode mode,
|
|
AP4_ByteStream& encrypted_stream,
|
|
AP4_LargeSize cleartext_size,
|
|
const AP4_UI08* key,
|
|
AP4_Size key_size,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_ByteStream*& stream)
|
|
{
|
|
// default return value
|
|
stream = NULL;
|
|
|
|
// default factory
|
|
if (block_cipher_factory == NULL) {
|
|
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
}
|
|
|
|
// get the encrypted size (includes IV and padding)
|
|
AP4_LargeSize encrypted_size = 0;
|
|
AP4_Result result = encrypted_stream.GetSize(encrypted_size);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// check that the encrypted size is consistent with the cipher mode
|
|
AP4_BlockCipher::CipherMode cipher_mode;
|
|
if (mode == AP4_OMA_DCF_CIPHER_MODE_CBC) {
|
|
// we need at least 16 bytes of IV and 32 bytes of data+padding
|
|
// we also need a multiple of the block size
|
|
if (encrypted_size < 48 || ((encrypted_size % 16) != 0)) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
cipher_mode = AP4_BlockCipher::CBC;
|
|
} else if (mode == AP4_OMA_DCF_CIPHER_MODE_CTR) {
|
|
// we need at least 16 bytes of IV
|
|
if (encrypted_size < 16) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
cipher_mode = AP4_BlockCipher::CTR;
|
|
} else {
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// read the IV
|
|
AP4_UI08 iv[16];
|
|
result = encrypted_stream.Seek(0);
|
|
if (AP4_FAILED(result)) return result;
|
|
result = encrypted_stream.Read(iv, 16);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// create a sub stream with just the encrypted payload without the IV
|
|
AP4_ByteStream* sub_stream = new AP4_SubStream(encrypted_stream, 16, encrypted_size-16);
|
|
|
|
// create the decrypting cipher
|
|
result = AP4_DecryptingStream::Create(cipher_mode,
|
|
*sub_stream,
|
|
cleartext_size,
|
|
iv,
|
|
16,
|
|
key,
|
|
key_size,
|
|
block_cipher_factory,
|
|
stream);
|
|
|
|
// we don't keep our own reference to the sub stream
|
|
sub_stream->Release();
|
|
|
|
return result;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfSampleDecrypter::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfSampleDecrypter::Create(AP4_ProtectedSampleDescription* sample_description,
|
|
const AP4_UI08* key,
|
|
AP4_Size key_size,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_OmaDcfSampleDecrypter*& cipher)
|
|
{
|
|
// check the parameters
|
|
if (key == NULL || block_cipher_factory == NULL) {
|
|
return AP4_ERROR_INVALID_PARAMETERS;
|
|
}
|
|
|
|
// default return value
|
|
cipher = NULL;
|
|
|
|
// default factory
|
|
if (block_cipher_factory == NULL) {
|
|
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
}
|
|
|
|
// get the scheme info atom
|
|
AP4_ContainerAtom* schi = sample_description->GetSchemeInfo()->GetSchiAtom();
|
|
if (schi == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
|
|
// get and check the cipher params
|
|
// NOTE: we only support an IV Length less than or equal to the cipher block size,
|
|
// and we don't know how to deal with a key indicator length != 0
|
|
AP4_OdafAtom* odaf = AP4_DYNAMIC_CAST(AP4_OdafAtom, schi->FindChild("odkm/odaf"));
|
|
if (odaf) {
|
|
if (odaf->GetIvLength() > AP4_CIPHER_BLOCK_SIZE) return AP4_ERROR_INVALID_FORMAT;
|
|
if (odaf->GetKeyIndicatorLength() != 0) return AP4_ERROR_INVALID_FORMAT;
|
|
} else {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
|
|
// check the scheme details and create the cipher
|
|
AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, schi->FindChild("odkm/ohdr"));
|
|
if (ohdr == NULL) return AP4_ERROR_INVALID_FORMAT;
|
|
AP4_UI08 encryption_method = ohdr->GetEncryptionMethod();
|
|
if (encryption_method == AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC) {
|
|
// in CBC mode, we only support IVs of the same size as the cipher block size
|
|
if (odaf->GetIvLength() != AP4_CIPHER_BLOCK_SIZE) return AP4_ERROR_INVALID_FORMAT;
|
|
|
|
// require RFC_2630 padding
|
|
if (ohdr->GetPaddingScheme() != AP4_OMA_DCF_PADDING_SCHEME_RFC_2630) {
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
// create the block cipher
|
|
AP4_BlockCipher* block_cipher = NULL;
|
|
AP4_Result result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::DECRYPT,
|
|
AP4_BlockCipher::CBC,
|
|
NULL,
|
|
key,
|
|
key_size,
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// create the cipher
|
|
cipher = new AP4_OmaDcfCbcSampleDecrypter(block_cipher,
|
|
odaf->GetSelectiveEncryption());
|
|
return AP4_SUCCESS;
|
|
} else if (encryption_method == AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR) {
|
|
// require NONE padding
|
|
if (ohdr->GetPaddingScheme() != AP4_OMA_DCF_PADDING_SCHEME_NONE) {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
|
|
// create the block cipher
|
|
AP4_BlockCipher* block_cipher = NULL;
|
|
AP4_BlockCipher::CtrParams ctr_params;
|
|
ctr_params.counter_size = odaf->GetIvLength();
|
|
AP4_Result result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::DECRYPT,
|
|
AP4_BlockCipher::CTR,
|
|
&ctr_params,
|
|
key,
|
|
key_size,
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// create the cipher
|
|
cipher = new AP4_OmaDcfCtrSampleDecrypter(block_cipher,
|
|
odaf->GetIvLength(),
|
|
odaf->GetSelectiveEncryption());
|
|
return AP4_SUCCESS;
|
|
} else {
|
|
return AP4_ERROR_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleDecrypter::AP4_OmaDcfCtrSampleDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCtrSampleDecrypter::AP4_OmaDcfCtrSampleDecrypter(
|
|
AP4_BlockCipher* block_cipher,
|
|
AP4_Size iv_length,
|
|
bool selective_encryption) :
|
|
AP4_OmaDcfSampleDecrypter(iv_length, selective_encryption)
|
|
{
|
|
m_Cipher = new AP4_CtrStreamCipher(block_cipher, iv_length);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleDecrypter::~AP4_OmaDcfCtrSampleDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCtrSampleDecrypter::~AP4_OmaDcfCtrSampleDecrypter()
|
|
{
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleDecrypter::DecryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfCtrSampleDecrypter::DecryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
const AP4_UI08* /*iv*/)
|
|
{
|
|
bool is_encrypted = true;
|
|
const unsigned char* in = data_in.GetData();
|
|
AP4_Size in_size = data_in.GetDataSize();
|
|
|
|
// default to 0 output
|
|
AP4_CHECK(data_out.SetDataSize(0));
|
|
|
|
// check the selective encryption flag
|
|
if (m_SelectiveEncryption) {
|
|
if (in_size < 1) return AP4_ERROR_INVALID_FORMAT;
|
|
is_encrypted = ((in[0]&0x80)!=0);
|
|
in++;
|
|
}
|
|
|
|
// check the size
|
|
unsigned int header_size = (m_SelectiveEncryption?1:0)+(is_encrypted?m_IvLength:0);
|
|
if (header_size > in_size) return AP4_ERROR_INVALID_FORMAT;
|
|
|
|
// process the sample data
|
|
AP4_Size payload_size = in_size-header_size;
|
|
AP4_CHECK(data_out.Reserve(payload_size));
|
|
unsigned char* out = data_out.UseData();
|
|
if (is_encrypted) {
|
|
// set the IV
|
|
if (m_IvLength == 16) {
|
|
m_Cipher->SetIV(in);
|
|
} else {
|
|
AP4_UI08 iv[16];
|
|
AP4_SetMemory(iv, 0, 16);
|
|
AP4_CopyMemory(iv+16-m_IvLength, in, m_IvLength);
|
|
m_Cipher->SetIV(iv);
|
|
}
|
|
AP4_CHECK(m_Cipher->ProcessBuffer(in+m_IvLength,
|
|
payload_size,
|
|
out));
|
|
} else {
|
|
AP4_CopyMemory(out, in, payload_size);
|
|
}
|
|
AP4_CHECK(data_out.SetDataSize(payload_size));
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleDecrypter::GetDecryptedSampleSize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Size
|
|
AP4_OmaDcfCtrSampleDecrypter::GetDecryptedSampleSize(AP4_Sample& sample)
|
|
{
|
|
if (m_Cipher == NULL) return 0;
|
|
|
|
// decide if this sample is encrypted or not
|
|
bool is_encrypted;
|
|
if (m_SelectiveEncryption) {
|
|
// read the first byte to see if the sample is encrypted or not
|
|
AP4_Byte h;
|
|
AP4_DataBuffer peek_buffer;
|
|
peek_buffer.SetBuffer(&h, 1);
|
|
sample.ReadData(peek_buffer, 1);
|
|
is_encrypted = ((h&0x80)!=0);
|
|
} else {
|
|
is_encrypted = true;
|
|
}
|
|
|
|
AP4_Size crypto_header_size = (m_SelectiveEncryption?1:0)+(is_encrypted?m_IvLength:0);
|
|
return sample.GetSize()-crypto_header_size;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCbcSampleDecrypter::AP4_OmaDcfCbcSampleDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCbcSampleDecrypter::AP4_OmaDcfCbcSampleDecrypter(
|
|
AP4_BlockCipher* block_cipher,
|
|
bool selective_encryption) :
|
|
AP4_OmaDcfSampleDecrypter(AP4_CIPHER_BLOCK_SIZE, selective_encryption)
|
|
{
|
|
m_Cipher = new AP4_CbcStreamCipher(block_cipher);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCbcSampleDecrypter::~AP4_OmaDcfCbcSampleDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCbcSampleDecrypter::~AP4_OmaDcfCbcSampleDecrypter()
|
|
{
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDbcCbcSampleDecrypter::DecryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfCbcSampleDecrypter::DecryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
const AP4_UI08* /*iv*/)
|
|
{
|
|
bool is_encrypted = true;
|
|
const unsigned char* in = data_in.GetData();
|
|
AP4_Size in_size = data_in.GetDataSize();
|
|
AP4_Size out_size;
|
|
|
|
// default to 0 output
|
|
AP4_CHECK(data_out.SetDataSize(0));
|
|
|
|
// check the selective encryption flag
|
|
if (m_SelectiveEncryption) {
|
|
if (in_size < 1) return AP4_ERROR_INVALID_FORMAT;
|
|
is_encrypted = ((in[0]&0x80)!=0);
|
|
in++;
|
|
}
|
|
|
|
// check the size
|
|
unsigned int header_size = (m_SelectiveEncryption?1:0)+(is_encrypted?m_IvLength:0);
|
|
if (header_size > in_size) return AP4_ERROR_INVALID_FORMAT;
|
|
|
|
// process the sample data
|
|
unsigned int payload_size = in_size-header_size;
|
|
data_out.Reserve(payload_size);
|
|
unsigned char* out = data_out.UseData();
|
|
if (is_encrypted) {
|
|
// get the IV
|
|
const AP4_UI08* iv = (const AP4_UI08*)in;
|
|
in += AP4_CIPHER_BLOCK_SIZE;
|
|
|
|
m_Cipher->SetIV(iv);
|
|
out_size = payload_size;
|
|
AP4_CHECK(m_Cipher->ProcessBuffer(in, payload_size, out, &out_size, true));
|
|
} else {
|
|
AP4_CopyMemory(out, in, payload_size);
|
|
out_size = payload_size;
|
|
}
|
|
|
|
AP4_CHECK(data_out.SetDataSize(out_size));
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCbcSampleDecrypter::GetDecryptedSampleSize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Size
|
|
AP4_OmaDcfCbcSampleDecrypter::GetDecryptedSampleSize(AP4_Sample& sample)
|
|
{
|
|
if (m_Cipher == NULL) return 0;
|
|
|
|
// decide if this sample is encrypted or not
|
|
bool is_encrypted;
|
|
if (m_SelectiveEncryption) {
|
|
// read the first byte to see if the sample is encrypted or not
|
|
AP4_Byte h;
|
|
AP4_DataBuffer peek_buffer;
|
|
peek_buffer.SetBuffer(&h, 1);
|
|
sample.ReadData(peek_buffer, 1);
|
|
is_encrypted = ((h&0x80)!=0);
|
|
} else {
|
|
is_encrypted = true;
|
|
}
|
|
|
|
if (is_encrypted) {
|
|
// with CBC, we need to decrypt the last block to know what the padding was
|
|
AP4_Size crypto_header_size = (m_SelectiveEncryption?1:0)+m_IvLength;
|
|
AP4_Size encrypted_size = sample.GetSize()-crypto_header_size;
|
|
AP4_DataBuffer encrypted;
|
|
AP4_DataBuffer decrypted;
|
|
AP4_Size decrypted_size = AP4_CIPHER_BLOCK_SIZE;
|
|
if (sample.GetSize() < crypto_header_size+AP4_CIPHER_BLOCK_SIZE) {
|
|
return 0;
|
|
}
|
|
AP4_Size offset = sample.GetSize()-2*AP4_CIPHER_BLOCK_SIZE;
|
|
if (AP4_FAILED(sample.ReadData(encrypted, 2*AP4_CIPHER_BLOCK_SIZE, offset))) {
|
|
return 0;
|
|
}
|
|
decrypted.Reserve(decrypted_size);
|
|
m_Cipher->SetIV(encrypted.GetData());
|
|
if (AP4_FAILED(m_Cipher->ProcessBuffer(encrypted.GetData()+AP4_CIPHER_BLOCK_SIZE,
|
|
AP4_CIPHER_BLOCK_SIZE,
|
|
decrypted.UseData(),
|
|
&decrypted_size,
|
|
true))) {
|
|
return 0;
|
|
}
|
|
unsigned int padding_size = AP4_CIPHER_BLOCK_SIZE-decrypted_size;
|
|
return encrypted_size-padding_size;
|
|
} else {
|
|
return sample.GetSize()-(m_SelectiveEncryption?1:0);
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfSampleEncrypter::AP4_OmaDcfSampleEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfSampleEncrypter::AP4_OmaDcfSampleEncrypter(const AP4_UI08* salt)
|
|
{
|
|
// left-align the salt
|
|
unsigned int i=0;
|
|
if (salt) {
|
|
for (; i<8; i++) {
|
|
m_Salt[i] = salt[i];
|
|
}
|
|
}
|
|
for (; i<sizeof(m_Salt)/sizeof(m_Salt[0]); i++) {
|
|
m_Salt[i] = 0;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleEncrypter::AP4_OmaDcfCtrSampleEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCtrSampleEncrypter::AP4_OmaDcfCtrSampleEncrypter(AP4_BlockCipher* block_cipher,
|
|
const AP4_UI08* salt) :
|
|
AP4_OmaDcfSampleEncrypter(salt)
|
|
{
|
|
m_Cipher = new AP4_CtrStreamCipher(block_cipher, 16);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleEncrypter::~AP4_OmaDcfCtrSampleEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCtrSampleEncrypter::~AP4_OmaDcfCtrSampleEncrypter()
|
|
{
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleEncrypter::EncryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfCtrSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
AP4_UI64 counter,
|
|
bool /*skip_encryption*/)
|
|
{
|
|
// setup the buffers
|
|
const unsigned char* in = data_in.GetData();
|
|
AP4_CHECK(data_out.SetDataSize(data_in.GetDataSize()+AP4_CIPHER_BLOCK_SIZE+1));
|
|
unsigned char* out = data_out.UseData();
|
|
|
|
// selective encryption flag
|
|
*out++ = 0x80;
|
|
|
|
// IV on 16 bytes: [SSSSSSSSXXXXXXXX]
|
|
// where SSSSSSSS is the 64-bit salt and
|
|
// XXXXXXXX is the 64-bit base counter
|
|
AP4_CopyMemory(out, m_Salt, 8);
|
|
AP4_BytesFromUInt64BE(&out[8], counter);
|
|
|
|
// encrypt the payload
|
|
AP4_Size data_size = data_in.GetDataSize();
|
|
m_Cipher->SetIV(out);
|
|
m_Cipher->ProcessBuffer(in, data_size, out+AP4_CIPHER_BLOCK_SIZE);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCtrSampleEncrypter::GetEncryptedSampleSize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Size
|
|
AP4_OmaDcfCtrSampleEncrypter::GetEncryptedSampleSize(AP4_Sample& sample)
|
|
{
|
|
return sample.GetSize()+AP4_CIPHER_BLOCK_SIZE+1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCbcSampleEncrypter::AP4_OmaDcfCbcSampleEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCbcSampleEncrypter::AP4_OmaDcfCbcSampleEncrypter(AP4_BlockCipher* block_cipher,
|
|
const AP4_UI08* salt) :
|
|
AP4_OmaDcfSampleEncrypter(salt)
|
|
{
|
|
m_Cipher = new AP4_CbcStreamCipher(block_cipher);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCbcSampleEncrypter::~AP4_OmaDcfCbcSampleEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfCbcSampleEncrypter::~AP4_OmaDcfCbcSampleEncrypter()
|
|
{
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCbcSampleEncrypter::EncryptSampleData
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfCbcSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out,
|
|
AP4_UI64 counter,
|
|
bool /*skip_encryption*/)
|
|
{
|
|
// make sure there is enough space in the output buffer
|
|
data_out.Reserve(data_in.GetDataSize()+2*AP4_CIPHER_BLOCK_SIZE+1);
|
|
|
|
// setup the buffers
|
|
AP4_Size out_size = data_in.GetDataSize()+AP4_CIPHER_BLOCK_SIZE;
|
|
unsigned char* out = data_out.UseData();
|
|
|
|
// selective encryption flag
|
|
*out++ = 0x80;
|
|
|
|
// IV on 16 bytes: [SSSSSSSSXXXXXXXX]
|
|
// where SSSSSSSS is the 64-bit salt and
|
|
// XXXXXXXX is the 64-bit base counter
|
|
AP4_CopyMemory(out, m_Salt, 8);
|
|
AP4_BytesFromUInt64BE(&out[8], counter);
|
|
|
|
// encrypt the payload
|
|
m_Cipher->SetIV(out);
|
|
m_Cipher->ProcessBuffer(data_in.GetData(),
|
|
data_in.GetDataSize(),
|
|
out+AP4_CIPHER_BLOCK_SIZE,
|
|
&out_size,
|
|
true);
|
|
AP4_CHECK(data_out.SetDataSize(out_size+AP4_CIPHER_BLOCK_SIZE+1));
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfCbcSampleEncrypter::GetEncryptedSampleSize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Size
|
|
AP4_OmaDcfCbcSampleEncrypter::GetEncryptedSampleSize(AP4_Sample& sample)
|
|
{
|
|
AP4_Size sample_size = sample.GetSize();
|
|
AP4_Size padding_size = AP4_CIPHER_BLOCK_SIZE-(sample_size%AP4_CIPHER_BLOCK_SIZE);
|
|
return sample_size+padding_size+AP4_CIPHER_BLOCK_SIZE+1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackDecrypter::Create
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfTrackDecrypter::Create(
|
|
const AP4_UI08* key,
|
|
AP4_Size key_size,
|
|
AP4_ProtectedSampleDescription* sample_description,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_BlockCipherFactory* block_cipher_factory,
|
|
AP4_OmaDcfTrackDecrypter*& decrypter)
|
|
{
|
|
// check and set defaults
|
|
if (key == NULL) {
|
|
return AP4_ERROR_INVALID_PARAMETERS;
|
|
}
|
|
if (block_cipher_factory == NULL) {
|
|
block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
}
|
|
decrypter = NULL;
|
|
|
|
// create the cipher
|
|
AP4_OmaDcfSampleDecrypter* cipher = NULL;
|
|
AP4_Result result = AP4_OmaDcfSampleDecrypter::Create(sample_description,
|
|
key,
|
|
key_size,
|
|
block_cipher_factory,
|
|
cipher);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
// instantiate the object
|
|
decrypter = new AP4_OmaDcfTrackDecrypter(cipher,
|
|
sample_entry,
|
|
sample_description->GetOriginalFormat());
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackDecrypter::AP4_OmaDcfTrackDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfTrackDecrypter::AP4_OmaDcfTrackDecrypter(AP4_OmaDcfSampleDecrypter* cipher,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_UI32 original_format) :
|
|
m_Cipher(cipher),
|
|
m_SampleEntry(sample_entry),
|
|
m_OriginalFormat(original_format)
|
|
{
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackDecrypter::~AP4_OmaDcfTrackDecrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfTrackDecrypter::~AP4_OmaDcfTrackDecrypter()
|
|
{
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackDecrypter::GetProcessedSampleSize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Size
|
|
AP4_OmaDcfTrackDecrypter::GetProcessedSampleSize(AP4_Sample& sample)
|
|
{
|
|
if (m_Cipher == NULL) return 0;
|
|
return m_Cipher->GetDecryptedSampleSize(sample);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackDecrypter::ProcessTrack
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfTrackDecrypter::ProcessTrack()
|
|
{
|
|
m_SampleEntry->SetType(m_OriginalFormat);
|
|
m_SampleEntry->DeleteChild(AP4_ATOM_TYPE_SINF);
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfDecrypter::ProcessSample
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfTrackDecrypter::ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out)
|
|
{
|
|
return m_Cipher->DecryptSampleData(data_in, data_out);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
class AP4_OmaDcfTrackEncrypter : public AP4_Processor::TrackHandler {
|
|
public:
|
|
// constructor
|
|
AP4_OmaDcfTrackEncrypter(AP4_OmaDcfCipherMode cipher_mode,
|
|
AP4_BlockCipher* block_cipher,
|
|
const AP4_UI08* iv,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_UI32 format,
|
|
const char* content_id,
|
|
const char* rights_issuer_url,
|
|
const AP4_Byte* textual_headers,
|
|
AP4_Size textual_headers_size);
|
|
virtual ~AP4_OmaDcfTrackEncrypter();
|
|
|
|
// methods
|
|
virtual AP4_Size GetProcessedSampleSize(AP4_Sample& sample);
|
|
virtual AP4_Result ProcessTrack();
|
|
virtual AP4_Result ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out);
|
|
|
|
private:
|
|
// members
|
|
AP4_OmaDcfSampleEncrypter* m_Cipher;
|
|
AP4_UI08 m_CipherMode;
|
|
AP4_UI08 m_CipherPadding;
|
|
AP4_SampleEntry* m_SampleEntry;
|
|
AP4_UI32 m_Format;
|
|
AP4_String m_ContentId;
|
|
AP4_String m_RightsIssuerUrl;
|
|
AP4_DataBuffer m_TextualHeaders;
|
|
AP4_UI64 m_Counter;
|
|
};
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackEncrypter::AP4_OmaDcfTrackEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfTrackEncrypter::AP4_OmaDcfTrackEncrypter(
|
|
AP4_OmaDcfCipherMode cipher_mode,
|
|
AP4_BlockCipher* block_cipher,
|
|
const AP4_UI08* salt,
|
|
AP4_SampleEntry* sample_entry,
|
|
AP4_UI32 format,
|
|
const char* content_id,
|
|
const char* rights_issuer_url,
|
|
const AP4_Byte* textual_headers,
|
|
AP4_Size textual_headers_size) :
|
|
m_SampleEntry(sample_entry),
|
|
m_Format(format),
|
|
m_ContentId(content_id),
|
|
m_RightsIssuerUrl(rights_issuer_url),
|
|
m_TextualHeaders(textual_headers, textual_headers_size),
|
|
m_Counter(0)
|
|
{
|
|
// instantiate the cipher (fixed params for now)
|
|
if (cipher_mode == AP4_OMA_DCF_CIPHER_MODE_CBC) {
|
|
m_Cipher = new AP4_OmaDcfCbcSampleEncrypter(block_cipher, salt);
|
|
m_CipherMode = AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC;
|
|
m_CipherPadding = AP4_OMA_DCF_PADDING_SCHEME_RFC_2630;
|
|
} else {
|
|
m_Cipher = new AP4_OmaDcfCtrSampleEncrypter(block_cipher, salt);
|
|
m_CipherMode = AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR;
|
|
m_CipherPadding = AP4_OMA_DCF_PADDING_SCHEME_NONE;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackEncrypter::~AP4_OmaDcfTrackEncrypter
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfTrackEncrypter::~AP4_OmaDcfTrackEncrypter()
|
|
{
|
|
delete m_Cipher;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackEncrypter::GetProcessedSampleSize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Size
|
|
AP4_OmaDcfTrackEncrypter::GetProcessedSampleSize(AP4_Sample& sample)
|
|
{
|
|
return m_Cipher->GetEncryptedSampleSize(sample);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackEncrypter::ProcessTrack
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfTrackEncrypter::ProcessTrack()
|
|
{
|
|
// original format
|
|
AP4_FrmaAtom* frma = new AP4_FrmaAtom(m_SampleEntry->GetType());
|
|
|
|
// scheme info
|
|
AP4_OdafAtom* odaf = new AP4_OdafAtom(true, 0, AP4_CIPHER_BLOCK_SIZE);
|
|
AP4_OhdrAtom* ohdr = new AP4_OhdrAtom(m_CipherMode,
|
|
m_CipherPadding,
|
|
0,
|
|
m_ContentId.GetChars(),
|
|
m_RightsIssuerUrl.GetChars(),
|
|
m_TextualHeaders.GetData(),
|
|
m_TextualHeaders.GetDataSize());
|
|
AP4_SchmAtom* schm = new AP4_SchmAtom(AP4_PROTECTION_SCHEME_TYPE_OMA,
|
|
AP4_PROTECTION_SCHEME_VERSION_OMA_20);
|
|
|
|
// populate the odkm container
|
|
AP4_ContainerAtom* odkm = new AP4_ContainerAtom(AP4_ATOM_TYPE_ODKM, (AP4_UI32)0, (AP4_UI32)0);
|
|
odkm->AddChild(odaf);
|
|
odkm->AddChild(ohdr);
|
|
|
|
// populate the schi container
|
|
AP4_ContainerAtom* schi = new AP4_ContainerAtom(AP4_ATOM_TYPE_SCHI);
|
|
schi->AddChild(odkm);
|
|
|
|
// populate the sinf container
|
|
AP4_ContainerAtom* sinf = new AP4_ContainerAtom(AP4_ATOM_TYPE_SINF);
|
|
sinf->AddChild(frma);
|
|
sinf->AddChild(schm);
|
|
sinf->AddChild(schi);
|
|
|
|
// add the sinf atom to the sample description
|
|
m_SampleEntry->AddChild(sinf);
|
|
|
|
// change the atom type of the sample description
|
|
m_SampleEntry->SetType(m_Format);
|
|
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfTrackEncrypter::ProcessSample
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfTrackEncrypter::ProcessSample(AP4_DataBuffer& data_in,
|
|
AP4_DataBuffer& data_out)
|
|
{
|
|
AP4_Result result = m_Cipher->EncryptSampleData(data_in,
|
|
data_out,
|
|
m_Counter,
|
|
false);
|
|
if (AP4_FAILED(result)) return result;
|
|
|
|
m_Counter += (data_in.GetDataSize()+AP4_CIPHER_BLOCK_SIZE-1)/AP4_CIPHER_BLOCK_SIZE;
|
|
return AP4_SUCCESS;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfDecryptingProcessor:AP4_OmaDcfDecryptingProcessor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfDecryptingProcessor::AP4_OmaDcfDecryptingProcessor(
|
|
const AP4_ProtectionKeyMap* key_map /* = NULL */,
|
|
AP4_BlockCipherFactory* block_cipher_factory /* = NULL */)
|
|
{
|
|
if (key_map) {
|
|
// copy the keys
|
|
m_KeyMap.SetKeys(*key_map);
|
|
}
|
|
|
|
if (block_cipher_factory == NULL) {
|
|
m_BlockCipherFactory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
} else {
|
|
m_BlockCipherFactory = block_cipher_factory;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfDecryptingProcessor:Initialize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfDecryptingProcessor::Initialize(AP4_AtomParent& top_level,
|
|
AP4_ByteStream& /* stream */,
|
|
ProgressListener* listener)
|
|
{
|
|
// decide which processor to instantiate based on the file type
|
|
AP4_FtypAtom* ftyp = AP4_DYNAMIC_CAST(AP4_FtypAtom, top_level.GetChild(AP4_ATOM_TYPE_FTYP));
|
|
if (ftyp) {
|
|
if (ftyp->GetMajorBrand() == AP4_OMA_DCF_BRAND_ODCF || ftyp->HasCompatibleBrand(AP4_OMA_DCF_BRAND_ODCF)) {
|
|
return AP4_OmaDcfAtomDecrypter::DecryptAtoms(top_level, listener, m_BlockCipherFactory, m_KeyMap);
|
|
} else {
|
|
return AP4_ERROR_INVALID_FORMAT;
|
|
}
|
|
} else {
|
|
return AP4_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfEncryptingProcessor:AP4_OmaDcfEncryptingProcessor
|
|
+---------------------------------------------------------------------*/
|
|
AP4_OmaDcfEncryptingProcessor::AP4_OmaDcfEncryptingProcessor(AP4_OmaDcfCipherMode cipher_mode,
|
|
AP4_BlockCipherFactory* block_cipher_factory) :
|
|
m_CipherMode(cipher_mode)
|
|
{
|
|
if (block_cipher_factory == NULL) {
|
|
m_BlockCipherFactory = &AP4_DefaultBlockCipherFactory::Instance;
|
|
} else {
|
|
m_BlockCipherFactory = block_cipher_factory;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfEncryptingProcessor::Initialize
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Result
|
|
AP4_OmaDcfEncryptingProcessor::Initialize(AP4_AtomParent& top_level,
|
|
AP4_ByteStream& /*stream*/,
|
|
AP4_Processor::ProgressListener* /*listener*/)
|
|
{
|
|
AP4_FtypAtom* ftyp = AP4_DYNAMIC_CAST(AP4_FtypAtom, top_level.GetChild(AP4_ATOM_TYPE_FTYP));
|
|
if (ftyp) {
|
|
// remove the atom, it will be replaced with a new one
|
|
top_level.RemoveChild(ftyp);
|
|
|
|
// keep the existing brand and compatible brands
|
|
AP4_Array<AP4_UI32> compatible_brands;
|
|
compatible_brands.EnsureCapacity(ftyp->GetCompatibleBrands().ItemCount()+1);
|
|
for (unsigned int i=0; i<ftyp->GetCompatibleBrands().ItemCount(); i++) {
|
|
compatible_brands.Append(ftyp->GetCompatibleBrands()[i]);
|
|
}
|
|
|
|
// add the OMA compatible brand if it is not already there
|
|
if (!ftyp->HasCompatibleBrand(AP4_OMA_DCF_BRAND_OPF2)) {
|
|
compatible_brands.Append(AP4_OMA_DCF_BRAND_OPF2);
|
|
}
|
|
|
|
// create a replacement
|
|
AP4_FtypAtom* new_ftyp = new AP4_FtypAtom(ftyp->GetMajorBrand(),
|
|
ftyp->GetMinorVersion(),
|
|
&compatible_brands[0],
|
|
compatible_brands.ItemCount());
|
|
delete ftyp;
|
|
ftyp = new_ftyp;
|
|
} else {
|
|
AP4_UI32 opf2 = AP4_OMA_DCF_BRAND_OPF2;
|
|
ftyp = new AP4_FtypAtom(AP4_FTYP_BRAND_ISOM, 0, &opf2, 1);
|
|
}
|
|
|
|
// insert the ftyp atom as the first child
|
|
return top_level.AddChild(ftyp, 0);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
| AP4_OmaDcfEncryptingProcessor:CreateTrackHandler
|
|
+---------------------------------------------------------------------*/
|
|
AP4_Processor::TrackHandler*
|
|
AP4_OmaDcfEncryptingProcessor::CreateTrackHandler(AP4_TrakAtom* trak)
|
|
{
|
|
// find the stsd atom
|
|
AP4_StsdAtom* stsd = AP4_DYNAMIC_CAST(AP4_StsdAtom, trak->FindChild("mdia/minf/stbl/stsd"));
|
|
|
|
// avoid tracks with no stsd atom (should not happen)
|
|
if (stsd == NULL) return NULL;
|
|
|
|
// only look at the first sample description
|
|
AP4_SampleEntry* entry = stsd->GetSampleEntry(0);
|
|
if (entry == NULL) return NULL;
|
|
|
|
// create a handler for this track if we have a key for it and we know
|
|
// how to map the type
|
|
const AP4_DataBuffer* key;
|
|
const AP4_DataBuffer* iv;
|
|
AP4_UI32 format = 0;
|
|
if (AP4_SUCCEEDED(m_KeyMap.GetKeyAndIv(trak->GetId(), key, iv))) {
|
|
switch (entry->GetType()) {
|
|
case AP4_ATOM_TYPE_MP4A:
|
|
format = AP4_ATOM_TYPE_ENCA;
|
|
break;
|
|
|
|
case AP4_ATOM_TYPE_MP4V:
|
|
case AP4_ATOM_TYPE_AVC1:
|
|
case AP4_ATOM_TYPE_AVC2:
|
|
case AP4_ATOM_TYPE_AVC3:
|
|
case AP4_ATOM_TYPE_AVC4:
|
|
case AP4_ATOM_TYPE_HEV1:
|
|
case AP4_ATOM_TYPE_HVC1:
|
|
format = AP4_ATOM_TYPE_ENCV;
|
|
break;
|
|
|
|
default: {
|
|
// try to find if this is audio or video
|
|
AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, trak->FindChild("mdia/hdlr"));
|
|
if (hdlr) {
|
|
switch (hdlr->GetHandlerType()) {
|
|
case AP4_HANDLER_TYPE_SOUN:
|
|
format = AP4_ATOM_TYPE_ENCA;
|
|
break;
|
|
|
|
case AP4_HANDLER_TYPE_VIDE:
|
|
format = AP4_ATOM_TYPE_ENCV;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (format) {
|
|
const char* content_id = m_PropertyMap.GetProperty(trak->GetId(), "ContentId");
|
|
const char* rights_issuer_url = m_PropertyMap.GetProperty(trak->GetId(), "RightsIssuerUrl");
|
|
AP4_DataBuffer textual_headers;
|
|
AP4_Result result = m_PropertyMap.GetTextualHeaders(trak->GetId(), textual_headers);
|
|
if (AP4_FAILED(result)) textual_headers.SetDataSize(0);
|
|
|
|
// create the block cipher
|
|
AP4_BlockCipher* block_cipher = NULL;
|
|
AP4_BlockCipher::CtrParams ctr_params;
|
|
AP4_BlockCipher::CipherMode cipher_mode;
|
|
const void* cipher_params = NULL;
|
|
if (m_CipherMode == AP4_OMA_DCF_CIPHER_MODE_CBC) {
|
|
cipher_mode = AP4_BlockCipher::CBC;
|
|
} else if (m_CipherMode == AP4_OMA_DCF_CIPHER_MODE_CTR) {
|
|
cipher_mode = AP4_BlockCipher::CTR;
|
|
ctr_params.counter_size = 16;
|
|
cipher_params = &ctr_params;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
result = m_BlockCipherFactory->CreateCipher(AP4_BlockCipher::AES_128,
|
|
AP4_BlockCipher::ENCRYPT,
|
|
cipher_mode,
|
|
cipher_params,
|
|
key->GetData(),
|
|
key->GetDataSize(),
|
|
block_cipher);
|
|
if (AP4_FAILED(result)) return NULL;
|
|
return new AP4_OmaDcfTrackEncrypter(m_CipherMode,
|
|
block_cipher,
|
|
iv->GetData(),
|
|
entry,
|
|
format,
|
|
content_id,
|
|
rights_issuer_url,
|
|
textual_headers.GetData(),
|
|
textual_headers.GetDataSize());
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|