gecko-dev/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
Chris Pearce d6743b5aff Bug 1186406 - Copy input to ClearKey's decoder, so we can return its containing shmem earlier. r=gerald
We're failing in the "Very rough kill-switch" case in
GMPVideoDecoderParent::Decode() we find that too many shmems are in use when we
come to send a "Decode" message to the GMP, and that causes an error which
percolates up to cause the test failure.

This patch changes gmp-clearkey to copy the input encrypted and compressed
sample and immediately return the shmem to the parent process. We are
copying the data anyway when we decrypt, so we can rejigg things so that we
don't actually end up doing a second copy.
2015-12-01 18:13:58 +13:00

223 lines
5.9 KiB
C++

/*
* Copyright 2015, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <vector>
#include "ClearKeyDecryptionManager.h"
#include "gmp-api/gmp-decryption.h"
#include <assert.h>
class ClearKeyDecryptor : public RefCounted
{
public:
ClearKeyDecryptor();
void InitKey(const Key& aKey);
bool HasKey() const { return !!mKey.size(); }
GMPErr Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
const CryptoMetaData& aMetadata);
const Key& DecryptionKey() const { return mKey; }
private:
~ClearKeyDecryptor();
Key mKey;
};
/* static */ ClearKeyDecryptionManager* ClearKeyDecryptionManager::sInstance = nullptr;
/* static */ ClearKeyDecryptionManager*
ClearKeyDecryptionManager::Get()
{
if (!sInstance) {
sInstance = new ClearKeyDecryptionManager();
}
return sInstance;
}
ClearKeyDecryptionManager::ClearKeyDecryptionManager()
{
CK_LOGD("ClearKeyDecryptionManager::ClearKeyDecryptionManager");
}
ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
{
CK_LOGD("ClearKeyDecryptionManager::~ClearKeyDecryptionManager");
sInstance = nullptr;
for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
it->second->Release();
}
mDecryptors.clear();
}
bool
ClearKeyDecryptionManager::HasSeenKeyId(const KeyId& aKeyId) const
{
CK_LOGD("ClearKeyDecryptionManager::SeenKeyId %s", mDecryptors.find(aKeyId) != mDecryptors.end() ? "t" : "f");
return mDecryptors.find(aKeyId) != mDecryptors.end();
}
bool
ClearKeyDecryptionManager::IsExpectingKeyForKeyId(const KeyId& aKeyId) const
{
CK_LOGD("ClearKeyDecryptionManager::IsExpectingKeyForId %08x...", *(uint32_t*)&aKeyId[0]);
const auto& decryptor = mDecryptors.find(aKeyId);
return decryptor != mDecryptors.end() && !decryptor->second->HasKey();
}
bool
ClearKeyDecryptionManager::HasKeyForKeyId(const KeyId& aKeyId) const
{
CK_LOGD("ClearKeyDecryptionManager::HasKeyForKeyId");
const auto& decryptor = mDecryptors.find(aKeyId);
return decryptor != mDecryptors.end() && decryptor->second->HasKey();
}
const Key&
ClearKeyDecryptionManager::GetDecryptionKey(const KeyId& aKeyId)
{
assert(HasKeyForKeyId(aKeyId));
return mDecryptors[aKeyId]->DecryptionKey();
}
void
ClearKeyDecryptionManager::InitKey(KeyId aKeyId, Key aKey)
{
CK_LOGD("ClearKeyDecryptionManager::InitKey %08x...", *(uint32_t*)&aKeyId[0]);
if (IsExpectingKeyForKeyId(aKeyId)) {
mDecryptors[aKeyId]->InitKey(aKey);
}
}
void
ClearKeyDecryptionManager::ExpectKeyId(KeyId aKeyId)
{
CK_LOGD("ClearKeyDecryptionManager::ExpectKeyId %08x...", *(uint32_t*)&aKeyId[0]);
if (!HasSeenKeyId(aKeyId)) {
mDecryptors[aKeyId] = new ClearKeyDecryptor();
}
mDecryptors[aKeyId]->AddRef();
}
void
ClearKeyDecryptionManager::ReleaseKeyId(KeyId aKeyId)
{
CK_LOGD("ClearKeyDecryptionManager::ReleaseKeyId");
assert(HasSeenKeyId(aKeyId));
ClearKeyDecryptor* decryptor = mDecryptors[aKeyId];
if (!decryptor->Release()) {
mDecryptors.erase(aKeyId);
}
}
GMPErr
ClearKeyDecryptionManager::Decrypt(std::vector<uint8_t>& aBuffer,
const CryptoMetaData& aMetadata)
{
return Decrypt(&aBuffer[0], aBuffer.size(), aMetadata);
}
GMPErr
ClearKeyDecryptionManager::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
const CryptoMetaData& aMetadata)
{
CK_LOGD("ClearKeyDecryptionManager::Decrypt");
if (!HasKeyForKeyId(aMetadata.mKeyId)) {
return GMPNoKeyErr;
}
return mDecryptors[aMetadata.mKeyId]->Decrypt(aBuffer, aBufferSize, aMetadata);
}
ClearKeyDecryptor::ClearKeyDecryptor()
{
CK_LOGD("ClearKeyDecryptor ctor");
}
ClearKeyDecryptor::~ClearKeyDecryptor()
{
CK_LOGD("ClearKeyDecryptor dtor; key = %08x...", *(uint32_t*)&mKey[0]);
}
void
ClearKeyDecryptor::InitKey(const Key& aKey)
{
mKey = aKey;
}
GMPErr
ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
const CryptoMetaData& aMetadata)
{
CK_LOGD("ClearKeyDecryptor::Decrypt");
// If the sample is split up into multiple encrypted subsamples, we need to
// stitch them into one continuous buffer for decryption.
std::vector<uint8_t> tmp(aBufferSize);
if (aMetadata.NumSubsamples()) {
// Take all encrypted parts of subsamples and stitch them into one
// continuous encrypted buffer.
uint8_t* data = aBuffer;
uint8_t* iter = &tmp[0];
for (size_t i = 0; i < aMetadata.NumSubsamples(); i++) {
data += aMetadata.mClearBytes[i];
uint32_t cipherBytes = aMetadata.mCipherBytes[i];
memcpy(iter, data, cipherBytes);
data += cipherBytes;
iter += cipherBytes;
}
tmp.resize((size_t)(iter - &tmp[0]));
} else {
memcpy(&tmp[0], aBuffer, aBufferSize);
}
assert(aMetadata.mIV.size() == 8 || aMetadata.mIV.size() == 16);
std::vector<uint8_t> iv(aMetadata.mIV);
iv.insert(iv.end(), CLEARKEY_KEY_LEN - aMetadata.mIV.size(), 0);
ClearKeyUtils::DecryptAES(mKey, tmp, iv);
if (aMetadata.NumSubsamples()) {
// Take the decrypted buffer, split up into subsamples, and insert those
// subsamples back into their original position in the original buffer.
uint8_t* data = aBuffer;
uint8_t* iter = &tmp[0];
for (size_t i = 0; i < aMetadata.NumSubsamples(); i++) {
data += aMetadata.mClearBytes[i];
uint32_t cipherBytes = aMetadata.mCipherBytes[i];
memcpy(data, iter, cipherBytes);
data += cipherBytes;
iter += cipherBytes;
}
} else {
memcpy(aBuffer, &tmp[0], aBufferSize);
}
return GMPNoErr;
}