Source release v3.2.0
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <arpa/inet.h> // needed for ntoh()
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "crypto_key.h"
|
||||
#include "log.h"
|
||||
@@ -26,6 +27,7 @@ std::string EncodeUint32(unsigned int u) {
|
||||
return s;
|
||||
}
|
||||
const uint32_t kRsaSignatureLength = 256;
|
||||
const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -52,6 +54,26 @@ CryptoSession::~CryptoSession() {
|
||||
Terminate();
|
||||
}
|
||||
|
||||
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
|
||||
OEMCrypto_ProvisioningMethod method;
|
||||
switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) {
|
||||
case OEMCrypto_OEMCertificate:
|
||||
*token_type = kClientTokenOemCert;
|
||||
break;
|
||||
case OEMCrypto_Keybox:
|
||||
*token_type = kClientTokenKeybox;
|
||||
break;
|
||||
case OEMCrypto_DrmCertificate:
|
||||
*token_type = kClientTokenDrmCert;
|
||||
break;
|
||||
case OEMCrypto_ProvisioningError:
|
||||
default:
|
||||
LOGE("OEMCrypto_GetProvisioningMethod failed", method);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CryptoSession::Init() {
|
||||
LOGV("CryptoSession::Init");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
@@ -64,6 +86,9 @@ void CryptoSession::Init() {
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CryptoSession::Terminate() {
|
||||
@@ -83,35 +108,81 @@ void CryptoSession::Terminate() {
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
bool CryptoSession::ValidateKeybox() {
|
||||
LOGV("CryptoSession::ValidateKeybox: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
bool CryptoSession::GetTokenFromKeybox(std::string* token) {
|
||||
OEMCryptoResult status;
|
||||
std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0');
|
||||
// lock is held by caller
|
||||
size_t buf_size = temp_buffer.size();
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
status = OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_);
|
||||
if (status == OEMCrypto_SUCCESS) {
|
||||
token->swap(temp_buffer);
|
||||
return true;
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_IsKeyboxValid(requested_security_level_);
|
||||
return (OEMCrypto_SUCCESS == result);
|
||||
LOGE("CryptoSession::GetTokenFromKeybox : error %d.", status);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetToken(std::string* token) {
|
||||
if (!token) {
|
||||
LOGE("CryptoSession::GetToken : No token passed to method.");
|
||||
bool CryptoSession::GetTokenFromOemCert(std::string* token) {
|
||||
OEMCryptoResult status;
|
||||
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
|
||||
// lock is held by caller
|
||||
bool retrying = false;
|
||||
while (true) {
|
||||
size_t buf_size = temp_buffer.size();
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
|
||||
if (OEMCrypto_SUCCESS == status) {
|
||||
token->swap(temp_buffer);
|
||||
return true;
|
||||
}
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER && !retrying) {
|
||||
temp_buffer.resize(buf_size);
|
||||
retrying = true;
|
||||
continue;
|
||||
}
|
||||
LOGE("CryptoSession::GetTokenFromOemCert : error %d.", status);
|
||||
return false;
|
||||
}
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t bufSize = sizeof(buf);
|
||||
LOGV("CryptoSession::GetToken: Lock");
|
||||
}
|
||||
|
||||
bool CryptoSession::GetClientToken(std::string* token) {
|
||||
if (!token) {
|
||||
LOGE("CryptoSession::GetClientToken : No token passed to method.");
|
||||
return false;
|
||||
}
|
||||
LOGV("CryptoSession::GetClientToken: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_GetKeyData(buf, &bufSize, requested_security_level_);
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
|
||||
// Only keybox is used for client token. All other cases use DRM Cert.
|
||||
if (pre_provision_token_type_ != kClientTokenKeybox) {
|
||||
return false;
|
||||
}
|
||||
return GetTokenFromKeybox(token);
|
||||
}
|
||||
|
||||
bool CryptoSession::GetProvisioningToken(std::string* token) {
|
||||
if (!token) {
|
||||
LOGE("CryptoSession::GetProvisioningToken : No token passed to method.");
|
||||
return false;
|
||||
}
|
||||
LOGV("CryptoSession::GetProvisioningToken: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pre_provision_token_type_ == kClientTokenKeybox) {
|
||||
return GetTokenFromKeybox(token);
|
||||
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
return GetTokenFromOemCert(token);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
token->assign((const char*)buf, (size_t)bufSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmSecurityLevel CryptoSession::GetSecurityLevel() {
|
||||
@@ -148,25 +219,31 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> id;
|
||||
size_t id_length = 32;
|
||||
|
||||
id.resize(id_length);
|
||||
|
||||
LOGV("CryptoSession::GetDeviceUniqueId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts =
|
||||
|
||||
if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
return GetTokenFromOemCert(device_id);
|
||||
} else {
|
||||
// Device's authentication root is a keybox.
|
||||
// Or not. If no keybox, let the OEMCrypto call fail.
|
||||
std::vector<uint8_t> id;
|
||||
size_t id_length = 32;
|
||||
id.resize(id_length);
|
||||
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
|
||||
return true;
|
||||
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CryptoSession::GetApiVersion(uint32_t* version) {
|
||||
@@ -690,6 +767,13 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
sts = OEMCrypto_CopyBuffer(requested_security_level_,
|
||||
params.encrypt_buffer, params.encrypt_length,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
|
||||
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE &&
|
||||
params.encrypt_length > kMaximumChunkSize) {
|
||||
// OEMCrypto_CopyBuffer rejected the buffer as too large, so chunk it up
|
||||
// into 100 KiB sections.
|
||||
sts = CopyBufferInChunks(params, buffer_descriptor);
|
||||
}
|
||||
}
|
||||
if (params.is_encrypted && params.cipher_mode != cipher_mode_) {
|
||||
return INCORRECT_CRYPTO_MODE;
|
||||
@@ -698,7 +782,7 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern_descriptor;
|
||||
pattern_descriptor.encrypt = params.pattern_descriptor.encrypt_blocks;
|
||||
pattern_descriptor.skip = params.pattern_descriptor.skip_blocks;
|
||||
pattern_descriptor.offset = params.pattern_descriptor.offset_blocks;
|
||||
pattern_descriptor.offset = 0; // Deprecated field
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
// Check if key needs to be selected
|
||||
if (params.is_encrypted) {
|
||||
@@ -710,6 +794,26 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, params.subsample_flags);
|
||||
|
||||
|
||||
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
||||
// OEMCrypto_DecryptCENC rejected the buffer as too large, so chunk it up
|
||||
// into sections no more than 100 KiB. The exact chunk size needs to be
|
||||
// an even number of pattern repetitions long or else the pattern will get
|
||||
// out of sync.
|
||||
const size_t pattern_length =
|
||||
(pattern_descriptor.encrypt + pattern_descriptor.skip) *
|
||||
kAes128BlockSize;
|
||||
const size_t chunk_size =
|
||||
pattern_length > 0 ?
|
||||
kMaximumChunkSize - (kMaximumChunkSize % pattern_length) :
|
||||
kMaximumChunkSize;
|
||||
|
||||
if (params.encrypt_length > chunk_size) {
|
||||
sts = DecryptInChunks(params, buffer_descriptor, pattern_descriptor,
|
||||
chunk_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (sts) {
|
||||
@@ -1302,4 +1406,182 @@ size_t CryptoSession::GenericEncryptionBlockSize(
|
||||
}
|
||||
}
|
||||
|
||||
OEMCryptoResult CryptoSession::CopyBufferInChunks(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor) {
|
||||
size_t remaining_encrypt_length = params.encrypt_length;
|
||||
uint8_t subsample_flags = OEMCrypto_FirstSubsample;
|
||||
|
||||
while (remaining_encrypt_length > 0) {
|
||||
// Calculate the size of the next chunk and its offset into the original
|
||||
// buffer.
|
||||
const size_t chunk_size = std::min(remaining_encrypt_length,
|
||||
kMaximumChunkSize);
|
||||
const size_t additional_offset =
|
||||
params.encrypt_length - remaining_encrypt_length;
|
||||
|
||||
// Update the remaining length of the original buffer only after calculating
|
||||
// the new values.
|
||||
remaining_encrypt_length -= chunk_size;
|
||||
|
||||
// Update the destination buffer with the new offset.
|
||||
switch (buffer_descriptor.type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
buffer_descriptor.buffer.clear.address =
|
||||
static_cast<uint8_t*>(params.decrypt_buffer) +
|
||||
params.decrypt_buffer_offset + additional_offset;
|
||||
buffer_descriptor.buffer.clear.max_length =
|
||||
params.decrypt_buffer_length -
|
||||
(params.decrypt_buffer_offset + additional_offset);
|
||||
break;
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
buffer_descriptor.buffer.secure.offset =
|
||||
params.decrypt_buffer_offset + additional_offset;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
// OEMCrypto_BufferType_Direct does not need modification.
|
||||
break;
|
||||
}
|
||||
|
||||
// Re-add "last subsample" flag if this is the last subsample.
|
||||
if (remaining_encrypt_length == 0) {
|
||||
subsample_flags |= OEMCrypto_LastSubsample;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_CopyBuffer(
|
||||
requested_security_level_, params.encrypt_buffer + additional_offset,
|
||||
chunk_size, &buffer_descriptor, subsample_flags);
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Clear any subsample flags before the next loop iteration.
|
||||
subsample_flags = 0;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult CryptoSession::DecryptInChunks(
|
||||
const CdmDecryptionParameters& params,
|
||||
const OEMCrypto_DestBufferDesc& full_buffer_descriptor,
|
||||
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor,
|
||||
size_t max_chunk_size) {
|
||||
size_t remaining_encrypt_length = params.encrypt_length;
|
||||
uint8_t subsample_flags =
|
||||
(params.subsample_flags & OEMCrypto_FirstSubsample) ?
|
||||
OEMCrypto_FirstSubsample : 0;
|
||||
std::vector<uint8_t> iv = *params.iv;
|
||||
|
||||
const size_t pattern_length_in_bytes =
|
||||
(pattern_descriptor.encrypt + pattern_descriptor.skip) *
|
||||
kAes128BlockSize;
|
||||
|
||||
while (remaining_encrypt_length > 0) {
|
||||
// Calculate the size of the next chunk and its offset into the
|
||||
// original buffer.
|
||||
const size_t chunk_size = std::min(remaining_encrypt_length,
|
||||
max_chunk_size);
|
||||
const size_t additional_offset =
|
||||
params.encrypt_length - remaining_encrypt_length;
|
||||
|
||||
// Update the remaining length of the original buffer only after
|
||||
// calculating the new values.
|
||||
remaining_encrypt_length -= chunk_size;
|
||||
|
||||
// Update the destination buffer with the new offset. Because OEMCrypto can
|
||||
// modify the OEMCrypto_DestBufferDesc during the call to
|
||||
// OEMCrypto_DecryptCENC, (and is known to do so on some platforms) a new
|
||||
// OEMCrypto_DestBufferDesc must be allocated for each call.
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor = full_buffer_descriptor;
|
||||
switch (buffer_descriptor.type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
buffer_descriptor.buffer.clear.address += additional_offset;
|
||||
buffer_descriptor.buffer.clear.max_length -= additional_offset;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
buffer_descriptor.buffer.secure.offset += additional_offset;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
// OEMCrypto_BufferType_Direct does not need modification.
|
||||
break;
|
||||
}
|
||||
|
||||
// Re-add "last subsample" flag if this is the last subsample.
|
||||
if (remaining_encrypt_length == 0 &&
|
||||
params.subsample_flags & OEMCrypto_LastSubsample) {
|
||||
subsample_flags |= OEMCrypto_LastSubsample;
|
||||
}
|
||||
|
||||
// block_offset and pattern_descriptor do not need to change because
|
||||
// max_chunk_size is guaranteed to be an even multiple of the
|
||||
// pattern length long, which is also guaranteed to be an exact number
|
||||
// of AES blocks long.
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer + additional_offset,
|
||||
chunk_size, params.is_encrypted, &iv.front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, subsample_flags);
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
return sts;
|
||||
}
|
||||
|
||||
// If we are not yet done, update the IV so that it is valid for the next
|
||||
// iteration.
|
||||
if (remaining_encrypt_length != 0) {
|
||||
if (cipher_mode_ == kCipherModeCtr) {
|
||||
// For CTR modes, update the IV depending on how many encrypted blocks
|
||||
// we passed. Since we calculated the chunk size to be an even number
|
||||
// of crypto blocks and pattern repetitions in size, we can do a
|
||||
// simplified calculation for this.
|
||||
uint64_t encrypted_blocks_passed = 0;
|
||||
if (pattern_length_in_bytes == 0) {
|
||||
encrypted_blocks_passed = chunk_size / kAes128BlockSize;
|
||||
} else {
|
||||
const size_t pattern_repetitions_passed =
|
||||
chunk_size / pattern_length_in_bytes;
|
||||
encrypted_blocks_passed =
|
||||
pattern_repetitions_passed * pattern_descriptor.encrypt;
|
||||
}
|
||||
IncrementIV(encrypted_blocks_passed, &iv);
|
||||
} else if (cipher_mode_ == kCipherModeCbc) {
|
||||
// For CBC modes, use the previous ciphertext block.
|
||||
|
||||
// Stash the last crypto block in the IV. We don't have to handle
|
||||
// partial crypto blocks here because we know we broke the buffer into
|
||||
// chunks along even crypto block boundaries.
|
||||
const uint8_t* const buffer_end =
|
||||
params.encrypt_buffer + additional_offset + chunk_size;
|
||||
|
||||
const uint8_t* block_end = NULL;
|
||||
if (pattern_length_in_bytes == 0) {
|
||||
// For cbc1, the last encrypted block is the last block of the
|
||||
// subsample.
|
||||
block_end = buffer_end;
|
||||
} else {
|
||||
// For cbcs, we must look for the last encrypted block, which is
|
||||
// probably not the last block of the subsample. Luckily, since the
|
||||
// buffer size is guaranteed to be an even number of pattern
|
||||
// repetitions long, we can use the pattern to know how many blocks to
|
||||
// look back.
|
||||
block_end = buffer_end - kAes128BlockSize * pattern_descriptor.skip;
|
||||
}
|
||||
|
||||
iv.assign(block_end - kAes128BlockSize, block_end);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any subsample flags before the next loop iteration.
|
||||
subsample_flags = 0;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
void CryptoSession::IncrementIV(uint64_t increase_by,
|
||||
std::vector<uint8_t>* iv_out) {
|
||||
std::vector<uint8_t>& iv = *iv_out;
|
||||
uint64_t* counter_buffer = reinterpret_cast<uint64_t*>(&iv[8]);
|
||||
(*counter_buffer) = htonll64(ntohll64(*counter_buffer) + increase_by);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user