Example #1
0
/*----------------------------------------------------------------------
|   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;
}
Example #2
0
/*----------------------------------------------------------------------
|   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;
}