diff --git a/libwvdrmengine/oemcrypto/mock/Android.mk b/libwvdrmengine/oemcrypto/mock/Android.mk index 8bcf11f6..fac879d6 100644 --- a/libwvdrmengine/oemcrypto/mock/Android.mk +++ b/libwvdrmengine/oemcrypto/mock/Android.mk @@ -12,7 +12,10 @@ LOCAL_SRC_FILES:= \ src/oemcrypto_keybox_testkey.cpp \ src/oemcrypto_logging.cpp \ src/oemcrypto_mock.cpp \ + src/oemcrypto_nonce_table.cpp \ src/oemcrypto_rsa_key_shared.cpp \ + src/oemcrypto_session.cpp \ + src/oemcrypto_session_key_table.cpp \ src/oemcrypto_usage_table_mock.cpp \ src/wvcrc.cpp \ diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 84bc20b9..a60a6dad 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -11,1074 +11,21 @@ #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include "keys.h" #include "log.h" #include "oemcrypto_key_mock.h" -#include "oemcrypto_logging.h" #include "oemcrypto_rsa_key_shared.h" -#include "oemcrypto_usage_table_mock.h" #include "string_conversions.h" #include "wv_cdm_constants.h" -static const int kPssSaltLength = 20; - -namespace { - -// Increment counter for AES-CTR. The CENC spec specifies we increment only -// the low 64 bits of the IV counter, and leave the high 64 bits alone. -void ctr128_inc64(uint8_t* counter) { - uint32_t n = 16; - do { - if (++counter[--n] != 0) return; - } while (n > 8); -} - -void dump_openssl_error() { - while (unsigned long err = ERR_get_error()) { - char buffer[120]; - LOGE("openssl error -- %lu -- %s", err, ERR_error_string(err, buffer)); - } -} - -} // namespace - namespace wvoec_mock { -SessionKeyTable::~SessionKeyTable() { - for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) { - if (NULL != i->second) { - delete i->second; - } - } -} - -bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) { - if (keys_.find(key_id) != keys_.end()) return false; - keys_[key_id] = new Key(key_data); - return true; -} - -Key* SessionKeyTable::Find(const KeyId key_id) { - if (keys_.find(key_id) == keys_.end()) { - return NULL; - } - return keys_[key_id]; -} - -void SessionKeyTable::Remove(const KeyId key_id) { - if (keys_.find(key_id) != keys_.end()) { - delete keys_[key_id]; - keys_.erase(key_id); - } -} - -void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) { - for (KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) { - it->second->UpdateDuration(control); - } -} - -SessionContext::~SessionContext() { - if (usage_entry_) { - delete usage_entry_; - usage_entry_ = NULL; - } -} - -// Internal utility function to derive key using CMAC-128 -bool SessionContext::DeriveKey(const std::vector& key, - const std::vector& context, int counter, - std::vector* out) { - if (key.empty() || counter > 4 || context.empty() || out == NULL) { - LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return false; - } - - const EVP_CIPHER* cipher = EVP_aes_128_cbc(); - CMAC_CTX* cmac_ctx = CMAC_CTX_new(); - - if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) { - LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); - return false; - } - - std::vector message; - message.push_back(counter); - message.insert(message.end(), context.begin(), context.end()); - - if (!CMAC_Update(cmac_ctx, &message[0], message.size())) { - LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); - return false; - } - - size_t reslen; - uint8_t res[128]; - if (!CMAC_Final(cmac_ctx, res, &reslen)) { - LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); - return false; - } - - out->assign(res, res + reslen); - - CMAC_CTX_free(cmac_ctx); - - return true; -} - -bool SessionContext::DeriveKeys(const std::vector& master_key, - const std::vector& mac_key_context, - const std::vector& enc_key_context) { - // Generate derived key for mac key - std::vector mac_key_server; - std::vector mac_key_client; - std::vector mac_key_part2; - if (!DeriveKey(master_key, mac_key_context, 1, &mac_key_server)) { - return false; - } - if (!DeriveKey(master_key, mac_key_context, 2, &mac_key_part2)) { - return false; - } - mac_key_server.insert(mac_key_server.end(), mac_key_part2.begin(), - mac_key_part2.end()); - - if (!DeriveKey(master_key, mac_key_context, 3, &mac_key_client)) { - return false; - } - if (!DeriveKey(master_key, mac_key_context, 4, &mac_key_part2)) { - return false; - } - mac_key_client.insert(mac_key_client.end(), mac_key_part2.begin(), - mac_key_part2.end()); - - // Generate derived key for encryption key - std::vector enc_key; - if (!DeriveKey(master_key, enc_key_context, 1, &enc_key)) { - return false; - } - - if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { - LOGI((" mac_key_context = " + wvcdm::b2a_hex(mac_key_context)).c_str()); - LOGI((" enc_key_context = " + wvcdm::b2a_hex(enc_key_context)).c_str()); - LOGI((" mac_key_server = " + wvcdm::b2a_hex(mac_key_server)).c_str()); - LOGI((" mac_key_client = " + wvcdm::b2a_hex(mac_key_client)).c_str()); - LOGI((" enc_key = " + wvcdm::b2a_hex(enc_key)).c_str()); - } - - set_mac_key_server(mac_key_server); - set_mac_key_client(mac_key_client); - set_encryption_key(enc_key); - return true; -} - -bool SessionContext::RSADeriveKeys( - const std::vector& enc_session_key, - const std::vector& mac_key_context, - const std::vector& enc_key_context) { - if (!rsa_key()) { - LOGE("[RSADeriveKeys(): no RSA key set]"); - return false; - } - if (enc_session_key.size() != static_cast(RSA_size(rsa_key()))) { - LOGE("[RSADeriveKeys(): encrypted session key wrong size:%zu, expected %d]", - enc_session_key.size(), RSA_size(rsa_key())); - dump_openssl_error(); - return false; - } - session_key_.resize(RSA_size(rsa_key())); - int decrypted_size = - RSA_private_decrypt(enc_session_key.size(), &enc_session_key[0], - &session_key_[0], rsa_key(), RSA_PKCS1_OAEP_PADDING); - if (-1 == decrypted_size) { - LOGE("[RSADeriveKeys(): error decrypting session key.]"); - dump_openssl_error(); - return false; - } - session_key_.resize(decrypted_size); - if (decrypted_size != static_cast(wvcdm::KEY_SIZE)) { - LOGE("[RSADeriveKeys(): error. session key is wrong size: %d.]", - decrypted_size); - dump_openssl_error(); - session_key_.clear(); - return false; - } - return DeriveKeys(session_key_, mac_key_context, enc_key_context); -} - -// Utility function to generate a message signature -bool SessionContext::GenerateSignature(const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t* signature_length) { - if (message == NULL || message_length == 0 || signature == NULL || - signature_length == 0) { - LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return false; - } - - if (mac_key_client_.empty() || - mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) { - LOGE("[GenerateSignature(): No MAC Key]"); - return false; - } - - if (*signature_length < SHA256_DIGEST_LENGTH) { - *signature_length = SHA256_DIGEST_LENGTH; - return false; - } - - unsigned int md_len = *signature_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; - } - return false; -} - -size_t SessionContext::RSASignatureSize() { - if (!rsa_key()) { - LOGE("[GenerateRSASignature(): no RSA key set]"); - return 0; - } - return static_cast(RSA_size(rsa_key())); -} - -OEMCryptoResult SessionContext::GenerateRSASignature( - const uint8_t* message, size_t message_length, uint8_t* signature, - size_t* signature_length, RSA_Padding_Scheme padding_scheme) { - if (message == NULL || message_length == 0 || signature == NULL || - signature_length == 0) { - LOGE("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if (!rsa_key()) { - LOGE("[GenerateRSASignature(): no RSA key set]"); - return OEMCrypto_ERROR_INVALID_RSA_KEY; - } - if (*signature_length < static_cast(RSA_size(rsa_key()))) { - *signature_length = RSA_size(rsa_key()); - return OEMCrypto_ERROR_SHORT_BUFFER; - } - if ((padding_scheme & allowed_schemes_) != padding_scheme) { - LOGE("[GenerateRSASignature(): padding_scheme not allowed]"); - return OEMCrypto_ERROR_INVALID_RSA_KEY; - } - // This is the standard padding scheme used for license requests. - if (padding_scheme == kSign_RSASSA_PSS) { - // Hash the message using SHA1. - uint8_t hash[SHA_DIGEST_LENGTH]; - if (!SHA1(message, message_length, hash)) { - LOGE("[GeneratRSASignature(): error creating signature hash.]"); - dump_openssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - // Add PSS padding. - std::vector padded_digest(*signature_length); - int status = RSA_padding_add_PKCS1_PSS_mgf1( - rsa_key(), &padded_digest[0], hash, EVP_sha1(), NULL, kPssSaltLength); - if (status == -1) { - LOGE("[GeneratRSASignature(): error padding hash.]"); - dump_openssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - // Encrypt PSS padded digest. - status = RSA_private_encrypt(*signature_length, &padded_digest[0], - signature, rsa_key(), RSA_NO_PADDING); - if (status == -1) { - LOGE("[GeneratRSASignature(): error in private encrypt.]"); - dump_openssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - // This is the alternate padding scheme used by cast receivers only. - } else if (padding_scheme == kSign_PKCS1_Block1) { - if (message_length > 83) { - LOGE("[GeneratRSASignature(): RSA digest too large.]"); - return OEMCrypto_ERROR_SIGNATURE_FAILURE; - } - // Pad the message with PKCS1 padding, and then encrypt. - size_t status = RSA_private_encrypt(message_length, message, signature, - rsa_key(), RSA_PKCS1_PADDING); - if (status != *signature_length) { - LOGE("[GeneratRSASignature(): error in RSA private encrypt. status=%d]", - status); - dump_openssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } else { // Bad RSA_Padding_Scheme - return OEMCrypto_ERROR_INVALID_RSA_KEY; - } - return OEMCrypto_SUCCESS; -} - -// Validate message signature -bool SessionContext::ValidateMessage(const uint8_t* given_message, - size_t message_length, - const uint8_t* given_signature, - size_t signature_length) { - if (signature_length != SHA256_DIGEST_LENGTH) { - return false; - } - uint8_t computed_signature[SHA256_DIGEST_LENGTH]; - memset(computed_signature, 0, SHA256_DIGEST_LENGTH); - unsigned int md_len = 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; - } - if (memcmp(given_signature, computed_signature, signature_length)) { - LOGE("Invalid signature given: %s", - wvcdm::HexEncode(given_signature, signature_length).c_str()); - LOGE("Invalid signature computed: %s", - wvcdm::HexEncode(computed_signature, signature_length).c_str()); - return false; - } - return true; -} - -OEMCryptoResult SessionContext::CheckStatusOnline(uint32_t nonce, - uint32_t control) { - if (!(control & kControlNonceEnabled)) { - LOGE("LoadKeys: Server provided Nonce_Required but Nonce_Enabled = 0."); - // Server error. Continue, and assume nonce required. - } - if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; - switch (usage_entry_status_) { - case kNoUsageEntry: - LOGE("LoadKeys: Session did not create usage entry."); - return OEMCrypto_ERROR_INVALID_CONTEXT; - case kUsageEntryLoaded: - LOGE("LoadKeys: Session reloaded existing entry."); - return OEMCrypto_ERROR_INVALID_CONTEXT; - case kUsageEntryNew: - return OEMCrypto_SUCCESS; - default: // invalid status. - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } -} - -OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce, - uint32_t control) { - if (control & kControlNonceEnabled) { - LOGE("KCB: Server provided NonceOrEntry but Nonce_Enabled = 1."); - // Server error. Continue, and assume nonce required. - } - switch (usage_entry_status_) { - case kNoUsageEntry: - LOGE("LoadKeys: Session did not create or load usage entry."); - return OEMCrypto_ERROR_INVALID_CONTEXT; - case kUsageEntryLoaded: - // Repeat load. Calling function will verify pst and keys. - return OEMCrypto_SUCCESS; - case kUsageEntryNew: - // First load. Verify nonce. - if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; - return OEMCrypto_SUCCESS; - default: // invalid status. - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } -} - -OEMCryptoResult SessionContext::CheckNonceOrEntry( - const KeyControlBlock& key_control_block) { - switch (key_control_block.control_bits() & kControlReplayMask) { - case kControlNonceRequired: // Online license. Nonce always required. - return CheckStatusOnline(key_control_block.nonce(), - key_control_block.control_bits()); - break; - case kControlNonceOrEntry: // Offline license. Nonce required on first use. - return CheckStatusOffline(key_control_block.nonce(), - key_control_block.control_bits()); - break; - default: - if ((key_control_block.control_bits() & kControlNonceEnabled) && - (!CheckNonce(key_control_block.nonce()))) { - LOGE("LoadKeys: BAD Nonce"); - return OEMCrypto_ERROR_INVALID_NONCE; - } - } - return OEMCrypto_SUCCESS; -} - -void SessionContext::StartTimer() { timer_start_ = time(NULL); } - -uint32_t SessionContext::CurrentTimer() { - time_t now = time(NULL); - return now - timer_start_; -} - -OEMCryptoResult SessionContext::LoadKeys( - 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(); - - // If there are already keys installed in this session, then we can load - // a shared license. - bool second_license = (session_keys_.size() > 0); - - // Decrypt and install keys in key object - // Each key will have a key control block. They will all have the same nonce. - OEMCryptoResult status = OEMCrypto_SUCCESS; - std::vector key_id; - std::vector enc_key_data; - std::vector key_data_iv; - std::vector key_control; - std::vector key_control_iv; - 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 = OEMCrypto_ERROR_UNKNOWN_FAILURE; - 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); - - OEMCryptoResult result = InstallKey( - key_id, enc_key_data, key_data_iv, key_control, key_control_iv, - key_array[i].cipher_mode == OEMCrypto_CipherMode_CTR, second_license); - if (result != OEMCrypto_SUCCESS) { - status = result; - break; - } - } - FlushNonces(); - if (status != OEMCrypto_SUCCESS) return status; - - // 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 enc_mac_keys_str = std::vector( - enc_mac_keys, enc_mac_keys + 2 * wvcdm::MAC_KEY_SIZE); - const std::vector enc_mac_key_iv_str = std::vector( - enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE); - - if (!UpdateMacKeys(enc_mac_keys_str, enc_mac_key_iv_str)) { - LOGE("Failed to update mac keys.\n"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } - if (usage_entry_) { - OEMCryptoResult result = OEMCrypto_SUCCESS; - switch (usage_entry_status_) { - case kNoUsageEntry: - if (pst_length > 0) { - LOGE("LoadKeys: PST specified but no usage entry loaded."); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - break; // no extra check. - case kUsageEntryNew: - result = usage_entry_->SetPST(pst, pst_length); - if (result != OEMCrypto_SUCCESS) { - return result; - } - if (!usage_entry_->SetMacKeys(mac_key_server_, mac_key_client_)) { - LOGE("LoadKeys: Usage table can't set keys.\n"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - break; - case kUsageEntryLoaded: - if (!usage_entry_->VerifyPST(pst, pst_length)) { - return OEMCrypto_ERROR_WRONG_PST; - } - if (!usage_entry_->VerifyMacKeys(mac_key_server_, mac_key_client_)) { - LOGE("LoadKeys: Usage table entry mac keys do not match.\n"); - return OEMCrypto_ERROR_WRONG_KEYS; - } - if (usage_entry_->Inactive()) return OEMCrypto_ERROR_LICENSE_INACTIVE; - break; - } - } - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult SessionContext::InstallKey( - const KeyId& key_id, const std::vector& key_data, - const std::vector& key_data_iv, - const std::vector& key_control, - const std::vector& key_control_iv, bool ctr_mode, - bool second_license) { - // Decrypt encrypted key_data using derived encryption key and offered iv - std::vector content_key; - std::vector key_control_str; - - if (!DecryptMessage(encryption_key_, key_data_iv, key_data, &content_key)) { - LOGE("[Installkey(): Could not decrypt key data]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - if (LogCategoryEnabled(kLoggingDumpContentKeys)) { - LOGI((" InstallKey: key_id = " + - wvcdm::b2a_hex(key_id)).c_str()); - LOGI((" InstallKey: content_key = " + - wvcdm::b2a_hex(content_key)).c_str()); - LOGI((" InstallKey: key_control = " + - wvcdm::b2a_hex(key_control_str)).c_str()); - } - - // Key control must be supplied by license server - if (key_control.empty()) { - LOGE("[Installkey(): WARNING: No Key Control]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if (key_control_iv.empty()) { - LOGE("[Installkey(): ERROR: No Key Control IV]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if (!DecryptMessage(content_key, key_control_iv, key_control, - &key_control_str)) { - LOGE("[Installkey(): ERROR: Could not decrypt content key]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - KeyControlBlock key_control_block(key_control_str); - if (!key_control_block.valid()) { - LOGE("Error parsing key control."); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if ((key_control_block.control_bits() & - kControlRequireAntiRollbackHardware) && - !ce_->config_is_anti_rollback_hw_present()) { - LOGE("Anti-rollback hardware is required but hardware not present."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - uint8_t minimum_patch_level = - (key_control_block.control_bits() & kControlSecurityPatchLevelMask) >> - kControlSecurityPatchLevelShift; - if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { - LOGE("[InstallKey(): security patch level: %d. Minimum:%d]", - OEMCrypto_Security_Patch_Level(), minimum_patch_level); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - OEMCryptoResult result = CheckNonceOrEntry(key_control_block); - if (result != OEMCrypto_SUCCESS) { - LOGE("LoadKeys: Failed Nonce/PST check."); - return result; - } - - if (key_control_block.control_bits() & kSharedLicense) { - if (!second_license) { - LOGE("LoadKeys: Shared License, but no keys previously loaded."); - return OEMCrypto_ERROR_MISSING_MASTER; - } - } - - Key key(content_key, key_control_block, ctr_mode); - session_keys_.Insert(key_id, key); - return OEMCrypto_SUCCESS; -} - -bool SessionContext::InstallRSAEncryptedKey( - const uint8_t* encrypted_message_key, size_t encrypted_message_key_length) { - encryption_key_.resize(RSA_size(rsa_key())); - int decrypted_size = RSA_private_decrypt( - encrypted_message_key_length, encrypted_message_key, &encryption_key_[0], - rsa_key(), RSA_PKCS1_OAEP_PADDING); - if (-1 == decrypted_size) { - LOGE("[RSADeriveKeys(): error decrypting session key.]"); - dump_openssl_error(); - return false; - } - encryption_key_.resize(decrypted_size); - if (decrypted_size != static_cast(wvcdm::KEY_SIZE)) { - LOGE("[RSADeriveKeys(): error. session key is wrong size: %d.]", - decrypted_size); - dump_openssl_error(); - encryption_key_.clear(); - return false; - } - return true; -} - -OEMCryptoResult SessionContext::RefreshKey( - const KeyId& key_id, const std::vector& key_control, - const std::vector& 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()) { - LOGE("Parse key control error."); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if ((key_control_block.control_bits() & kControlNonceEnabled) && - (!CheckNonce(key_control_block.nonce()))) { - LOGE("KCB: BAD Nonce"); - return OEMCrypto_ERROR_INVALID_NONCE; - } - // Apply duration to all keys in this session - session_keys_.UpdateDuration(key_control_block); - return OEMCrypto_SUCCESS; - } - - Key* content_key = session_keys_.Find(key_id); - - if (NULL == content_key) { - if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { - LOGD("Error: no matching content key."); - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - if (key_control.empty()) { - if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { - LOGD("Error: no key_control."); - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - const std::vector content_key_value = content_key->value(); - - // Decrypt encrypted key control block - std::vector control; - if (key_control_iv.empty()) { - if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { - LOGD("Key control block is NOT encrypted."); - } - control = key_control; - } else { - if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { - LOGD("Key control block is encrypted."); - } - if (!DecryptMessage(content_key_value, key_control_iv, key_control, - &control)) { - if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { - LOGD("Error decrypting key control block."); - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } - - KeyControlBlock key_control_block(control); - if (!key_control_block.valid()) { - if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { - LOGD("Parse key control error."); - } - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if ((key_control_block.control_bits() & kControlNonceEnabled) && - (!CheckNonce(key_control_block.nonce()))) { - LOGE("KCB: BAD Nonce"); - return OEMCrypto_ERROR_INVALID_NONCE; - } - content_key->UpdateDuration(key_control_block); - return OEMCrypto_SUCCESS; -} - -bool SessionContext::DecryptRSAKey(const uint8_t* enc_rsa_key, - size_t enc_rsa_key_length, - const uint8_t* enc_rsa_key_iv, - uint8_t* pkcs8_rsa_key) { - // Decrypt rsa key with keybox. - uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; - memcpy(iv_buffer, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); - AES_KEY aes_key; - AES_set_decrypt_key(&encryption_key_[0], 128, &aes_key); - AES_cbc_encrypt(enc_rsa_key, pkcs8_rsa_key, enc_rsa_key_length, &aes_key, - iv_buffer, AES_DECRYPT); - return true; -} - -bool SessionContext::EncryptRSAKey(const uint8_t* pkcs8_rsa_key, - size_t enc_rsa_key_length, - const uint8_t* enc_rsa_key_iv, - uint8_t* enc_rsa_key) { - // Encrypt rsa key with keybox. - uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; - memcpy(iv_buffer, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); - AES_KEY aes_key; - AES_set_encrypt_key(&encryption_key_[0], 128, &aes_key); - AES_cbc_encrypt(pkcs8_rsa_key, enc_rsa_key, enc_rsa_key_length, &aes_key, - iv_buffer, AES_ENCRYPT); - return true; -} - -bool SessionContext::LoadRSAKey(const uint8_t* pkcs8_rsa_key, - size_t rsa_key_length) { - rsa_key_.reset(); - if (rsa_key_length < 8) { - LOGE("[LoadRSAKey(): Very Short Buffer]"); - return false; - } - if ((memcmp(pkcs8_rsa_key, "SIGN", 4) == 0)) { - uint32_t* schemes_n = (uint32_t*)(pkcs8_rsa_key + 4); - allowed_schemes_ = htonl(*schemes_n); - pkcs8_rsa_key += 8; - rsa_key_length -= 8; - } else { - allowed_schemes_ = kSign_RSASSA_PSS; - } - return rsa_key_.LoadPkcs8RsaKey(pkcs8_rsa_key, rsa_key_length); -} - -OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, - uint32_t use_type, - OEMCryptoBufferType buffer_type) { - const KeyControlBlock& control = current_content_key()->control(); - if (use_type && (!(control.control_bits() & use_type))) { - LOGE("[%s(): control bit says not allowed.", log_string.c_str()); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (control.control_bits() & kControlDataPathSecure) { - if (!ce_->config_closed_platform() && - buffer_type == OEMCrypto_BufferType_Clear) { - LOGE("[%s(): Secure key with insecure buffer]", log_string.c_str()); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - } - if (control.control_bits() & kControlReplayMask) { - if (!CheckUsageEntry()) { - LOGE("[%s(): usage entry not valid]", log_string.c_str()); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - } - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("[%s(): key expired.", log_string.c_str()); - return OEMCrypto_ERROR_KEY_EXPIRED; - } - } - if (!ce_->config_local_display_only()) { - // Only look at HDCP and Analog restrictions if the display is non-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_->config_current_hdcp_capability() || - ce_->config_current_hdcp_capability() == 0) { - return OEMCrypto_ERROR_INSUFFICIENT_HDCP; - } - } - if (control.control_bits() & kControlSRMVersionRequired) { - LOGE("[%s(): control bit says SRM version required.", log_string.c_str()); - return OEMCrypto_ERROR_INSUFFICIENT_HDCP; - } - } - if (!ce_->config_local_display_only() || - buffer_type == OEMCrypto_BufferType_Clear) { - if (control.control_bits() & kControlDisableAnalogOutput) { - LOGE("[%s(): control bit says disable analog.", log_string.c_str()); - return OEMCrypto_ERROR_ANALOG_OUTPUT; - } - } - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, - size_t buffer_length, - const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer) { - // Check there is a content key - if (current_content_key() == NULL) { - LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); - return OEMCrypto_ERROR_NO_CONTENT_KEY; - } - const std::vector& key = current_content_key()->value(); - // Set the AES key. - if (static_cast(key.size()) != AES_BLOCK_SIZE) { - LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size()); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", kControlAllowEncrypt, - OEMCrypto_BufferType_Clear); - if (result != OEMCrypto_SUCCESS) return result; - if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { - LOGE("[Generic_Encrypt(): algorithm bad."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (buffer_length % AES_BLOCK_SIZE != 0) { - LOGE("[Generic_Encrypt(): buffers size bad."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - const uint8_t* key_u8 = &key[0]; - AES_KEY aes_key; - if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { - LOGE("[Generic_Encrypt(): FAILURE]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; - memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); - AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, - AES_ENCRYPT); - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, - size_t buffer_length, - const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer) { - // Check there is a content key - if (current_content_key() == NULL) { - LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); - return OEMCrypto_ERROR_NO_CONTENT_KEY; - } - const std::vector& key = current_content_key()->value(); - // Set the AES key. - if (static_cast(key.size()) != AES_BLOCK_SIZE) { - LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", kControlAllowDecrypt, - OEMCrypto_BufferType_Clear); - if (result != OEMCrypto_SUCCESS) return result; - - if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { - LOGE("[Generic_Decrypt(): bad algorithm."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (buffer_length % AES_BLOCK_SIZE != 0) { - LOGE("[Generic_Decrypt(): bad buffer size."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - const uint8_t* key_u8 = &key[0]; - AES_KEY aes_key; - if (AES_set_decrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { - LOGE("[Generic_Decrypt(): FAILURE]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; - memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); - AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, - AES_DECRYPT); - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, - size_t buffer_length, - OEMCrypto_Algorithm algorithm, - uint8_t* signature, - size_t* signature_length) { - // Check there is a content key - if (current_content_key() == NULL) { - LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); - return OEMCrypto_ERROR_NO_CONTENT_KEY; - } - if (*signature_length < SHA256_DIGEST_LENGTH) { - *signature_length = SHA256_DIGEST_LENGTH; - LOGE("[Generic_Sign(): bad signature length."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - const std::vector& key = current_content_key()->value(); - if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { - LOGE("[Generic_Sign(): CONTENT_KEY has wrong size; %d", key.size()); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - OEMCryptoResult result = CheckKeyUse("Generic_Sign", kControlAllowSign, - OEMCrypto_BufferType_Clear); - if (result != OEMCrypto_SUCCESS) return result; - if (algorithm != OEMCrypto_HMAC_SHA256) { - LOGE("[Generic_Sign(): bad algorithm."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - unsigned int md_len = *signature_length; - if (HMAC(EVP_sha256(), &key[0], key.size(), in_buffer, buffer_length, - signature, &md_len)) { - *signature_length = md_len; - return OEMCrypto_SUCCESS; - } - LOGE("[Generic_Sign(): hmac failed."); - dump_openssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; -} - -OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, - size_t buffer_length, - OEMCrypto_Algorithm algorithm, - const uint8_t* signature, - size_t signature_length) { - // Check there is a content key - if (current_content_key() == NULL) { - LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (signature_length < SHA256_DIGEST_LENGTH) { - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - const std::vector& key = current_content_key()->value(); - if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { - LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size()); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - OEMCryptoResult result = CheckKeyUse("Generic_Verify", kControlAllowVerify, - OEMCrypto_BufferType_Clear); - if (result != OEMCrypto_SUCCESS) return result; - if (algorithm != OEMCrypto_HMAC_SHA256) { - LOGE("[Generic_Verify(): bad algorithm."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - unsigned int md_len = signature_length; - uint8_t computed_signature[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; - } else { - return OEMCrypto_ERROR_SIGNATURE_FAILURE; - } - } - LOGE("[Generic_Verify(): HMAC failed."); - dump_openssl_error(); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; -} - -bool SessionContext::UpdateMacKeys(const std::vector& enc_mac_keys, - const std::vector& iv) { - // Decrypt mac key from enc_mac_key using device_keya - std::vector mac_keys; - if (!DecryptMessage(encryption_key_, iv, enc_mac_keys, &mac_keys)) { - return false; - } - mac_key_server_ = std::vector( - mac_keys.begin(), mac_keys.begin() + wvcdm::MAC_KEY_SIZE); - mac_key_client_ = std::vector(mac_keys.begin() + wvcdm::MAC_KEY_SIZE, - mac_keys.end()); - return true; -} - -bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) { - const Key* content_key = session_keys_.Find(key_id); - if (LogCategoryEnabled(kLoggingTraceDecryption)) { - LOGI(("Select Key: key_id = " + wvcdm::b2a_hex(key_id)).c_str()); - if (content_key) { - LOGI(("Select Key: key = " + wvcdm::b2a_hex(content_key->value())) - .c_str()); - } else { - LOGI("Select Key: key = null."); - } - } - if (NULL == content_key) { - LOGE("[QueryKeyControlBlock(): No key matches key id]"); - return false; - } - data[0] = 0; // verification optional. - data[1] = htonl(content_key->control().duration()); - data[2] = 0; // nonce optional. - data[3] = htonl(content_key->control().control_bits()); - return true; -} - -OEMCryptoResult SessionContext::SelectContentKey(const KeyId& key_id) { - const Key* content_key = session_keys_.Find(key_id); - - if (LogCategoryEnabled(kLoggingTraceDecryption)) { - LOGI((" Select Key: key_id = " + wvcdm::b2a_hex(key_id)).c_str()); - LOGI((" Select Key: key = " + wvcdm::b2a_hex(content_key->value())) - .c_str()); - } - - if (NULL == content_key) { - LOGE("[SelectContentKey(): No key matches key id]"); - return OEMCrypto_ERROR_NO_CONTENT_KEY; - } - current_content_key_ = content_key; - const KeyControlBlock& control = current_content_key()->control(); - if (control.duration() > 0) { - if (control.duration() < CurrentTimer()) { - LOGE("[SelectContentKey(): KEY_EXPIRED]"); - return OEMCrypto_ERROR_KEY_EXPIRED; - } - } - return OEMCrypto_SUCCESS; -} - -void SessionContext::AddNonce(uint32_t nonce) { nonce_table_.AddNonce(nonce); } - -bool SessionContext::CheckNonce(uint32_t nonce) { - return nonce_table_.CheckNonce(nonce); -} - -void SessionContext::FlushNonces() { nonce_table_.Flush(); } - -bool SessionContext::CheckUsageEntry() { - if (!usage_entry_) return false; - return usage_entry_->CheckForUse(); -} - -OEMCryptoResult SessionContext::CreateNewUsageEntry( - uint32_t* usage_entry_number) { - OEMCryptoResult result = ce_->usage_table().CreateNewUsageEntry( - this, &usage_entry_, usage_entry_number); - if (usage_entry_) { - usage_entry_status_ = kUsageEntryNew; - } - return result; -} - -OEMCryptoResult SessionContext::LoadUsageEntry( - uint32_t index, const std::vector& buffer) { - OEMCryptoResult result = - ce_->usage_table().LoadUsageEntry(this, &usage_entry_, index, buffer); - if (usage_entry_) { - usage_entry_status_ = kUsageEntryLoaded; - } - return result; -} - -OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer, - size_t* header_buffer_length, - uint8_t* entry_buffer, - size_t* entry_buffer_length) { - if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; - return ce_->usage_table().UpdateUsageEntry(this, usage_entry_, header_buffer, - header_buffer_length, entry_buffer, - entry_buffer_length); -} - -OEMCryptoResult SessionContext::DeactivateUsageEntry( - const std::vector& pst) { - if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; - usage_entry_->Deactivate(pst); - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult SessionContext::ReportUsage(const std::vector& pst, - uint8_t* buffer, - size_t* buffer_length) { - if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; - return usage_entry_->ReportUsage(pst, buffer, buffer_length); -} - -OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) { - if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; - return ce_->usage_table().MoveEntry(usage_entry_, new_index); -} - -OEMCryptoResult SessionContext::CopyOldUsageEntry( - const std::vector& pst) { - if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; - return ce_->usage_table().CopyOldUsageEntry(usage_entry_, pst); -} +// Note: The class CryptoEngine is configured at compile time by compiling in +// different device property files. The methods in this file are generic to +// all configurations. See the files oemcrypto_engine_device_properties*.cpp +// for methods that are configured for specific configurations. CryptoEngine::CryptoEngine(wvcdm::FileSystem* file_system) : root_of_trust_(config_provisioning_method()), @@ -1129,301 +76,4 @@ SessionContext* CryptoEngine::FindSession(SessionId sid) { return NULL; } -// Internal utility function to decrypt the message -bool SessionContext::DecryptMessage(const std::vector& key, - const std::vector& iv, - const std::vector& message, - std::vector* decrypted) { - if (key.empty() || iv.empty() || message.empty() || !decrypted) { - LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return false; - } - decrypted->resize(message.size()); - uint8_t iv_buffer[16]; - memcpy(iv_buffer, &iv[0], 16); - AES_KEY aes_key; - AES_set_decrypt_key(&key[0], 128, &aes_key); - AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key, - iv_buffer, AES_DECRYPT); - return true; -} - -OEMCryptoResult SessionContext::DecryptCENC( - const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, - size_t cipher_data_length, bool is_encrypted, uint8_t* clear_data, - OEMCryptoBufferType buffer_type) { - // If the data is clear, we do not need a current key selected. - if (!is_encrypted) { - if (buffer_type != OEMCrypto_BufferType_Direct) { - memcpy(reinterpret_cast(clear_data), cipher_data, - cipher_data_length); - return OEMCrypto_SUCCESS; - } - // For reference implementation, we quietly drop the clear direct video. - return OEMCrypto_SUCCESS; - } - - // Check there is a content key - if (current_content_key() == NULL) { - LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - - OEMCryptoResult result = CheckKeyUse("DecryptCENC", 0, buffer_type); - if (result != OEMCrypto_SUCCESS) return result; - - const std::vector& content_key = current_content_key()->value(); - - // Set the AES key. - if (static_cast(content_key.size()) != AES_BLOCK_SIZE) { - LOGE("[DecryptCTR(): CONTENT_KEY has wrong size: %d", content_key.size()); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - const uint8_t* key_u8 = &content_key[0]; - - if (buffer_type == OEMCrypto_BufferType_Direct) { - // For reference implementation, we quietly drop the decrypted direct video. - return OEMCrypto_SUCCESS; - } - - if (buffer_type == OEMCrypto_BufferType_Secure) { - // For reference implementation, we also quietly drop secure data. - return OEMCrypto_SUCCESS; - } - - if (!current_content_key()->ctr_mode()) { - if (block_offset > 0) return OEMCrypto_ERROR_INVALID_CONTEXT; - return DecryptCBC(key_u8, iv, pattern, cipher_data, cipher_data_length, - clear_data); - } - if (pattern->skip > 0) { - return PatternDecryptCTR(key_u8, iv, block_offset, pattern, cipher_data, - cipher_data_length, clear_data); - } - return DecryptCTR(key_u8, iv, block_offset, cipher_data, cipher_data_length, - clear_data); -} - -OEMCryptoResult SessionContext::DecryptCBC( - const uint8_t* key, const uint8_t* initial_iv, - const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, - size_t cipher_data_length, uint8_t* clear_data) { - AES_KEY aes_key; - AES_set_decrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); - uint8_t iv[AES_BLOCK_SIZE]; - memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); - - size_t l = 0; - size_t pattern_offset = pattern->offset; - while (l < cipher_data_length) { - size_t size = - std::min(cipher_data_length - l, static_cast(AES_BLOCK_SIZE)); - size_t pattern_length = pattern->encrypt + pattern->skip; - bool skip_block = - (pattern_offset >= pattern->encrypt) && (pattern_length > 0); - if (pattern_length > 0) { - pattern_offset = (pattern_offset + 1) % pattern_length; - } - if (skip_block || (size < AES_BLOCK_SIZE)) { - memcpy(&clear_data[l], &cipher_data[l], size); - } else { - uint8_t aes_output[AES_BLOCK_SIZE]; - AES_decrypt(&cipher_data[l], aes_output, &aes_key); - for (size_t n = 0; n < AES_BLOCK_SIZE; n++) { - clear_data[l + n] = aes_output[n] ^ iv[n]; - } - memcpy(iv, &cipher_data[l], AES_BLOCK_SIZE); - } - l += size; - } - return OEMCrypto_SUCCESS; -} - -OEMCryptoResult SessionContext::PatternDecryptCTR( - const uint8_t* key, const uint8_t* initial_iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, - size_t cipher_data_length, uint8_t* clear_data) { - AES_KEY aes_key; - AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); - uint8_t iv[AES_BLOCK_SIZE]; - memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); - - size_t l = 0; - size_t pattern_offset = pattern->offset; - while (l < cipher_data_length) { - size_t size = - std::min(cipher_data_length - l, AES_BLOCK_SIZE - block_offset); - size_t pattern_length = pattern->encrypt + pattern->skip; - bool skip_block = - (pattern_offset >= pattern->encrypt) && (pattern_length > 0); - if (pattern_length > 0) { - pattern_offset = (pattern_offset + 1) % pattern_length; - } - if (skip_block) { - memcpy(&clear_data[l], &cipher_data[l], size); - } else { - uint8_t aes_output[AES_BLOCK_SIZE]; - AES_encrypt(iv, aes_output, &aes_key); - for (size_t n = 0; n < size; n++) { - clear_data[l + n] = aes_output[n + block_offset] ^ cipher_data[l + n]; - } - ctr128_inc64(iv); - } - l += size; - block_offset = 0; - } - return OEMCrypto_SUCCESS; -} - -// This is a special case of PatternDecryptCTR with no skip pattern. It uses -// more optimized versions of openssl's implementation of AES CTR mode. -OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8, - const uint8_t* iv, - size_t block_offset, - const uint8_t* cipher_data, - size_t cipher_data_length, - uint8_t* clear_data) { - // Local copy (will be modified). - // Allocated as 64-bit ints to enforce 64-bit alignment for later access as a - // 64-bit value. - uint64_t aes_iv[2]; - assert(sizeof(aes_iv) == AES_BLOCK_SIZE); - // The double-cast is needed to comply with strict aliasing rules. - uint8_t* aes_iv_u8 = - reinterpret_cast(reinterpret_cast(aes_iv)); - memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); - - // The CENC spec specifies we increment only the low 64 bits of the IV - // counter, and leave the high 64 bits alone. This is different from the - // OpenSSL implementation, which increments the entire 128 bit iv. That is - // why we implement the CTR loop ourselves. - size_t l = 0; - if (block_offset > 0 && l < cipher_data_length) { - // Encrypt the IV. - uint8_t ecount_buf[AES_BLOCK_SIZE]; - - AES_KEY aes_key; - if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { - LOGE("[DecryptCTR(): FAILURE]"); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - AES_encrypt(aes_iv_u8, ecount_buf, &aes_key); - for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length; - ++n, ++l) { - clear_data[l] = cipher_data[l] ^ ecount_buf[n]; - } - ctr128_inc64(aes_iv_u8); - block_offset = 0; - } - - uint64_t remaining = cipher_data_length - l; - int out_len = 0; - - while (remaining) { - EVP_CIPHER_CTX ctx; - EVP_CIPHER_CTX_init(&ctx); - EVP_CIPHER_CTX_set_padding(&ctx, 0); - if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_ctr(), NULL, key_u8, aes_iv_u8)) { - LOGE("[DecryptCTR(): EVP_INIT ERROR]"); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - - // Test the MSB of the counter portion of the initialization vector. If the - // value is 0xFF the counter is near wrapping. In this case we calculate - // the number of bytes we can safely decrypt before the counter wraps. - uint64_t decrypt_length = 0; - if (aes_iv_u8[8] == 0xFF) { - uint64_t bottom_64_bits = wvcdm::ntohll64(aes_iv[1]); - uint64_t bytes_before_iv_wrap = (~bottom_64_bits + 1) * AES_BLOCK_SIZE; - decrypt_length = - bytes_before_iv_wrap < remaining ? bytes_before_iv_wrap : remaining; - } else { - decrypt_length = remaining; - } - - if (!EVP_DecryptUpdate(&ctx, &clear_data[l], &out_len, &cipher_data[l], - decrypt_length)) { - LOGE("[DecryptCTR(): EVP_UPDATE_ERROR]"); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - l += decrypt_length; - remaining = cipher_data_length - l; - - int final; - if (!EVP_DecryptFinal_ex( - &ctx, &clear_data[cipher_data_length - remaining], & final)) { - LOGE("[DecryptCTR(): EVP_FINAL_ERROR]"); - return OEMCrypto_ERROR_DECRYPT_FAILED; - } - EVP_CIPHER_CTX_cleanup(&ctx); - - // If remaining is not zero, reset the iv before the second pass. - if (remaining) { - memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); - memset(&aes_iv_u8[8], 0, AES_BLOCK_SIZE / 2); - } - } - - return OEMCrypto_SUCCESS; -} - -void NonceTable::AddNonce(uint32_t nonce) { - int new_slot = -1; - int oldest_slot = -1; - - // Flush any nonces that have been checked but not flushed. - // After flush, nonces will be either valid or invalid. - Flush(); - - for (int i = 0; i < kTableSize; ++i) { - // Increase age of all valid nonces. - if (kNTStateValid == state_[i]) { - ++age_[i]; - if (-1 == oldest_slot) { - oldest_slot = i; - } else { - if (age_[i] > age_[oldest_slot]) { - oldest_slot = i; - } - } - } else { - if (-1 == new_slot) { - age_[i] = 0; - nonces_[i] = nonce; - state_[i] = kNTStateValid; - new_slot = i; - } - } - } - if (-1 == new_slot) { - // reuse oldest - // assert (oldest_slot != -1) - int i = oldest_slot; - age_[i] = 0; - nonces_[i] = nonce; - state_[i] = kNTStateValid; - } -} - -bool NonceTable::CheckNonce(uint32_t nonce) { - for (int i = 0; i < kTableSize; ++i) { - if (kNTStateInvalid != state_[i]) { - if (nonce == nonces_[i]) { - state_[i] = kNTStateFlushPending; - return true; - } - } - } - return false; -} - -void NonceTable::Flush() { - for (int i = 0; i < kTableSize; ++i) { - if (kNTStateFlushPending == state_[i]) { - state_[i] = kNTStateInvalid; - } - } -} - } // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index aeff4f68..d12c7308 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -2,8 +2,8 @@ // // Mock implementation of OEMCrypto APIs // -#ifndef OEMCRYPTO_ENGINE_MOCK_H_ -#define OEMCRYPTO_ENGINE_MOCK_H_ +#ifndef MOCK_OEMCRYPTO_ENGINE_MOCK_H_ +#define MOCK_OEMCRYPTO_ENGINE_MOCK_H_ #include #include @@ -18,241 +18,14 @@ #include "oemcrypto_auth_mock.h" #include "oemcrypto_key_mock.h" #include "oemcrypto_rsa_key_shared.h" +#include "oemcrypto_session.h" #include "oemcrypto_usage_table_mock.h" #include "wv_cdm_types.h" namespace wvoec_mock { -class SessionContext; -class CryptoEngine; -class UsageTable; -class UsageTableEntry; - -typedef uint32_t SessionId; typedef std::map ActiveSessions; -typedef std::vector KeyId; -typedef std::map KeyMap; - -// SessionKeyTable holds the keys for the current session -class SessionKeyTable { - public: - SessionKeyTable() {} - ~SessionKeyTable(); - - bool Insert(const KeyId key_id, const Key& key_data); - Key* Find(const KeyId key_id); - void Remove(const KeyId key_id); - void UpdateDuration(const KeyControlBlock& control); - size_t size() const { return keys_.size(); } - - private: - KeyMap keys_; - - CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable); -}; - -class NonceTable { - public: - static const int kTableSize = 16; - NonceTable() { - for (int i = 0; i < kTableSize; ++i) { - state_[i] = kNTStateInvalid; - } - } - ~NonceTable() {} - void AddNonce(uint32_t nonce); - bool CheckNonce(uint32_t nonce); - void Flush(); - - private: - enum NonceTableState { - kNTStateInvalid, - kNTStateValid, - kNTStateFlushPending - }; - NonceTableState state_[kTableSize]; - uint32_t age_[kTableSize]; - uint32_t nonces_[kTableSize]; -}; - -class SessionContext { - private: - SessionContext() {} - - public: - SessionContext(CryptoEngine* ce, SessionId sid, const RSA_shared_ptr& rsa_key) - : valid_(true), - ce_(ce), - id_(sid), - current_content_key_(NULL), - rsa_key_(rsa_key), - allowed_schemes_(kSign_RSASSA_PSS), - usage_entry_(NULL), - usage_entry_status_(kNoUsageEntry) {} - ~SessionContext(); - - bool isValid() { return valid_; } - - bool DeriveKeys(const std::vector& master_key, - const std::vector& mac_context, - const std::vector& enc_context); - bool RSADeriveKeys(const std::vector& enc_session_key, - const std::vector& mac_context, - const std::vector& enc_context); - bool GenerateSignature(const uint8_t* message, size_t message_length, - uint8_t* signature, size_t* signature_length); - size_t RSASignatureSize(); - OEMCryptoResult GenerateRSASignature(const uint8_t* message, - size_t message_length, - uint8_t* signature, - size_t* signature_length, - RSA_Padding_Scheme padding_scheme); - bool ValidateMessage(const uint8_t* message, size_t message_length, - const uint8_t* signature, size_t signature_length); - OEMCryptoResult DecryptCENC(const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, - const uint8_t* cipher_data, - size_t cipher_data_length, bool is_encrypted, - uint8_t* clear_data, - OEMCryptoBufferType buffer_type); - - OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer, - size_t buffer_length, const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer); - OEMCryptoResult Generic_Decrypt(const uint8_t* in_buffer, - size_t buffer_length, const uint8_t* iv, - OEMCrypto_Algorithm algorithm, - uint8_t* out_buffer); - OEMCryptoResult Generic_Sign(const uint8_t* in_buffer, size_t buffer_length, - OEMCrypto_Algorithm algorithm, - uint8_t* signature, size_t* signature_length); - OEMCryptoResult Generic_Verify(const uint8_t* in_buffer, size_t buffer_length, - OEMCrypto_Algorithm algorithm, - const uint8_t* signature, - size_t signature_length); - void StartTimer(); - uint32_t CurrentTimer(); // (seconds). - OEMCryptoResult LoadKeys(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); - OEMCryptoResult InstallKey(const KeyId& key_id, - const std::vector& key_data, - const std::vector& key_data_iv, - const std::vector& key_control, - const std::vector& key_control_iv, - bool ctr_mode, bool second_license); - bool InstallRSAEncryptedKey(const uint8_t* encrypted_message_key, - size_t encrypted_message_key_length); - bool DecryptRSAKey(const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, - const uint8_t* wrapped_rsa_key_iv, uint8_t* pkcs8_rsa_key); - bool EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length, - const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key); - bool LoadRSAKey(const uint8_t* pkcs8_rsa_key, size_t rsa_key_length); - OEMCryptoResult RefreshKey(const KeyId& key_id, - const std::vector& key_control, - const std::vector& key_control_iv); - bool UpdateMacKeys(const std::vector& mac_keys, - const std::vector& iv); - bool QueryKeyControlBlock(const KeyId& key_id, uint32_t* data); - OEMCryptoResult SelectContentKey(const KeyId& key_id); - const Key* current_content_key(void) { return current_content_key_; } - void set_mac_key_server(const std::vector& mac_key_server) { - mac_key_server_ = mac_key_server; - } - const std::vector& mac_key_server() { return mac_key_server_; } - void set_mac_key_client(const std::vector& mac_key_client) { - mac_key_client_ = mac_key_client; - } - const std::vector& mac_key_client() { return mac_key_client_; } - - void set_encryption_key(const std::vector& enc_key) { - encryption_key_ = enc_key; - } - const std::vector& encryption_key() { return encryption_key_; } - uint32_t allowed_schemes() const { return allowed_schemes_; } - - void AddNonce(uint32_t nonce); - bool CheckNonce(uint32_t nonce); - void FlushNonces(); - - OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number); - OEMCryptoResult LoadUsageEntry(uint32_t index, - const std::vector& buffer); - OEMCryptoResult UpdateUsageEntry(uint8_t* header_buffer, - size_t* header_buffer_length, - uint8_t* entry_buffer, - size_t* entry_buffer_length); - OEMCryptoResult DeactivateUsageEntry(const std::vector& pst); - OEMCryptoResult ReportUsage(const std::vector& pst, uint8_t* buffer, - size_t* buffer_length); - OEMCryptoResult MoveEntry(uint32_t new_index); - OEMCryptoResult CopyOldUsageEntry(const std::vector& pst); - - private: - bool DeriveKey(const std::vector& key, - const std::vector& context, int counter, - std::vector* out); - bool DecryptMessage(const std::vector& key, - const std::vector& iv, - const std::vector& message, - std::vector* decrypted); - // Either verify the nonce or usage entry, as required by the key control - // block. - OEMCryptoResult CheckNonceOrEntry(const KeyControlBlock& key_control_block); - // If there is a usage entry, check that it is not inactive. - // It also updates the status of the entry if needed. - bool CheckUsageEntry(); - // Check that the usage entry status is valid for online use. - OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control); - // Check that the usage entry status is valid for offline use. - OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control); - OEMCryptoResult DecryptCBC(const uint8_t* key, const uint8_t* iv, - const OEMCrypto_CENCEncryptPatternDesc* pattern, - const uint8_t* cipher_data, - size_t cipher_data_length, uint8_t* clear_data); - OEMCryptoResult PatternDecryptCTR( - const uint8_t* key, const uint8_t* iv, size_t block_offset, - const OEMCrypto_CENCEncryptPatternDesc* pattern, - const uint8_t* cipher_data, size_t cipher_data_length, - uint8_t* clear_data); - OEMCryptoResult DecryptCTR(const uint8_t* key_u8, const uint8_t* iv, - size_t block_offset, const uint8_t* cipher_data, - size_t cipher_data_length, uint8_t* clear_data); - // Checks if the key is allowed for the specified type. If there is a usage - // entry, it also checks the usage entry. - OEMCryptoResult CheckKeyUse(const std::string& log_string, uint32_t use_type, - OEMCryptoBufferType buffer_type); - RSA* rsa_key() { return rsa_key_.get(); } - - bool valid_; - CryptoEngine* ce_; - SessionId id_; - std::vector mac_key_server_; - std::vector mac_key_client_; - std::vector encryption_key_; - std::vector session_key_; - const Key* current_content_key_; - SessionKeyTable session_keys_; - NonceTable nonce_table_; - RSA_shared_ptr rsa_key_; - uint32_t allowed_schemes_; // for RSA signatures. - time_t timer_start_; - UsageTableEntry* usage_entry_; - - enum UsageEntryStatus { - kNoUsageEntry, // No entry loaded for this session. - kUsageEntryNew, // After entry was created. - kUsageEntryLoaded, // After loading entry or loading keys. - }; - UsageEntryStatus usage_entry_status_; - CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext); -}; - class CryptoEngine { public: CryptoEngine(wvcdm::FileSystem* file_system); @@ -333,4 +106,4 @@ class CryptoEngine { } // namespace wvoec_mock -#endif // OEMCRYPTO_ENGINE_MOCK_H_ +#endif // MOCK_OEMCRYPTO_ENGINE_MOCK_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 2c8b6d98..c7416b1a 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -21,6 +21,7 @@ #include "log.h" #include "oemcrypto_engine_mock.h" #include "oemcrypto_logging.h" +#include "oemcrypto_session.h" #include "oemcrypto_usage_table_mock.h" #include "string_conversions.h" #include "wv_cdm_constants.h" diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_nonce_table.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_nonce_table.cpp new file mode 100644 index 00000000..e7f5e911 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_nonce_table.cpp @@ -0,0 +1,67 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_nonce_table.h" + +namespace wvoec_mock { + +void NonceTable::AddNonce(uint32_t nonce) { + int new_slot = -1; + int oldest_slot = -1; + + // Flush any nonces that have been checked but not flushed. + // After flush, nonces will be either valid or invalid. + Flush(); + + for (int i = 0; i < kTableSize; ++i) { + // Increase age of all valid nonces. + if (kNTStateValid == state_[i]) { + ++age_[i]; + if (-1 == oldest_slot) { + oldest_slot = i; + } else { + if (age_[i] > age_[oldest_slot]) { + oldest_slot = i; + } + } + } else { + if (-1 == new_slot) { + age_[i] = 0; + nonces_[i] = nonce; + state_[i] = kNTStateValid; + new_slot = i; + } + } + } + if (-1 == new_slot) { + // reuse oldest + // assert (oldest_slot != -1) + int i = oldest_slot; + age_[i] = 0; + nonces_[i] = nonce; + state_[i] = kNTStateValid; + } +} + +bool NonceTable::CheckNonce(uint32_t nonce) { + for (int i = 0; i < kTableSize; ++i) { + if (kNTStateInvalid != state_[i]) { + if (nonce == nonces_[i]) { + state_[i] = kNTStateFlushPending; + return true; + } + } + } + return false; +} + +void NonceTable::Flush() { + for (int i = 0; i < kTableSize; ++i) { + if (kNTStateFlushPending == state_[i]) { + state_[i] = kNTStateInvalid; + } + } +} + +} // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_nonce_table.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_nonce_table.h new file mode 100644 index 00000000..493c1a83 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_nonce_table.h @@ -0,0 +1,38 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef MOCK_OEMCRYPTO_NONCE_TABLE_H_ +#define MOCK_OEMCRYPTO_NONCE_TABLE_H_ + +#include + +namespace wvoec_mock { + +class NonceTable { + public: + static const int kTableSize = 16; + NonceTable() { + for (int i = 0; i < kTableSize; ++i) { + state_[i] = kNTStateInvalid; + } + } + ~NonceTable() {} + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + void Flush(); + + private: + enum NonceTableState { + kNTStateInvalid, + kNTStateValid, + kNTStateFlushPending + }; + NonceTableState state_[kTableSize]; + uint32_t age_[kTableSize]; + uint32_t nonces_[kTableSize]; +}; + +} // namespace wvoec_mock + +#endif // MOCK_OEMCRYPTO_NONCE_TABLE_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp new file mode 100644 index 00000000..17abccbd --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.cpp @@ -0,0 +1,1288 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_session.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keys.h" +#include "log.h" +#include "oemcrypto_engine_mock.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_logging.h" +#include "oemcrypto_rsa_key_shared.h" +#include "string_conversions.h" +#include "wv_cdm_constants.h" + +static const int kPssSaltLength = 20; + +namespace { + +// Increment counter for AES-CTR. The CENC spec specifies we increment only +// the low 64 bits of the IV counter, and leave the high 64 bits alone. +void ctr128_inc64(uint8_t* counter) { + uint32_t n = 16; + do { + if (++counter[--n] != 0) return; + } while (n > 8); +} + +void dump_openssl_error() { + while (unsigned long err = ERR_get_error()) { + char buffer[120]; + LOGE("openssl error -- %lu -- %s", err, ERR_error_string(err, buffer)); + } +} + +} // namespace + +namespace wvoec_mock { + +SessionContext::~SessionContext() { + if (usage_entry_) { + delete usage_entry_; + usage_entry_ = NULL; + } +} + +// Internal utility function to derive key using CMAC-128 +bool SessionContext::DeriveKey(const std::vector& key, + const std::vector& context, int counter, + std::vector* out) { + if (key.empty() || counter > 4 || context.empty() || out == NULL) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + CMAC_CTX* cmac_ctx = CMAC_CTX_new(); + + if (!CMAC_Init(cmac_ctx, &key[0], key.size(), cipher, 0)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + std::vector message; + message.push_back(counter); + message.insert(message.end(), context.begin(), context.end()); + + if (!CMAC_Update(cmac_ctx, &message[0], message.size())) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + size_t reslen; + uint8_t res[128]; + if (!CMAC_Final(cmac_ctx, res, &reslen)) { + LOGE("[DeriveKey(): OEMCrypto_ERROR_CMAC_FAILURE]"); + return false; + } + + out->assign(res, res + reslen); + + CMAC_CTX_free(cmac_ctx); + + return true; +} + +bool SessionContext::DeriveKeys(const std::vector& master_key, + const std::vector& mac_key_context, + const std::vector& enc_key_context) { + // Generate derived key for mac key + std::vector mac_key_server; + std::vector mac_key_client; + std::vector mac_key_part2; + if (!DeriveKey(master_key, mac_key_context, 1, &mac_key_server)) { + return false; + } + if (!DeriveKey(master_key, mac_key_context, 2, &mac_key_part2)) { + return false; + } + mac_key_server.insert(mac_key_server.end(), mac_key_part2.begin(), + mac_key_part2.end()); + + if (!DeriveKey(master_key, mac_key_context, 3, &mac_key_client)) { + return false; + } + if (!DeriveKey(master_key, mac_key_context, 4, &mac_key_part2)) { + return false; + } + mac_key_client.insert(mac_key_client.end(), mac_key_part2.begin(), + mac_key_part2.end()); + + // Generate derived key for encryption key + std::vector enc_key; + if (!DeriveKey(master_key, enc_key_context, 1, &enc_key)) { + return false; + } + + if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) { + LOGI((" mac_key_context = " + wvcdm::b2a_hex(mac_key_context)).c_str()); + LOGI((" enc_key_context = " + wvcdm::b2a_hex(enc_key_context)).c_str()); + LOGI((" mac_key_server = " + wvcdm::b2a_hex(mac_key_server)).c_str()); + LOGI((" mac_key_client = " + wvcdm::b2a_hex(mac_key_client)).c_str()); + LOGI((" enc_key = " + wvcdm::b2a_hex(enc_key)).c_str()); + } + + set_mac_key_server(mac_key_server); + set_mac_key_client(mac_key_client); + set_encryption_key(enc_key); + return true; +} + +bool SessionContext::RSADeriveKeys( + const std::vector& enc_session_key, + const std::vector& mac_key_context, + const std::vector& enc_key_context) { + if (!rsa_key()) { + LOGE("[RSADeriveKeys(): no RSA key set]"); + return false; + } + if (enc_session_key.size() != static_cast(RSA_size(rsa_key()))) { + LOGE("[RSADeriveKeys(): encrypted session key wrong size:%zu, expected %d]", + enc_session_key.size(), RSA_size(rsa_key())); + dump_openssl_error(); + return false; + } + session_key_.resize(RSA_size(rsa_key())); + int decrypted_size = + RSA_private_decrypt(enc_session_key.size(), &enc_session_key[0], + &session_key_[0], rsa_key(), RSA_PKCS1_OAEP_PADDING); + if (-1 == decrypted_size) { + LOGE("[RSADeriveKeys(): error decrypting session key.]"); + dump_openssl_error(); + return false; + } + session_key_.resize(decrypted_size); + if (decrypted_size != static_cast(wvcdm::KEY_SIZE)) { + LOGE("[RSADeriveKeys(): error. session key is wrong size: %d.]", + decrypted_size); + dump_openssl_error(); + session_key_.clear(); + return false; + } + return DeriveKeys(session_key_, mac_key_context, enc_key_context); +} + +// Utility function to generate a message signature +bool SessionContext::GenerateSignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length) { + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0) { + LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + + if (mac_key_client_.empty() || + mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) { + LOGE("[GenerateSignature(): No MAC Key]"); + return false; + } + + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + return false; + } + + unsigned int md_len = *signature_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; + } + return false; +} + +size_t SessionContext::RSASignatureSize() { + if (!rsa_key()) { + LOGE("[GenerateRSASignature(): no RSA key set]"); + return 0; + } + return static_cast(RSA_size(rsa_key())); +} + +OEMCryptoResult SessionContext::GenerateRSASignature( + const uint8_t* message, size_t message_length, uint8_t* signature, + size_t* signature_length, RSA_Padding_Scheme padding_scheme) { + if (message == NULL || message_length == 0 || signature == NULL || + signature_length == 0) { + LOGE("[GenerateRSASignature(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!rsa_key()) { + LOGE("[GenerateRSASignature(): no RSA key set]"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + if (*signature_length < static_cast(RSA_size(rsa_key()))) { + *signature_length = RSA_size(rsa_key()); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if ((padding_scheme & allowed_schemes_) != padding_scheme) { + LOGE("[GenerateRSASignature(): padding_scheme not allowed]"); + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + // This is the standard padding scheme used for license requests. + if (padding_scheme == kSign_RSASSA_PSS) { + // Hash the message using SHA1. + uint8_t hash[SHA_DIGEST_LENGTH]; + if (!SHA1(message, message_length, hash)) { + LOGE("[GeneratRSASignature(): error creating signature hash.]"); + dump_openssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Add PSS padding. + std::vector padded_digest(*signature_length); + int status = RSA_padding_add_PKCS1_PSS_mgf1( + rsa_key(), &padded_digest[0], hash, EVP_sha1(), NULL, kPssSaltLength); + if (status == -1) { + LOGE("[GeneratRSASignature(): error padding hash.]"); + dump_openssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Encrypt PSS padded digest. + status = RSA_private_encrypt(*signature_length, &padded_digest[0], + signature, rsa_key(), RSA_NO_PADDING); + if (status == -1) { + LOGE("[GeneratRSASignature(): error in private encrypt.]"); + dump_openssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + // This is the alternate padding scheme used by cast receivers only. + } else if (padding_scheme == kSign_PKCS1_Block1) { + if (message_length > 83) { + LOGE("[GeneratRSASignature(): RSA digest too large.]"); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + // Pad the message with PKCS1 padding, and then encrypt. + size_t status = RSA_private_encrypt(message_length, message, signature, + rsa_key(), RSA_PKCS1_PADDING); + if (status != *signature_length) { + LOGE("[GeneratRSASignature(): error in RSA private encrypt. status=%d]", + status); + dump_openssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } else { // Bad RSA_Padding_Scheme + return OEMCrypto_ERROR_INVALID_RSA_KEY; + } + return OEMCrypto_SUCCESS; +} + +// Validate message signature +bool SessionContext::ValidateMessage(const uint8_t* given_message, + size_t message_length, + const uint8_t* given_signature, + size_t signature_length) { + if (signature_length != SHA256_DIGEST_LENGTH) { + return false; + } + uint8_t computed_signature[SHA256_DIGEST_LENGTH]; + memset(computed_signature, 0, SHA256_DIGEST_LENGTH); + unsigned int md_len = 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; + } + if (memcmp(given_signature, computed_signature, signature_length)) { + LOGE("Invalid signature given: %s", + wvcdm::HexEncode(given_signature, signature_length).c_str()); + LOGE("Invalid signature computed: %s", + wvcdm::HexEncode(computed_signature, signature_length).c_str()); + return false; + } + return true; +} + +OEMCryptoResult SessionContext::CheckStatusOnline(uint32_t nonce, + uint32_t control) { + if (!(control & kControlNonceEnabled)) { + LOGE("LoadKeys: Server provided Nonce_Required but Nonce_Enabled = 0."); + // Server error. Continue, and assume nonce required. + } + if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; + switch (usage_entry_status_) { + case kNoUsageEntry: + LOGE("LoadKeys: Session did not create usage entry."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + case kUsageEntryLoaded: + LOGE("LoadKeys: Session reloaded existing entry."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + case kUsageEntryNew: + return OEMCrypto_SUCCESS; + default: // invalid status. + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce, + uint32_t control) { + if (control & kControlNonceEnabled) { + LOGE("KCB: Server provided NonceOrEntry but Nonce_Enabled = 1."); + // Server error. Continue, and assume nonce required. + } + switch (usage_entry_status_) { + case kNoUsageEntry: + LOGE("LoadKeys: Session did not create or load usage entry."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + case kUsageEntryLoaded: + // Repeat load. Calling function will verify pst and keys. + return OEMCrypto_SUCCESS; + case kUsageEntryNew: + // First load. Verify nonce. + if (!CheckNonce(nonce)) return OEMCrypto_ERROR_INVALID_NONCE; + return OEMCrypto_SUCCESS; + default: // invalid status. + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } +} + +OEMCryptoResult SessionContext::CheckNonceOrEntry( + const KeyControlBlock& key_control_block) { + switch (key_control_block.control_bits() & kControlReplayMask) { + case kControlNonceRequired: // Online license. Nonce always required. + return CheckStatusOnline(key_control_block.nonce(), + key_control_block.control_bits()); + break; + case kControlNonceOrEntry: // Offline license. Nonce required on first use. + return CheckStatusOffline(key_control_block.nonce(), + key_control_block.control_bits()); + break; + default: + if ((key_control_block.control_bits() & kControlNonceEnabled) && + (!CheckNonce(key_control_block.nonce()))) { + LOGE("LoadKeys: BAD Nonce"); + return OEMCrypto_ERROR_INVALID_NONCE; + } + } + return OEMCrypto_SUCCESS; +} + +void SessionContext::StartTimer() { timer_start_ = time(NULL); } + +uint32_t SessionContext::CurrentTimer() { + time_t now = time(NULL); + return now - timer_start_; +} + +OEMCryptoResult SessionContext::LoadKeys( + 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(); + + // If there are already keys installed in this session, then we can load + // a shared license. + bool second_license = (session_keys_.size() > 0); + + // Decrypt and install keys in key object + // Each key will have a key control block. They will all have the same nonce. + OEMCryptoResult status = OEMCrypto_SUCCESS; + std::vector key_id; + std::vector enc_key_data; + std::vector key_data_iv; + std::vector key_control; + std::vector key_control_iv; + 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 = OEMCrypto_ERROR_UNKNOWN_FAILURE; + 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); + + OEMCryptoResult result = InstallKey( + key_id, enc_key_data, key_data_iv, key_control, key_control_iv, + key_array[i].cipher_mode == OEMCrypto_CipherMode_CTR, second_license); + if (result != OEMCrypto_SUCCESS) { + status = result; + break; + } + } + FlushNonces(); + if (status != OEMCrypto_SUCCESS) return status; + + // 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 enc_mac_keys_str = std::vector( + enc_mac_keys, enc_mac_keys + 2 * wvcdm::MAC_KEY_SIZE); + const std::vector enc_mac_key_iv_str = std::vector( + enc_mac_key_iv, enc_mac_key_iv + wvcdm::KEY_IV_SIZE); + + if (!UpdateMacKeys(enc_mac_keys_str, enc_mac_key_iv_str)) { + LOGE("Failed to update mac keys.\n"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + if (usage_entry_) { + OEMCryptoResult result = OEMCrypto_SUCCESS; + switch (usage_entry_status_) { + case kNoUsageEntry: + if (pst_length > 0) { + LOGE("LoadKeys: PST specified but no usage entry loaded."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + break; // no extra check. + case kUsageEntryNew: + result = usage_entry_->SetPST(pst, pst_length); + if (result != OEMCrypto_SUCCESS) { + return result; + } + if (!usage_entry_->SetMacKeys(mac_key_server_, mac_key_client_)) { + LOGE("LoadKeys: Usage table can't set keys.\n"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + break; + case kUsageEntryLoaded: + if (!usage_entry_->VerifyPST(pst, pst_length)) { + return OEMCrypto_ERROR_WRONG_PST; + } + if (!usage_entry_->VerifyMacKeys(mac_key_server_, mac_key_client_)) { + LOGE("LoadKeys: Usage table entry mac keys do not match.\n"); + return OEMCrypto_ERROR_WRONG_KEYS; + } + if (usage_entry_->Inactive()) return OEMCrypto_ERROR_LICENSE_INACTIVE; + break; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::InstallKey( + const KeyId& key_id, const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv, bool ctr_mode, + bool second_license) { + // Decrypt encrypted key_data using derived encryption key and offered iv + std::vector content_key; + std::vector key_control_str; + + if (!DecryptMessage(encryption_key_, key_data_iv, key_data, &content_key)) { + LOGE("[Installkey(): Could not decrypt key data]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (LogCategoryEnabled(kLoggingDumpContentKeys)) { + LOGI((" InstallKey: key_id = " + + wvcdm::b2a_hex(key_id)).c_str()); + LOGI((" InstallKey: content_key = " + + wvcdm::b2a_hex(content_key)).c_str()); + LOGI((" InstallKey: key_control = " + + wvcdm::b2a_hex(key_control_str)).c_str()); + } + + // Key control must be supplied by license server + if (key_control.empty()) { + LOGE("[Installkey(): WARNING: No Key Control]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (key_control_iv.empty()) { + LOGE("[Installkey(): ERROR: No Key Control IV]"); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!DecryptMessage(content_key, key_control_iv, key_control, + &key_control_str)) { + LOGE("[Installkey(): ERROR: Could not decrypt content key]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + KeyControlBlock key_control_block(key_control_str); + if (!key_control_block.valid()) { + LOGE("Error parsing key control."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if ((key_control_block.control_bits() & + kControlRequireAntiRollbackHardware) && + !ce_->config_is_anti_rollback_hw_present()) { + LOGE("Anti-rollback hardware is required but hardware not present."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint8_t minimum_patch_level = + (key_control_block.control_bits() & kControlSecurityPatchLevelMask) >> + kControlSecurityPatchLevelShift; + if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { + LOGE("[InstallKey(): security patch level: %d. Minimum:%d]", + OEMCrypto_Security_Patch_Level(), minimum_patch_level); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + OEMCryptoResult result = CheckNonceOrEntry(key_control_block); + if (result != OEMCrypto_SUCCESS) { + LOGE("LoadKeys: Failed Nonce/PST check."); + return result; + } + + if (key_control_block.control_bits() & kSharedLicense) { + if (!second_license) { + LOGE("LoadKeys: Shared License, but no keys previously loaded."); + return OEMCrypto_ERROR_MISSING_MASTER; + } + } + + Key key(content_key, key_control_block, ctr_mode); + session_keys_.Insert(key_id, key); + return OEMCrypto_SUCCESS; +} + +bool SessionContext::InstallRSAEncryptedKey( + const uint8_t* encrypted_message_key, size_t encrypted_message_key_length) { + encryption_key_.resize(RSA_size(rsa_key())); + int decrypted_size = RSA_private_decrypt( + encrypted_message_key_length, encrypted_message_key, &encryption_key_[0], + rsa_key(), RSA_PKCS1_OAEP_PADDING); + if (-1 == decrypted_size) { + LOGE("[RSADeriveKeys(): error decrypting session key.]"); + dump_openssl_error(); + return false; + } + encryption_key_.resize(decrypted_size); + if (decrypted_size != static_cast(wvcdm::KEY_SIZE)) { + LOGE("[RSADeriveKeys(): error. session key is wrong size: %d.]", + decrypted_size); + dump_openssl_error(); + encryption_key_.clear(); + return false; + } + return true; +} + +OEMCryptoResult SessionContext::RefreshKey( + const KeyId& key_id, const std::vector& key_control, + const std::vector& 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()) { + LOGE("Parse key control error."); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if ((key_control_block.control_bits() & kControlNonceEnabled) && + (!CheckNonce(key_control_block.nonce()))) { + LOGE("KCB: BAD Nonce"); + return OEMCrypto_ERROR_INVALID_NONCE; + } + // Apply duration to all keys in this session + session_keys_.UpdateDuration(key_control_block); + return OEMCrypto_SUCCESS; + } + + Key* content_key = session_keys_.Find(key_id); + + if (NULL == content_key) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error: no matching content key."); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + if (key_control.empty()) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error: no key_control."); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + const std::vector content_key_value = content_key->value(); + + // Decrypt encrypted key control block + std::vector control; + if (key_control_iv.empty()) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Key control block is NOT encrypted."); + } + control = key_control; + } else { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Key control block is encrypted."); + } + if (!DecryptMessage(content_key_value, key_control_iv, key_control, + &control)) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Error decrypting key control block."); + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + + KeyControlBlock key_control_block(control); + if (!key_control_block.valid()) { + if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) { + LOGD("Parse key control error."); + } + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if ((key_control_block.control_bits() & kControlNonceEnabled) && + (!CheckNonce(key_control_block.nonce()))) { + LOGE("KCB: BAD Nonce"); + return OEMCrypto_ERROR_INVALID_NONCE; + } + content_key->UpdateDuration(key_control_block); + return OEMCrypto_SUCCESS; +} + +bool SessionContext::DecryptRSAKey(const uint8_t* enc_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* pkcs8_rsa_key) { + // Decrypt rsa key with keybox. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_decrypt_key(&encryption_key_[0], 128, &aes_key); + AES_cbc_encrypt(enc_rsa_key, pkcs8_rsa_key, enc_rsa_key_length, &aes_key, + iv_buffer, AES_DECRYPT); + return true; +} + +bool SessionContext::EncryptRSAKey(const uint8_t* pkcs8_rsa_key, + size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, + uint8_t* enc_rsa_key) { + // Encrypt rsa key with keybox. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, enc_rsa_key_iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&encryption_key_[0], 128, &aes_key); + AES_cbc_encrypt(pkcs8_rsa_key, enc_rsa_key, enc_rsa_key_length, &aes_key, + iv_buffer, AES_ENCRYPT); + return true; +} + +bool SessionContext::LoadRSAKey(const uint8_t* pkcs8_rsa_key, + size_t rsa_key_length) { + rsa_key_.reset(); + if (rsa_key_length < 8) { + LOGE("[LoadRSAKey(): Very Short Buffer]"); + return false; + } + if ((memcmp(pkcs8_rsa_key, "SIGN", 4) == 0)) { + uint32_t* schemes_n = (uint32_t*)(pkcs8_rsa_key + 4); + allowed_schemes_ = htonl(*schemes_n); + pkcs8_rsa_key += 8; + rsa_key_length -= 8; + } else { + allowed_schemes_ = kSign_RSASSA_PSS; + } + return rsa_key_.LoadPkcs8RsaKey(pkcs8_rsa_key, rsa_key_length); +} + +OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, + uint32_t use_type, + OEMCryptoBufferType buffer_type) { + const KeyControlBlock& control = current_content_key()->control(); + if (use_type && (!(control.control_bits() & use_type))) { + LOGE("[%s(): control bit says not allowed.", log_string.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (control.control_bits() & kControlDataPathSecure) { + if (!ce_->config_closed_platform() && + buffer_type == OEMCrypto_BufferType_Clear) { + LOGE("[%s(): Secure key with insecure buffer]", log_string.c_str()); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + } + if (control.control_bits() & kControlReplayMask) { + if (!CheckUsageEntry()) { + LOGE("[%s(): usage entry not valid]", log_string.c_str()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[%s(): key expired.", log_string.c_str()); + return OEMCrypto_ERROR_KEY_EXPIRED; + } + } + if (!ce_->config_local_display_only()) { + // Only look at HDCP and Analog restrictions if the display is non-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_->config_current_hdcp_capability() || + ce_->config_current_hdcp_capability() == 0) { + return OEMCrypto_ERROR_INSUFFICIENT_HDCP; + } + } + if (control.control_bits() & kControlSRMVersionRequired) { + LOGE("[%s(): control bit says SRM version required.", log_string.c_str()); + return OEMCrypto_ERROR_INSUFFICIENT_HDCP; + } + } + if (!ce_->config_local_display_only() || + buffer_type == OEMCrypto_BufferType_Clear) { + if (control.control_bits() & kControlDisableAnalogOutput) { + LOGE("[%s(): control bit says disable analog.", log_string.c_str()); + return OEMCrypto_ERROR_ANALOG_OUTPUT; + } + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Encrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + const std::vector& key = current_content_key()->value(); + // Set the AES key. + if (static_cast(key.size()) != AES_BLOCK_SIZE) { + LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", kControlAllowEncrypt, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { + LOGE("[Generic_Encrypt(): algorithm bad."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (buffer_length % AES_BLOCK_SIZE != 0) { + LOGE("[Generic_Encrypt(): buffers size bad."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const uint8_t* key_u8 = &key[0]; + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[Generic_Encrypt(): FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); + AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, + AES_ENCRYPT); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, + size_t buffer_length, + const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Decrypt(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + const std::vector& key = current_content_key()->value(); + // Set the AES key. + if (static_cast(key.size()) != AES_BLOCK_SIZE) { + LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", kControlAllowDecrypt, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + + if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { + LOGE("[Generic_Decrypt(): bad algorithm."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (buffer_length % AES_BLOCK_SIZE != 0) { + LOGE("[Generic_Decrypt(): bad buffer size."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const uint8_t* key_u8 = &key[0]; + AES_KEY aes_key; + if (AES_set_decrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[Generic_Decrypt(): FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, iv, wvcdm::KEY_IV_SIZE); + AES_cbc_encrypt(in_buffer, out_buffer, buffer_length, &aes_key, iv_buffer, + AES_DECRYPT); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, + size_t* signature_length) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Generic_Sign(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + if (*signature_length < SHA256_DIGEST_LENGTH) { + *signature_length = SHA256_DIGEST_LENGTH; + LOGE("[Generic_Sign(): bad signature length."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector& key = current_content_key()->value(); + if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { + LOGE("[Generic_Sign(): CONTENT_KEY has wrong size; %d", key.size()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Sign", kControlAllowSign, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + if (algorithm != OEMCrypto_HMAC_SHA256) { + LOGE("[Generic_Sign(): bad algorithm."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + unsigned int md_len = *signature_length; + if (HMAC(EVP_sha256(), &key[0], key.size(), in_buffer, buffer_length, + signature, &md_len)) { + *signature_length = md_len; + return OEMCrypto_SUCCESS; + } + LOGE("[Generic_Sign(): hmac failed."); + dump_openssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, + size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length) { + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[Decrypt_Verify(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (signature_length < SHA256_DIGEST_LENGTH) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + const std::vector& key = current_content_key()->value(); + if (static_cast(key.size()) != SHA256_DIGEST_LENGTH) { + LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size()); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult result = CheckKeyUse("Generic_Verify", kControlAllowVerify, + OEMCrypto_BufferType_Clear); + if (result != OEMCrypto_SUCCESS) return result; + if (algorithm != OEMCrypto_HMAC_SHA256) { + LOGE("[Generic_Verify(): bad algorithm."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + unsigned int md_len = signature_length; + uint8_t computed_signature[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; + } else { + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + } + LOGE("[Generic_Verify(): HMAC failed."); + dump_openssl_error(); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; +} + +bool SessionContext::UpdateMacKeys(const std::vector& enc_mac_keys, + const std::vector& iv) { + // Decrypt mac key from enc_mac_key using device_keya + std::vector mac_keys; + if (!DecryptMessage(encryption_key_, iv, enc_mac_keys, &mac_keys)) { + return false; + } + mac_key_server_ = std::vector( + mac_keys.begin(), mac_keys.begin() + wvcdm::MAC_KEY_SIZE); + mac_key_client_ = std::vector(mac_keys.begin() + wvcdm::MAC_KEY_SIZE, + mac_keys.end()); + return true; +} + +bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) { + const Key* content_key = session_keys_.Find(key_id); + if (LogCategoryEnabled(kLoggingTraceDecryption)) { + LOGI(("Select Key: key_id = " + wvcdm::b2a_hex(key_id)).c_str()); + if (content_key) { + LOGI(("Select Key: key = " + wvcdm::b2a_hex(content_key->value())) + .c_str()); + } else { + LOGI("Select Key: key = null."); + } + } + if (NULL == content_key) { + LOGE("[QueryKeyControlBlock(): No key matches key id]"); + return false; + } + data[0] = 0; // verification optional. + data[1] = htonl(content_key->control().duration()); + data[2] = 0; // nonce optional. + data[3] = htonl(content_key->control().control_bits()); + return true; +} + +OEMCryptoResult SessionContext::SelectContentKey(const KeyId& key_id) { + const Key* content_key = session_keys_.Find(key_id); + + if (LogCategoryEnabled(kLoggingTraceDecryption)) { + LOGI((" Select Key: key_id = " + wvcdm::b2a_hex(key_id)).c_str()); + LOGI((" Select Key: key = " + wvcdm::b2a_hex(content_key->value())) + .c_str()); + } + + if (NULL == content_key) { + LOGE("[SelectContentKey(): No key matches key id]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + current_content_key_ = content_key; + const KeyControlBlock& control = current_content_key()->control(); + if (control.duration() > 0) { + if (control.duration() < CurrentTimer()) { + LOGE("[SelectContentKey(): KEY_EXPIRED]"); + return OEMCrypto_ERROR_KEY_EXPIRED; + } + } + return OEMCrypto_SUCCESS; +} + +void SessionContext::AddNonce(uint32_t nonce) { nonce_table_.AddNonce(nonce); } + +bool SessionContext::CheckNonce(uint32_t nonce) { + return nonce_table_.CheckNonce(nonce); +} + +void SessionContext::FlushNonces() { nonce_table_.Flush(); } + +bool SessionContext::CheckUsageEntry() { + if (!usage_entry_) return false; + return usage_entry_->CheckForUse(); +} + +OEMCryptoResult SessionContext::CreateNewUsageEntry( + uint32_t* usage_entry_number) { + OEMCryptoResult result = ce_->usage_table().CreateNewUsageEntry( + this, &usage_entry_, usage_entry_number); + if (usage_entry_) { + usage_entry_status_ = kUsageEntryNew; + } + return result; +} + +OEMCryptoResult SessionContext::LoadUsageEntry( + uint32_t index, const std::vector& buffer) { + OEMCryptoResult result = + ce_->usage_table().LoadUsageEntry(this, &usage_entry_, index, buffer); + if (usage_entry_) { + usage_entry_status_ = kUsageEntryLoaded; + } + return result; +} + +OEMCryptoResult SessionContext::UpdateUsageEntry(uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + return ce_->usage_table().UpdateUsageEntry(this, usage_entry_, header_buffer, + header_buffer_length, entry_buffer, + entry_buffer_length); +} + +OEMCryptoResult SessionContext::DeactivateUsageEntry( + const std::vector& pst) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + usage_entry_->Deactivate(pst); + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::ReportUsage(const std::vector& pst, + uint8_t* buffer, + size_t* buffer_length) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + return usage_entry_->ReportUsage(pst, buffer, buffer_length); +} + +OEMCryptoResult SessionContext::MoveEntry(uint32_t new_index) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + return ce_->usage_table().MoveEntry(usage_entry_, new_index); +} + +OEMCryptoResult SessionContext::CopyOldUsageEntry( + const std::vector& pst) { + if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT; + return ce_->usage_table().CopyOldUsageEntry(usage_entry_, pst); +} + + +// Internal utility function to decrypt the message +bool SessionContext::DecryptMessage(const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted) { + if (key.empty() || iv.empty() || message.empty() || !decrypted) { + LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); + return false; + } + decrypted->resize(message.size()); + uint8_t iv_buffer[16]; + memcpy(iv_buffer, &iv[0], 16); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key, + iv_buffer, AES_DECRYPT); + return true; +} + +OEMCryptoResult SessionContext::DecryptCENC( + const uint8_t* iv, size_t block_offset, + const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, + size_t cipher_data_length, bool is_encrypted, uint8_t* clear_data, + OEMCryptoBufferType buffer_type) { + // If the data is clear, we do not need a current key selected. + if (!is_encrypted) { + if (buffer_type != OEMCrypto_BufferType_Direct) { + memcpy(reinterpret_cast(clear_data), cipher_data, + cipher_data_length); + return OEMCrypto_SUCCESS; + } + // For reference implementation, we quietly drop the clear direct video. + return OEMCrypto_SUCCESS; + } + + // Check there is a content key + if (current_content_key() == NULL) { + LOGE("[DecryptCTR(): OEMCrypto_ERROR_NO_CONTENT_KEY]"); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + + OEMCryptoResult result = CheckKeyUse("DecryptCENC", 0, buffer_type); + if (result != OEMCrypto_SUCCESS) return result; + + const std::vector& content_key = current_content_key()->value(); + + // Set the AES key. + if (static_cast(content_key.size()) != AES_BLOCK_SIZE) { + LOGE("[DecryptCTR(): CONTENT_KEY has wrong size: %d", content_key.size()); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + const uint8_t* key_u8 = &content_key[0]; + + if (buffer_type == OEMCrypto_BufferType_Direct) { + // For reference implementation, we quietly drop the decrypted direct video. + return OEMCrypto_SUCCESS; + } + + if (buffer_type == OEMCrypto_BufferType_Secure) { + // For reference implementation, we also quietly drop secure data. + return OEMCrypto_SUCCESS; + } + + if (!current_content_key()->ctr_mode()) { + if (block_offset > 0) return OEMCrypto_ERROR_INVALID_CONTEXT; + return DecryptCBC(key_u8, iv, pattern, cipher_data, cipher_data_length, + clear_data); + } + if (pattern->skip > 0) { + return PatternDecryptCTR(key_u8, iv, block_offset, pattern, cipher_data, + cipher_data_length, clear_data); + } + return DecryptCTR(key_u8, iv, block_offset, cipher_data, cipher_data_length, + clear_data); +} + +OEMCryptoResult SessionContext::DecryptCBC( + const uint8_t* key, const uint8_t* initial_iv, + const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, + size_t cipher_data_length, uint8_t* clear_data) { + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); + uint8_t iv[AES_BLOCK_SIZE]; + memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); + + size_t l = 0; + size_t pattern_offset = pattern->offset; + while (l < cipher_data_length) { + size_t size = + std::min(cipher_data_length - l, static_cast(AES_BLOCK_SIZE)); + size_t pattern_length = pattern->encrypt + pattern->skip; + bool skip_block = + (pattern_offset >= pattern->encrypt) && (pattern_length > 0); + if (pattern_length > 0) { + pattern_offset = (pattern_offset + 1) % pattern_length; + } + if (skip_block || (size < AES_BLOCK_SIZE)) { + memcpy(&clear_data[l], &cipher_data[l], size); + } else { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_decrypt(&cipher_data[l], aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE; n++) { + clear_data[l + n] = aes_output[n] ^ iv[n]; + } + memcpy(iv, &cipher_data[l], AES_BLOCK_SIZE); + } + l += size; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult SessionContext::PatternDecryptCTR( + const uint8_t* key, const uint8_t* initial_iv, size_t block_offset, + const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data, + size_t cipher_data_length, uint8_t* clear_data) { + AES_KEY aes_key; + AES_set_encrypt_key(&key[0], AES_BLOCK_SIZE * 8, &aes_key); + uint8_t iv[AES_BLOCK_SIZE]; + memcpy(iv, &initial_iv[0], AES_BLOCK_SIZE); + + size_t l = 0; + size_t pattern_offset = pattern->offset; + while (l < cipher_data_length) { + size_t size = + std::min(cipher_data_length - l, AES_BLOCK_SIZE - block_offset); + size_t pattern_length = pattern->encrypt + pattern->skip; + bool skip_block = + (pattern_offset >= pattern->encrypt) && (pattern_length > 0); + if (pattern_length > 0) { + pattern_offset = (pattern_offset + 1) % pattern_length; + } + if (skip_block) { + memcpy(&clear_data[l], &cipher_data[l], size); + } else { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < size; n++) { + clear_data[l + n] = aes_output[n + block_offset] ^ cipher_data[l + n]; + } + ctr128_inc64(iv); + } + l += size; + block_offset = 0; + } + return OEMCrypto_SUCCESS; +} + +// This is a special case of PatternDecryptCTR with no skip pattern. It uses +// more optimized versions of openssl's implementation of AES CTR mode. +OEMCryptoResult SessionContext::DecryptCTR(const uint8_t* key_u8, + const uint8_t* iv, + size_t block_offset, + const uint8_t* cipher_data, + size_t cipher_data_length, + uint8_t* clear_data) { + // Local copy (will be modified). + // Allocated as 64-bit ints to enforce 64-bit alignment for later access as a + // 64-bit value. + uint64_t aes_iv[2]; + assert(sizeof(aes_iv) == AES_BLOCK_SIZE); + // The double-cast is needed to comply with strict aliasing rules. + uint8_t* aes_iv_u8 = + reinterpret_cast(reinterpret_cast(aes_iv)); + memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); + + // The CENC spec specifies we increment only the low 64 bits of the IV + // counter, and leave the high 64 bits alone. This is different from the + // OpenSSL implementation, which increments the entire 128 bit iv. That is + // why we implement the CTR loop ourselves. + size_t l = 0; + if (block_offset > 0 && l < cipher_data_length) { + // Encrypt the IV. + uint8_t ecount_buf[AES_BLOCK_SIZE]; + + AES_KEY aes_key; + if (AES_set_encrypt_key(key_u8, AES_BLOCK_SIZE * 8, &aes_key) != 0) { + LOGE("[DecryptCTR(): FAILURE]"); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + AES_encrypt(aes_iv_u8, ecount_buf, &aes_key); + for (int n = block_offset; n < AES_BLOCK_SIZE && l < cipher_data_length; + ++n, ++l) { + clear_data[l] = cipher_data[l] ^ ecount_buf[n]; + } + ctr128_inc64(aes_iv_u8); + block_offset = 0; + } + + uint64_t remaining = cipher_data_length - l; + int out_len = 0; + + while (remaining) { + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + if (!EVP_DecryptInit_ex(&ctx, EVP_aes_128_ctr(), NULL, key_u8, aes_iv_u8)) { + LOGE("[DecryptCTR(): EVP_INIT ERROR]"); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + + // Test the MSB of the counter portion of the initialization vector. If the + // value is 0xFF the counter is near wrapping. In this case we calculate + // the number of bytes we can safely decrypt before the counter wraps. + uint64_t decrypt_length = 0; + if (aes_iv_u8[8] == 0xFF) { + uint64_t bottom_64_bits = wvcdm::ntohll64(aes_iv[1]); + uint64_t bytes_before_iv_wrap = (~bottom_64_bits + 1) * AES_BLOCK_SIZE; + decrypt_length = + bytes_before_iv_wrap < remaining ? bytes_before_iv_wrap : remaining; + } else { + decrypt_length = remaining; + } + + if (!EVP_DecryptUpdate(&ctx, &clear_data[l], &out_len, &cipher_data[l], + decrypt_length)) { + LOGE("[DecryptCTR(): EVP_UPDATE_ERROR]"); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + l += decrypt_length; + remaining = cipher_data_length - l; + + int final; + if (!EVP_DecryptFinal_ex( + &ctx, &clear_data[cipher_data_length - remaining], & final)) { + LOGE("[DecryptCTR(): EVP_FINAL_ERROR]"); + return OEMCrypto_ERROR_DECRYPT_FAILED; + } + EVP_CIPHER_CTX_cleanup(&ctx); + + // If remaining is not zero, reset the iv before the second pass. + if (remaining) { + memcpy(aes_iv_u8, &iv[0], AES_BLOCK_SIZE); + memset(&aes_iv_u8[8], 0, AES_BLOCK_SIZE / 2); + } + } + return OEMCrypto_SUCCESS; +} + +} // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h new file mode 100644 index 00000000..768d0165 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session.h @@ -0,0 +1,210 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef MOCK_OEMCRYPTO_SESSION_H_ +#define MOCK_OEMCRYPTO_SESSION_H_ + +#include +#include +#include +#include + +#include + +#include "OEMCryptoCENC.h" // Needed for enums only. +#include "file_store.h" +#include "lock.h" +#include "oemcrypto_auth_mock.h" +#include "oemcrypto_key_mock.h" +#include "oemcrypto_nonce_table.h" +#include "oemcrypto_rsa_key_shared.h" +#include "oemcrypto_session_key_table.h" +#include "oemcrypto_usage_table_mock.h" +#include "wv_cdm_types.h" + +namespace wvoec_mock { + +class CryptoEngine; +typedef uint32_t SessionId; + +class SessionContext { + private: + SessionContext() {} + + public: + SessionContext(CryptoEngine* ce, SessionId sid, const RSA_shared_ptr& rsa_key) + : valid_(true), + ce_(ce), + id_(sid), + current_content_key_(NULL), + rsa_key_(rsa_key), + allowed_schemes_(kSign_RSASSA_PSS), + usage_entry_(NULL), + usage_entry_status_(kNoUsageEntry) {} + ~SessionContext(); + + bool isValid() { return valid_; } + + bool DeriveKeys(const std::vector& master_key, + const std::vector& mac_context, + const std::vector& enc_context); + bool RSADeriveKeys(const std::vector& enc_session_key, + const std::vector& mac_context, + const std::vector& enc_context); + bool GenerateSignature(const uint8_t* message, size_t message_length, + uint8_t* signature, size_t* signature_length); + size_t RSASignatureSize(); + OEMCryptoResult GenerateRSASignature(const uint8_t* message, + size_t message_length, + uint8_t* signature, + size_t* signature_length, + RSA_Padding_Scheme padding_scheme); + bool ValidateMessage(const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length); + OEMCryptoResult DecryptCENC(const uint8_t* iv, size_t block_offset, + const OEMCrypto_CENCEncryptPatternDesc* pattern, + const uint8_t* cipher_data, + size_t cipher_data_length, bool is_encrypted, + uint8_t* clear_data, + OEMCryptoBufferType buffer_type); + + OEMCryptoResult Generic_Encrypt(const uint8_t* in_buffer, + size_t buffer_length, const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + OEMCryptoResult Generic_Decrypt(const uint8_t* in_buffer, + size_t buffer_length, const uint8_t* iv, + OEMCrypto_Algorithm algorithm, + uint8_t* out_buffer); + OEMCryptoResult Generic_Sign(const uint8_t* in_buffer, size_t buffer_length, + OEMCrypto_Algorithm algorithm, + uint8_t* signature, size_t* signature_length); + OEMCryptoResult Generic_Verify(const uint8_t* in_buffer, size_t buffer_length, + OEMCrypto_Algorithm algorithm, + const uint8_t* signature, + size_t signature_length); + void StartTimer(); + uint32_t CurrentTimer(); // (seconds). + OEMCryptoResult LoadKeys(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); + OEMCryptoResult InstallKey(const KeyId& key_id, + const std::vector& key_data, + const std::vector& key_data_iv, + const std::vector& key_control, + const std::vector& key_control_iv, + bool ctr_mode, bool second_license); + bool InstallRSAEncryptedKey(const uint8_t* encrypted_message_key, + size_t encrypted_message_key_length); + bool DecryptRSAKey(const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, + const uint8_t* wrapped_rsa_key_iv, uint8_t* pkcs8_rsa_key); + bool EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length, + const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key); + bool LoadRSAKey(const uint8_t* pkcs8_rsa_key, size_t rsa_key_length); + OEMCryptoResult RefreshKey(const KeyId& key_id, + const std::vector& key_control, + const std::vector& key_control_iv); + bool UpdateMacKeys(const std::vector& mac_keys, + const std::vector& iv); + bool QueryKeyControlBlock(const KeyId& key_id, uint32_t* data); + OEMCryptoResult SelectContentKey(const KeyId& key_id); + const Key* current_content_key(void) { return current_content_key_; } + void set_mac_key_server(const std::vector& mac_key_server) { + mac_key_server_ = mac_key_server; + } + const std::vector& mac_key_server() { return mac_key_server_; } + void set_mac_key_client(const std::vector& mac_key_client) { + mac_key_client_ = mac_key_client; + } + const std::vector& mac_key_client() { return mac_key_client_; } + + void set_encryption_key(const std::vector& enc_key) { + encryption_key_ = enc_key; + } + const std::vector& encryption_key() { return encryption_key_; } + uint32_t allowed_schemes() const { return allowed_schemes_; } + + void AddNonce(uint32_t nonce); + bool CheckNonce(uint32_t nonce); + void FlushNonces(); + + OEMCryptoResult CreateNewUsageEntry(uint32_t* usage_entry_number); + OEMCryptoResult LoadUsageEntry(uint32_t index, + const std::vector& buffer); + OEMCryptoResult UpdateUsageEntry(uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length); + OEMCryptoResult DeactivateUsageEntry(const std::vector& pst); + OEMCryptoResult ReportUsage(const std::vector& pst, uint8_t* buffer, + size_t* buffer_length); + OEMCryptoResult MoveEntry(uint32_t new_index); + OEMCryptoResult CopyOldUsageEntry(const std::vector& pst); + + private: + bool DeriveKey(const std::vector& key, + const std::vector& context, int counter, + std::vector* out); + bool DecryptMessage(const std::vector& key, + const std::vector& iv, + const std::vector& message, + std::vector* decrypted); + // Either verify the nonce or usage entry, as required by the key control + // block. + OEMCryptoResult CheckNonceOrEntry(const KeyControlBlock& key_control_block); + // If there is a usage entry, check that it is not inactive. + // It also updates the status of the entry if needed. + bool CheckUsageEntry(); + // Check that the usage entry status is valid for online use. + OEMCryptoResult CheckStatusOnline(uint32_t nonce, uint32_t control); + // Check that the usage entry status is valid for offline use. + OEMCryptoResult CheckStatusOffline(uint32_t nonce, uint32_t control); + OEMCryptoResult DecryptCBC(const uint8_t* key, const uint8_t* iv, + const OEMCrypto_CENCEncryptPatternDesc* pattern, + const uint8_t* cipher_data, + size_t cipher_data_length, uint8_t* clear_data); + OEMCryptoResult PatternDecryptCTR( + const uint8_t* key, const uint8_t* iv, size_t block_offset, + const OEMCrypto_CENCEncryptPatternDesc* pattern, + const uint8_t* cipher_data, size_t cipher_data_length, + uint8_t* clear_data); + OEMCryptoResult DecryptCTR(const uint8_t* key_u8, const uint8_t* iv, + size_t block_offset, const uint8_t* cipher_data, + size_t cipher_data_length, uint8_t* clear_data); + // Checks if the key is allowed for the specified type. If there is a usage + // entry, it also checks the usage entry. + OEMCryptoResult CheckKeyUse(const std::string& log_string, uint32_t use_type, + OEMCryptoBufferType buffer_type); + RSA* rsa_key() { return rsa_key_.get(); } + + bool valid_; + CryptoEngine* ce_; + SessionId id_; + std::vector mac_key_server_; + std::vector mac_key_client_; + std::vector encryption_key_; + std::vector session_key_; + const Key* current_content_key_; + SessionKeyTable session_keys_; + NonceTable nonce_table_; + RSA_shared_ptr rsa_key_; + uint32_t allowed_schemes_; // for RSA signatures. + time_t timer_start_; + UsageTableEntry* usage_entry_; + + enum UsageEntryStatus { + kNoUsageEntry, // No entry loaded for this session. + kUsageEntryNew, // After entry was created. + kUsageEntryLoaded, // After loading entry or loading keys. + }; + UsageEntryStatus usage_entry_status_; + CORE_DISALLOW_COPY_AND_ASSIGN(SessionContext); +}; + +} // namespace wvoec_mock + +#endif // MOCK_OEMCRYPTO_SESSION_H_ diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session_key_table.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session_key_table.cpp new file mode 100644 index 00000000..c313f133 --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session_key_table.cpp @@ -0,0 +1,46 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#include "oemcrypto_session_key_table.h" + +#include "keys.h" +#include "log.h" + +namespace wvoec_mock { + +SessionKeyTable::~SessionKeyTable() { + for (KeyMap::iterator i = keys_.begin(); i != keys_.end(); ++i) { + if (NULL != i->second) { + delete i->second; + } + } +} + +bool SessionKeyTable::Insert(const KeyId key_id, const Key& key_data) { + if (keys_.find(key_id) != keys_.end()) return false; + keys_[key_id] = new Key(key_data); + return true; +} + +Key* SessionKeyTable::Find(const KeyId key_id) { + if (keys_.find(key_id) == keys_.end()) { + return NULL; + } + return keys_[key_id]; +} + +void SessionKeyTable::Remove(const KeyId key_id) { + if (keys_.find(key_id) != keys_.end()) { + delete keys_[key_id]; + keys_.erase(key_id); + } +} + +void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) { + for (KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) { + it->second->UpdateDuration(control); + } +} + +} // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session_key_table.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session_key_table.h new file mode 100644 index 00000000..f3567bdb --- /dev/null +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_session_key_table.h @@ -0,0 +1,45 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Mock implementation of OEMCrypto APIs +// +#ifndef MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_ +#define MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_ + +#include +#include +#include + +#include "oemcrypto_key_mock.h" +#include "wv_cdm_types.h" + +namespace wvoec_mock { + +class SessionContext; +class CryptoEngine; +class UsageTable; +class UsageTableEntry; + +typedef std::vector KeyId; +typedef std::map KeyMap; + +// SessionKeyTable holds the keys for the current session +class SessionKeyTable { + public: + SessionKeyTable() {} + ~SessionKeyTable(); + + bool Insert(const KeyId key_id, const Key& key_data); + Key* Find(const KeyId key_id); + void Remove(const KeyId key_id); + void UpdateDuration(const KeyControlBlock& control); + size_t size() const { return keys_.size(); } + + private: + KeyMap keys_; + + CORE_DISALLOW_COPY_AND_ASSIGN(SessionKeyTable); +}; + +} // namespace wvoec_mock + +#endif // MOCK_OEMCRYPTO_SESSION_KEY_TABLE_H_