summaryrefslogtreecommitdiffstats
path: root/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp')
-rw-r--r--media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp241
1 files changed, 241 insertions, 0 deletions
diff --git a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
new file mode 100644
index 000000000..87b316563
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -0,0 +1,241 @@
+/*
+ * 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 "psshparser/PsshParser.h"
+#include "gmp-api/gmp-decryption.h"
+#include "mozilla/CheckedInt.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()
+{
+ if (HasKey()) {
+ CK_LOGD("ClearKeyDecryptor dtor; key = %08x...", *(uint32_t*)&mKey[0]);
+ } else {
+ CK_LOGD("ClearKeyDecryptor dtor");
+ }
+}
+
+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.
+ static_assert(sizeof(uintptr_t) == sizeof(uint8_t*),
+ "We need uintptr_t to be exactly the same size as a pointer");
+ mozilla::CheckedInt<uintptr_t> data = reinterpret_cast<uintptr_t>(aBuffer);
+ const uintptr_t endBuffer =
+ reinterpret_cast<uintptr_t>(aBuffer + aBufferSize);
+ uint8_t* iter = &tmp[0];
+ for (size_t i = 0; i < aMetadata.NumSubsamples(); i++) {
+ data += aMetadata.mClearBytes[i];
+ if (!data.isValid() || data.value() > endBuffer) {
+ // Trying to read past the end of the buffer!
+ return GMPCryptoErr;
+ }
+ const uint32_t& cipherBytes = aMetadata.mCipherBytes[i];
+ mozilla::CheckedInt<uintptr_t> dataAfterCipher = data + cipherBytes;
+ if (!dataAfterCipher.isValid() || dataAfterCipher.value() > endBuffer) {
+ // Trying to read past the end of the buffer!
+ return GMPCryptoErr;
+ }
+
+ memcpy(iter, reinterpret_cast<uint8_t*>(data.value()), cipherBytes);
+
+ data = dataAfterCipher;
+ 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(), CENC_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;
+}