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:
Fred Gylys-Colwell
2014-04-10 17:34:51 -07:00
parent 026a04701e
commit e95eebf326
9 changed files with 1343 additions and 2285 deletions

View File

@@ -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) {