Refactor OEMCrypto mock and its unit tests
This is a copy of the Widevine CL: https://widevine-internal-review.googlesource.com/#/c/9708/ This CL refactors some of code in oemcrypto/mock and oemcrypto/test in preparation for adding usage table code. Change-Id: I7e58c8ecd6d92b3e177cb915733212fcad645485
This commit is contained in:
@@ -79,19 +79,13 @@ void SessionKeyTable::Remove(const KeyId key_id) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SessionKeyTable::UpdateDuration(const KeyControlBlock& control) {
|
||||
void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) {
|
||||
for(KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) {
|
||||
if (!it->second->UpdateDuration(control)) {
|
||||
return false;
|
||||
}
|
||||
it->second->UpdateDuration(control);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SessionContext::Open() {
|
||||
}
|
||||
|
||||
void SessionContext::Close() {
|
||||
SessionContext::~SessionContext() {
|
||||
}
|
||||
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
@@ -235,7 +229,7 @@ bool SessionContext::GenerateSignature(const uint8_t* message,
|
||||
}
|
||||
|
||||
unsigned int md_len = *signature_length;
|
||||
if (HMAC(EVP_sha256(), &mac_key_client_[0], SHA256_DIGEST_LENGTH,
|
||||
if (HMAC(EVP_sha256(), &mac_key_client_[0], mac_key_client_.size(),
|
||||
message, message_length, signature, &md_len)) {
|
||||
*signature_length = md_len;
|
||||
return true;
|
||||
@@ -331,7 +325,7 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message,
|
||||
}
|
||||
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
|
||||
unsigned int md_len = SHA256_DIGEST_LENGTH;
|
||||
if (!HMAC(EVP_sha256(), &mac_key_server_[0], SHA256_DIGEST_LENGTH,
|
||||
if (!HMAC(EVP_sha256(), &mac_key_server_[0], mac_key_server_.size(),
|
||||
given_message, message_length, computed_signature, &md_len)) {
|
||||
LOGE("ValidateMessage: Could not compute signature.");
|
||||
return false;
|
||||
@@ -346,75 +340,14 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::ParseKeyControl(
|
||||
const std::vector<uint8_t>& key_control_string,
|
||||
KeyControlBlock& key_control_block) {
|
||||
|
||||
key_control_block.Invalidate();
|
||||
|
||||
if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) {
|
||||
LOGD("ParseKeyControl: wrong size.");
|
||||
return false;
|
||||
}
|
||||
if (!key_control_block.SetFromString(key_control_string)) {
|
||||
LOGE("KCB: BAD Size or Structure");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("KCB:");
|
||||
LOGD(" valid: %d", key_control_block.valid());
|
||||
LOGD(" duration: %d", key_control_block.duration());
|
||||
LOGD(" nonce: %08X", key_control_block.nonce());
|
||||
LOGD(" magic: %08X", key_control_block.verification());
|
||||
LOGD(" bits: %08X", key_control_block.control_bits());
|
||||
switch(key_control_block.control_bits() & kControlReplayMask) {
|
||||
case kControlNonceRequired:
|
||||
LOGD(" bits kControlReplay kControlNonceRequired.");
|
||||
break;
|
||||
case kControlNonceOrEntry:
|
||||
LOGD(" bits kControlReplay kControlNonceOrEntry.");
|
||||
break;
|
||||
default:
|
||||
LOGD(" bits kControlReplay unset.");
|
||||
break;
|
||||
}
|
||||
LOGD(" bits kControlKDCPVersion 0x%02x.",
|
||||
(key_control_block.control_bits() & kControlHDCPVersionMask)
|
||||
>> kControlHDCPVersionShift);
|
||||
LOGD(" bit kControlAllowEncrypt %s.",
|
||||
(key_control_block.control_bits() & kControlAllowEncrypt) ? "set" : "unset");
|
||||
LOGD(" bit kControlAllowDecrypt %s.",
|
||||
(key_control_block.control_bits() & kControlAllowDecrypt) ? "set" : "unset");
|
||||
LOGD(" bit kControlAllowSign %s.",
|
||||
(key_control_block.control_bits() & kControlAllowSign) ? "set" : "unset");
|
||||
LOGD(" bit kControlAllowVerify %s.",
|
||||
(key_control_block.control_bits() & kControlAllowVerify) ? "set" : "unset");
|
||||
LOGD(" bit kControlObserveDataPath %s.",
|
||||
(key_control_block.control_bits() & kControlObserveDataPath) ? "set" : "unset");
|
||||
LOGD(" bit kControlObserveHDCP %s.",
|
||||
(key_control_block.control_bits() & kControlObserveHDCP) ? "set" : "unset");
|
||||
LOGD(" bit kControlObserveCGMS %s.",
|
||||
(key_control_block.control_bits() & kControlObserveCGMS) ? "set" : "unset");
|
||||
LOGD(" bit kControlDataPathSecure %s.",
|
||||
(key_control_block.control_bits() & kControlDataPathSecure) ? "set" : "unset");
|
||||
LOGD(" bit kControlNonceEnabled %s.",
|
||||
(key_control_block.control_bits() & kControlNonceEnabled) ? "set" : "unset");
|
||||
LOGD(" bit kControlHDCPRequired %s.",
|
||||
(key_control_block.control_bits() & kControlHDCPRequired) ? "set" : "unset");
|
||||
uint32_t cgms_bits = key_control_block.control_bits() & 0x3;
|
||||
const char* cgms_values[4] = {"free", "BAD", "once", "never"};
|
||||
LOGD(" CGMS = %s", cgms_values[cgms_bits]);
|
||||
|
||||
if (!key_control_block.Validate()) {
|
||||
LOGE("KCB: BAD Signature");
|
||||
return false;
|
||||
}
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled)
|
||||
&& (!CheckNonce(key_control_block.nonce()))) {
|
||||
bool SessionContext::CheckNonceOrPST(KeyControlBlock& key_control_block,
|
||||
const std::vector<uint8_t>& pst) {
|
||||
// TODO(fredgc): look at replay control bits, too.
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled) &&
|
||||
(!CheckNonce(key_control_block.nonce()))) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -427,19 +360,79 @@ uint32_t SessionContext::CurrentTimer() {
|
||||
return now - timer_start_;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::LoadKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_keys, size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
// Validate message signature
|
||||
if (!ValidateMessage(message, message_length, signature, signature_length)) {
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
StartTimer();
|
||||
|
||||
// Decrypt and install keys in key object
|
||||
// Each key will have a key control block. They will all have the same nonce.
|
||||
bool status = true;
|
||||
std::vector<uint8_t> key_id;
|
||||
std::vector<uint8_t> enc_key_data;
|
||||
std::vector<uint8_t> key_data_iv;
|
||||
std::vector<uint8_t> key_control;
|
||||
std::vector<uint8_t> key_control_iv;
|
||||
std::vector<uint8_t> pstv(pst, pst + pst_length);
|
||||
for (unsigned int i = 0; i < num_keys; i++) {
|
||||
key_id.assign(key_array[i].key_id,
|
||||
key_array[i].key_id + key_array[i].key_id_length);
|
||||
enc_key_data.assign(key_array[i].key_data,
|
||||
key_array[i].key_data + key_array[i].key_data_length);
|
||||
key_data_iv.assign(key_array[i].key_data_iv,
|
||||
key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE);
|
||||
if (key_array[i].key_control == NULL) {
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
key_control.assign(key_array[i].key_control,
|
||||
key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE);
|
||||
key_control_iv.assign(key_array[i].key_control_iv,
|
||||
key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE);
|
||||
|
||||
if (!InstallKey(key_id, enc_key_data, key_data_iv, key_control,
|
||||
key_control_iv, pstv)) {
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FlushNonces();
|
||||
if (!status) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
|
||||
// enc_mac_key can be NULL if license renewal is not supported
|
||||
if (enc_mac_keys != NULL) {
|
||||
// V2.1 license protocol: update mac keys after processing license response
|
||||
const std::vector<uint8_t> enc_mac_keys_str = std::vector<uint8_t>(
|
||||
enc_mac_keys, enc_mac_keys + 2 * wvcdm::MAC_KEY_SIZE);
|
||||
const std::vector<uint8_t> enc_mac_key_iv_str = std::vector<uint8_t>(
|
||||
enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE);
|
||||
|
||||
if (!UpdateMacKeys(enc_mac_keys_str, enc_mac_key_iv_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
bool SessionContext::InstallKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_data,
|
||||
const std::vector<uint8_t>& key_data_iv,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv) {
|
||||
|
||||
const std::vector<uint8_t>& key_control_iv,
|
||||
const std::vector<uint8_t>& pst) {
|
||||
// Decrypt encrypted key_data using derived encryption key and offered iv
|
||||
std::vector<uint8_t> content_key;
|
||||
std::vector<uint8_t> key_control_str;
|
||||
KeyControlBlock key_control_block;
|
||||
|
||||
if (!ce_->DecryptMessage(this, encryption_key_, key_data_iv,
|
||||
key_data, &content_key)) {
|
||||
if (!DecryptMessage(encryption_key_, key_data_iv, key_data, &content_key)) {
|
||||
LOGE("[Installkey(): Could not decrypt key data]");
|
||||
return false;
|
||||
}
|
||||
@@ -456,27 +449,94 @@ bool SessionContext::InstallKey(const KeyId& key_id,
|
||||
// Key control must be supplied by license server
|
||||
if (key_control.empty()) {
|
||||
LOGE("[Installkey(): WARNING: No Key Control]");
|
||||
key_control_block.Invalidate();
|
||||
return false;
|
||||
} else {
|
||||
if (key_control_iv.empty()) {
|
||||
LOGE("[Installkey(): ERROR: No Key Control IV]");
|
||||
return false;
|
||||
}
|
||||
if (!ce_->DecryptMessage(this, content_key, key_control_iv,
|
||||
key_control, &key_control_str)) {
|
||||
LOGE("[Installkey(): ERROR: Could not decrypt content key]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (key_control_iv.empty()) {
|
||||
LOGE("[Installkey(): ERROR: No Key Control IV]");
|
||||
return false;
|
||||
}
|
||||
if (!DecryptMessage(content_key, key_control_iv, key_control,
|
||||
&key_control_str)) {
|
||||
LOGE("[Installkey(): ERROR: Could not decrypt content key]");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ParseKeyControl(key_control_str, key_control_block)) {
|
||||
LOGE("Error parsing key control.");
|
||||
KeyControlBlock key_control_block(key_control_str);
|
||||
if (!key_control_block.valid()) {
|
||||
LOGE("Error parsing key control.");
|
||||
return false;
|
||||
}
|
||||
if (!CheckNonceOrPST(key_control_block, pst)) {
|
||||
LOGE("Failed Nonce or PST check.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Key key(content_key, key_control_block);
|
||||
session_keys_.Insert(key_id, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::RefreshKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv) {
|
||||
if (key_id.empty()) {
|
||||
// Key control is not encrypted if key id is NULL
|
||||
KeyControlBlock key_control_block(key_control);
|
||||
if (!key_control_block.valid()) {
|
||||
LOGD("Parse key control error.");
|
||||
return false;
|
||||
}
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled) &&
|
||||
(!CheckNonce(key_control_block.nonce()))) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
}
|
||||
// Apply duration to all keys in this session
|
||||
session_keys_.UpdateDuration(key_control_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
Key* content_key = session_keys_.Find(key_id);
|
||||
|
||||
if (NULL == content_key) {
|
||||
LOGD("Error: no matching content key.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key_control.empty()) {
|
||||
LOGD("Error: no key_control.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> content_key_value = content_key->value();
|
||||
|
||||
// Decrypt encrypted key control block
|
||||
std::vector<uint8_t> control;
|
||||
if (key_control_iv.empty()) {
|
||||
// TODO(fredg): get confirmation from server team this is valid.
|
||||
LOGD("Key control block is NOT encrypted.");
|
||||
control = key_control;
|
||||
} else {
|
||||
// TODO(fredg): get confirmation from server team this is valid, too.
|
||||
LOGD("Key control block is encrypted.");
|
||||
if (!DecryptMessage(content_key_value, key_control_iv, key_control,
|
||||
&control)) {
|
||||
LOGD("Error decrypting key control block.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Key key(KEYTYPE_CONTENT, content_key, key_control_block);
|
||||
session_keys_.Insert(key_id, key);
|
||||
KeyControlBlock key_control_block(control);
|
||||
if (!key_control_block.valid()) {
|
||||
LOGD("Parse key control error.");
|
||||
return false;
|
||||
}
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled) &&
|
||||
(!CheckNonce(key_control_block.nonce()))) {
|
||||
LOGE("KCB: BAD Nonce");
|
||||
return false;
|
||||
}
|
||||
content_key->UpdateDuration(key_control_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -720,7 +780,7 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
unsigned int md_len = *signature_length;
|
||||
if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH,
|
||||
if (HMAC(EVP_sha256(), &key[0], key.size(),
|
||||
in_buffer, buffer_length, signature, &md_len)) {
|
||||
*signature_length = md_len;
|
||||
return OEMCrypto_SUCCESS;
|
||||
@@ -765,7 +825,7 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
|
||||
}
|
||||
unsigned int md_len = signature_length;
|
||||
uint8_t computed_signature[SHA256_DIGEST_LENGTH];
|
||||
if (HMAC(EVP_sha256(), &key[0], SHA256_DIGEST_LENGTH,
|
||||
if (HMAC(EVP_sha256(), &key[0], key.size(),
|
||||
in_buffer, buffer_length, computed_signature, &md_len)) {
|
||||
if (0 == memcmp(signature, computed_signature, SHA256_DIGEST_LENGTH)) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
@@ -778,71 +838,12 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
|
||||
bool SessionContext::RefreshKey(const KeyId& key_id,
|
||||
const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv) {
|
||||
if (key_id.empty()) {
|
||||
// Key control is not encrypted if key id is NULL
|
||||
KeyControlBlock key_control_block(true);
|
||||
if (!ParseKeyControl(key_control, key_control_block)) {
|
||||
LOGD("Parse key control error.");
|
||||
return false;
|
||||
}
|
||||
// Apply duration to all keys in this session
|
||||
return session_keys_.UpdateDuration(key_control_block);
|
||||
}
|
||||
|
||||
Key* content_key = session_keys_.Find(key_id);
|
||||
|
||||
if (NULL == content_key) {
|
||||
LOGD("Error: no matching content key.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key_control.empty()) {
|
||||
LOGD("Error: no key_control.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> content_key_value = content_key->value();
|
||||
|
||||
// Decrypt encrypted key control block
|
||||
std::vector<uint8_t> control;
|
||||
if (key_control_iv.empty()) {
|
||||
// TODO(fredg): get confirmation from server team this is valid.
|
||||
LOGD("Key control block is NOT encrypted.");
|
||||
control = key_control;
|
||||
} else {
|
||||
// TODO(fredg): get confirmation from server team this is valid, too.
|
||||
LOGD("Key control block is encrypted.");
|
||||
if (!ce_->DecryptMessage(this, content_key_value, key_control_iv,
|
||||
key_control, &control)) {
|
||||
LOGD("Error decrypting key control block.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
KeyControlBlock key_control_block(true);
|
||||
if (!ParseKeyControl(control, key_control_block)) {
|
||||
LOGD("Error parsing key control.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!content_key->UpdateDuration(key_control_block)) {
|
||||
LOGD("Error updating duration.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::UpdateMacKeys(const std::vector<uint8_t>& enc_mac_keys,
|
||||
const std::vector<uint8_t>& iv) {
|
||||
|
||||
// Decrypt mac key from enc_mac_key using device_keya
|
||||
std::vector<uint8_t> mac_keys;
|
||||
if (!ce_->DecryptMessage(this, encryption_key_, iv,
|
||||
enc_mac_keys, &mac_keys)) {
|
||||
if (!DecryptMessage(encryption_key_, iv, enc_mac_keys, &mac_keys)) {
|
||||
return false;
|
||||
}
|
||||
mac_key_server_ = std::vector<uint8_t>(mac_keys.begin(),
|
||||
@@ -933,11 +934,10 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) {
|
||||
}
|
||||
|
||||
// Internal utility function to decrypt the message
|
||||
bool CryptoEngine::DecryptMessage(SessionContext* session,
|
||||
const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<uint8_t>& message,
|
||||
std::vector<uint8_t>* decrypted) {
|
||||
bool SessionContext::DecryptMessage(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<uint8_t>& message,
|
||||
std::vector<uint8_t>* decrypted) {
|
||||
if (key.empty() || iv.empty() || message.empty() || !decrypted) {
|
||||
LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return false;
|
||||
@@ -952,13 +952,10 @@ bool CryptoEngine::DecryptMessage(SessionContext* session,
|
||||
return true;
|
||||
}
|
||||
|
||||
OEMCryptoResult CryptoEngine::DecryptCTR(SessionContext* session,
|
||||
const uint8_t* iv, size_t block_offset,
|
||||
const uint8_t* cipher_data,
|
||||
size_t cipher_data_length,
|
||||
bool is_encrypted, uint8_t* clear_data,
|
||||
BufferType buffer_type) {
|
||||
|
||||
OEMCryptoResult SessionContext::DecryptCTR(
|
||||
const uint8_t* iv, size_t block_offset, const uint8_t* cipher_data,
|
||||
size_t cipher_data_length, bool is_encrypted, uint8_t* clear_data,
|
||||
BufferType buffer_type) {
|
||||
// If the data is clear, we do not need a current key selected.
|
||||
if (!is_encrypted && buffer_type != kBufferTypeDirect) {
|
||||
memcpy(reinterpret_cast<uint8_t*>(clear_data),
|
||||
@@ -967,39 +964,38 @@ OEMCryptoResult CryptoEngine::DecryptCTR(SessionContext* session,
|
||||
}
|
||||
|
||||
// Check there is a content key
|
||||
if (session->current_content_key() == NULL) {
|
||||
if (current_content_key() == NULL) {
|
||||
LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]");
|
||||
return OEMCrypto_ERROR_DECRYPT_FAILED;
|
||||
}
|
||||
const KeyControlBlock& control = session->current_content_key()->control();
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
if (control.control_bits() & kControlDataPathSecure) {
|
||||
if (buffer_type == kBufferTypeClear) {
|
||||
LOGE("[DecryptCTR(): Secure key with insecure buffer]");
|
||||
return OEMCrypto_ERROR_DECRYPT_FAILED;
|
||||
}
|
||||
}
|
||||
if (!local_display_) { // Only look at HDCP if the display is not local.
|
||||
if (control.control_bits() & kControlHDCPRequired) {
|
||||
uint8_t required_hdcp
|
||||
= (control.control_bits() & kControlHDCPVersionMask)
|
||||
>> kControlHDCPVersionShift;
|
||||
// For reference implementation, we pretend we can handle the current
|
||||
// HDCP version.
|
||||
if (required_hdcp > current_hdcp_capability()
|
||||
|| current_hdcp_capability() == 0) {
|
||||
return OEMCrypto_ERROR_INSUFFICIENT_HDCP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (control.duration() > 0) {
|
||||
if (control.duration() < session->CurrentTimer()) {
|
||||
if (control.duration() < CurrentTimer()) {
|
||||
LOGE("[DecryptCTR(): KEY_EXPIRED]");
|
||||
return OEMCrypto_ERROR_KEY_EXPIRED;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& content_key = session->current_content_key()->value();
|
||||
if (!ce_->local_display()) { // Only look at HDCP if the display is not
|
||||
// local.
|
||||
if (control.control_bits() & kControlHDCPRequired) {
|
||||
uint8_t required_hdcp =
|
||||
(control.control_bits() & kControlHDCPVersionMask) >>
|
||||
kControlHDCPVersionShift;
|
||||
// For reference implementation, we pretend we can handle the current
|
||||
// HDCP version.
|
||||
if (required_hdcp > ce_->current_hdcp_capability() ||
|
||||
ce_->current_hdcp_capability() == 0) {
|
||||
return OEMCrypto_ERROR_INSUFFICIENT_HDCP;
|
||||
}
|
||||
}
|
||||
}
|
||||
const std::vector<uint8_t>& content_key = current_content_key()->value();
|
||||
|
||||
// Set the AES key.
|
||||
if (static_cast<int>(content_key.size()) != AES_BLOCK_SIZE) {
|
||||
|
||||
Reference in New Issue
Block a user