mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-03 18:47:53 +00:00
Bug 1516673 - Adds CBCS encryption scheme functionality. r=bryce
Differential Revision: https://phabricator.services.mozilla.com/D60053 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
b39107855d
commit
ea383d394a
@ -24,6 +24,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Span.h"
|
||||
|
||||
using namespace cdm;
|
||||
|
||||
@ -169,12 +170,62 @@ Status ClearKeyDecryptor::Decrypt(uint8_t* aBuffer, uint32_t aBufferSize,
|
||||
// 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);
|
||||
static_assert(sizeof(uintptr_t) == sizeof(uint8_t*),
|
||||
"We need uintptr_t to be exactly the same size as a pointer");
|
||||
|
||||
// Decrypt CBCS case:
|
||||
if (aMetadata.mEncryptionScheme == EncryptionScheme::kCbcs) {
|
||||
mozilla::CheckedInt<uintptr_t> data = reinterpret_cast<uintptr_t>(aBuffer);
|
||||
if (!data.isValid()) {
|
||||
return Status::kDecryptError;
|
||||
}
|
||||
const uintptr_t endBuffer =
|
||||
reinterpret_cast<uintptr_t>(aBuffer + aBufferSize);
|
||||
|
||||
if (aMetadata.NumSubsamples() == 0) {
|
||||
if (data.value() > endBuffer) {
|
||||
return Status::kDecryptError;
|
||||
}
|
||||
mozilla::Span<uint8_t> encryptedSpan = mozilla::MakeSpan(
|
||||
reinterpret_cast<uint8_t*>(data.value()), aBufferSize);
|
||||
if (!ClearKeyUtils::DecryptCbcs(mKey, aMetadata.mIV, encryptedSpan,
|
||||
aMetadata.mCryptByteBlock,
|
||||
aMetadata.mSkipByteBlock)) {
|
||||
return Status::kDecryptError;
|
||||
}
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aMetadata.NumSubsamples(); i++) {
|
||||
data += aMetadata.mClearBytes[i];
|
||||
if (!data.isValid() || data.value() > endBuffer) {
|
||||
return Status::kDecryptError;
|
||||
}
|
||||
mozilla::CheckedInt<uintptr_t> dataAfterCipher =
|
||||
data + aMetadata.mCipherBytes[i];
|
||||
if (!dataAfterCipher.isValid() || dataAfterCipher.value() > endBuffer) {
|
||||
// Trying to read past the end of the buffer!
|
||||
return Status::kDecryptError;
|
||||
}
|
||||
mozilla::Span<uint8_t> encryptedSpan = mozilla::MakeSpan(
|
||||
reinterpret_cast<uint8_t*>(data.value()), aMetadata.mCipherBytes[i]);
|
||||
if (!ClearKeyUtils::DecryptCbcs(mKey, aMetadata.mIV, encryptedSpan,
|
||||
aMetadata.mCryptByteBlock,
|
||||
aMetadata.mSkipByteBlock)) {
|
||||
return Status::kDecryptError;
|
||||
}
|
||||
data += aMetadata.mCipherBytes[i];
|
||||
if (!data.isValid()) {
|
||||
return Status::kDecryptError;
|
||||
}
|
||||
return Status::kSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt CENC case:
|
||||
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);
|
||||
|
@ -42,14 +42,16 @@ class CryptoMetaData {
|
||||
return;
|
||||
}
|
||||
|
||||
mEncryptionScheme = aInputBuffer->encryption_scheme;
|
||||
Assign(mKeyId, aInputBuffer->key_id, aInputBuffer->key_id_size);
|
||||
Assign(mIV, aInputBuffer->iv, aInputBuffer->iv_size);
|
||||
mCryptByteBlock = aInputBuffer->pattern.crypt_byte_block;
|
||||
mSkipByteBlock = aInputBuffer->pattern.skip_byte_block;
|
||||
|
||||
for (uint32_t i = 0; i < aInputBuffer->num_subsamples; ++i) {
|
||||
const cdm::SubsampleEntry& subsample = aInputBuffer->subsamples[i];
|
||||
|
||||
mCipherBytes.push_back(subsample.cipher_bytes);
|
||||
mClearBytes.push_back(subsample.clear_bytes);
|
||||
mCipherBytes.push_back(subsample.cipher_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,8 +65,11 @@ class CryptoMetaData {
|
||||
return mClearBytes.size();
|
||||
}
|
||||
|
||||
cdm::EncryptionScheme mEncryptionScheme;
|
||||
std::vector<uint8_t> mKeyId;
|
||||
std::vector<uint8_t> mIV;
|
||||
uint32_t mCryptByteBlock;
|
||||
uint32_t mSkipByteBlock;
|
||||
std::vector<uint32_t> mClearBytes;
|
||||
std::vector<uint32_t> mCipherBytes;
|
||||
};
|
||||
|
@ -16,36 +16,52 @@
|
||||
|
||||
#include "ClearKeyUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <cctype>
|
||||
#include <ctype.h>
|
||||
#include <memory.h>
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
// This include is required in order for content_decryption_module to work // on
|
||||
// Unix systems.
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "ArrayUtils.h"
|
||||
#include "BigEndian.h"
|
||||
#include "ClearKeyBase64.h"
|
||||
// This include is required in order for content_decryption_module to work
|
||||
// on Unix systems.
|
||||
#include "stddef.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "pk11pub.h"
|
||||
#include "prerror.h"
|
||||
#include "psshparser/PsshParser.h"
|
||||
#include "secmodt.h"
|
||||
|
||||
using namespace cdm;
|
||||
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::vector;
|
||||
|
||||
struct DeleteHelper {
|
||||
void operator()(PK11Context* value) { PK11_DestroyContext(value, true); }
|
||||
void operator()(PK11SlotInfo* value) { PK11_FreeSlot(value); }
|
||||
void operator()(PK11SymKey* value) { PK11_FreeSymKey(value); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct MaybeDeleteHelper {
|
||||
void operator()(T* ptr) {
|
||||
if (ptr) {
|
||||
DeleteHelper del;
|
||||
del(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void CK_Log(const char* aFmt, ...) {
|
||||
FILE* out = stdout;
|
||||
|
||||
@ -84,6 +100,67 @@ void CK_LogArray(const char* prepend, const uint8_t* aData,
|
||||
CK_LOGD("%s%s", prepend, data.c_str());
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool ClearKeyUtils::DecryptCbcs(const vector<uint8_t>& aKey,
|
||||
const vector<uint8_t>& aIV,
|
||||
mozilla::Span<uint8_t> aSubsample,
|
||||
uint32_t aCryptByteBlock,
|
||||
uint32_t aSkipByteBlock) {
|
||||
assert(aKey.size() == CENC_KEY_LEN);
|
||||
assert(aIV.size() == CENC_KEY_LEN);
|
||||
assert(aCryptByteBlock <= 0xFF);
|
||||
assert(aSkipByteBlock <= 0xFF);
|
||||
|
||||
std::unique_ptr<PK11SlotInfo, MaybeDeleteHelper<PK11SlotInfo>> slot(
|
||||
PK11_GetInternalKeySlot());
|
||||
|
||||
if (!slot.get()) {
|
||||
CK_LOGE("Failed to get internal PK11 slot");
|
||||
return false;
|
||||
}
|
||||
|
||||
SECItem keyItem = {siBuffer, (unsigned char*)&aKey[0], CENC_KEY_LEN};
|
||||
SECItem ivItem = {siBuffer, (unsigned char*)&aIV[0], CENC_KEY_LEN};
|
||||
|
||||
std::unique_ptr<PK11SymKey, MaybeDeleteHelper<PK11SymKey>> key(
|
||||
PK11_ImportSymKey(slot.get(), CKM_AES_CBC, PK11_OriginUnwrap, CKA_DECRYPT,
|
||||
&keyItem, nullptr));
|
||||
|
||||
if (!key.get()) {
|
||||
CK_LOGE("Failed to import sym key");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<PK11Context, MaybeDeleteHelper<PK11Context>> ctx(
|
||||
PK11_CreateContextBySymKey(CKM_AES_CBC, CKA_DECRYPT, key.get(), &ivItem));
|
||||
|
||||
uint8_t* encryptedSubsample = &aSubsample[0];
|
||||
const uint32_t BLOCK_SIZE = 16;
|
||||
const uint32_t skipBytes = aSkipByteBlock * BLOCK_SIZE;
|
||||
const uint32_t totalBlocks = aSubsample.Length() / BLOCK_SIZE;
|
||||
uint32_t blocksProcessed = 0;
|
||||
|
||||
while (blocksProcessed < totalBlocks) {
|
||||
uint32_t blocksToDecrypt = aCryptByteBlock <= totalBlocks - blocksProcessed
|
||||
? aCryptByteBlock
|
||||
: totalBlocks - blocksProcessed;
|
||||
uint32_t bytesToDecrypt = blocksToDecrypt * BLOCK_SIZE;
|
||||
int outLen;
|
||||
SECStatus rv;
|
||||
rv = PK11_CipherOp(ctx.get(), encryptedSubsample, &outLen, bytesToDecrypt,
|
||||
encryptedSubsample, bytesToDecrypt);
|
||||
if (rv != SECSuccess) {
|
||||
CK_LOGE("PK11_CipherOp() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
encryptedSubsample += skipBytes + bytesToDecrypt;
|
||||
blocksProcessed += aSkipByteBlock + blocksToDecrypt;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool ClearKeyUtils::DecryptAES(const vector<uint8_t>& aKey,
|
||||
vector<uint8_t>& aData, vector<uint8_t>& aIV) {
|
||||
|
@ -17,15 +17,17 @@
|
||||
#ifndef __ClearKeyUtils_h__
|
||||
#define __ClearKeyUtils_h__
|
||||
|
||||
#include <assert.h>
|
||||
// stdef.h is required for content_decryption_module to work on Unix systems.
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
// This include is required in order for content_decryption_module to work
|
||||
// on Unix systems.
|
||||
#include "stddef.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "pk11pub.h"
|
||||
|
||||
#if 0
|
||||
void CK_Log(const char* aFmt, ...);
|
||||
@ -64,6 +66,11 @@ struct KeyIdPair {
|
||||
|
||||
class ClearKeyUtils {
|
||||
public:
|
||||
static bool DecryptCbcs(const std::vector<uint8_t>& aKey,
|
||||
const std::vector<uint8_t>& aIV,
|
||||
mozilla::Span<uint8_t> aSubsampleEncryptedRange,
|
||||
uint32_t aCryptByteBlocks, uint32_t aSkipByteBlocks);
|
||||
|
||||
static bool DecryptAES(const std::vector<uint8_t>& aKey,
|
||||
std::vector<uint8_t>& aData,
|
||||
std::vector<uint8_t>& aIV);
|
||||
|
Loading…
Reference in New Issue
Block a user