/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | DcfParser_ParseV2Header +---------------------------------------------------------------------*/ static BLT_Result DcfParser_ParseV2Header(DcfParser* self, ATX_InputStream* stream) { /* rewind the byte stream */ ATX_InputStream_Seek(stream, 0); /* parse the atoms from the stream */ AP4_ByteStream* mp4_stream = new ATX_InputStream_To_AP4_ByteStream_Adapter(stream); AP4_AtomParent atoms; AP4_Result result = AP4_DefaultAtomFactory::Instance.CreateAtomsFromStream(*mp4_stream, atoms); mp4_stream->Release(); AP4_ByteStream* decrypting_stream = NULL; AP4_ContainerAtom* odrm = dynamic_cast<AP4_ContainerAtom*>(atoms.GetChild(AP4_ATOM_TYPE_ODRM)); if (odrm) { AP4_OdheAtom* odhe = dynamic_cast<AP4_OdheAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODHE)); AP4_OddaAtom* odda = dynamic_cast<AP4_OddaAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODDA)); if (odhe && odda) { const char* content_id = ""; /* get the content ID */ AP4_OhdrAtom* ohdr = dynamic_cast<AP4_OhdrAtom*>(odhe->GetChild(AP4_ATOM_TYPE_OHDR)); if (ohdr) { content_id = ohdr->GetContentId().GetChars(); } /* get the content key */ NPT_DataBuffer key; result = DcfParser_GetContentKey(self, content_id, key); if (BLT_FAILED(result)) { ATX_LOG_FINE_2("GetKeyForContent(%s) returned %d", content_id, result); return BLT_ERROR_NO_MEDIA_KEY; } /* create the decrypting stream */ result = AP4_OmaDcfAtomDecrypter::CreateDecryptingStream(*odrm, key.GetData(), key.GetDataSize(), self->cipher_factory, decrypting_stream); if (AP4_SUCCEEDED(result)) { /* update the content type */ ATX_CopyStringN(self->input.content_type, odhe->GetContentType().GetChars(), sizeof(self->input.content_type)); /* update the encrypted size */ self->input.encrypted_size = odda->GetEncryptedDataLength(); } } } /* check that we have found what we needed in the atoms */ if (decrypting_stream == NULL) return BLT_ERROR_INVALID_MEDIA_FORMAT; /* update the output size */ AP4_LargeSize plaintext_size = 0; if (AP4_SUCCEEDED(decrypting_stream->GetSize(plaintext_size))) { self->output.size = plaintext_size; } else { self->output.size = self->input.encrypted_size; } /* create a reverse adapter */ result = AP4_ByteStream_To_ATX_InputStream_Adapter_Create(decrypting_stream, &self->output.stream); decrypting_stream->Release(); return BLT_SUCCESS; }