diff --git a/libwvdrmengine/oemcrypto/mock/Android.mk b/libwvdrmengine/oemcrypto/mock/Android.mk index 2b8f89fe..8bcf11f6 100644 --- a/libwvdrmengine/oemcrypto/mock/Android.mk +++ b/libwvdrmengine/oemcrypto/mock/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ src/keys.cpp \ + src/oemcrypto_auth_mock.cpp \ src/oemcrypto_engine_device_properties.cpp \ src/oemcrypto_engine_mock.cpp \ src/oemcrypto_key_mock.cpp \ @@ -11,6 +12,7 @@ LOCAL_SRC_FILES:= \ src/oemcrypto_keybox_testkey.cpp \ src/oemcrypto_logging.cpp \ src/oemcrypto_mock.cpp \ + src/oemcrypto_rsa_key_shared.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 76e9f44d..282c0545 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -90,7 +90,10 @@ void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) { } SessionContext::~SessionContext() { - if (usage_entry_) usage_entry_->set_session(NULL); + if (usage_entry_) { + delete usage_entry_; + usage_entry_ = NULL; + } } // Internal utility function to derive key using CMAC-128 @@ -346,56 +349,68 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message, return true; } -bool SessionContext::CheckNonceOrEntry(const KeyControlBlock& key_control_block, - const std::vector& pst) { +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. - if (pst.size() == 0) { - LOGE("KCB: PST null for kControlNonceRequired."); - return false; - } - if (!(key_control_block.control_bits() & kControlNonceEnabled)) { - LOGE("KCB: Server provided Nonce_Required but Nonce_Enabled = 0."); - // Server error. Continue, and assume nonce required. - } - if (!CheckNonce(key_control_block.nonce())) return false; - if (!usage_entry_) { - if (ce_->usage_table()->FindEntry(pst)) { - LOGE("KCB: Cannot create duplicate entries in usage table."); - return false; - } - usage_entry_ = ce_->usage_table()->CreateEntry(pst, this); - } + 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; - case kControlNonceOrEntry: // Offline license. Nonce required on first use. - if (key_control_block.control_bits() & kControlNonceEnabled) { - LOGE("KCB: Server provided NonceOrEntry but Nonce_Enabled = 1."); - // Server error. Continue, and assume nonce required. - } - if (pst.size() == 0) { - LOGE("KCB: PST null for kControlNonceOrEntry."); - return false; - } - if (!usage_entry_) { - usage_entry_ = ce_->usage_table()->FindEntry(pst); - if (usage_entry_) { - if (usage_entry_->inactive()) return false; - } else { - if (!CheckNonce(key_control_block.nonce())) return false; - usage_entry_ = ce_->usage_table()->CreateEntry(pst, this); - } - } else { - if (usage_entry_->inactive()) return false; - } - break; // Usage table not required. Look at nonce enabled bit. default: if ((key_control_block.control_bits() & kControlNonceEnabled) && (!CheckNonce(key_control_block.nonce()))) { - LOGE("KCB: BAD Nonce"); - return false; + LOGE("LoadKeys: BAD Nonce"); + return OEMCrypto_ERROR_INVALID_NONCE; } } - return true; + return OEMCrypto_SUCCESS; } void SessionContext::StartTimer() { timer_start_ = time(NULL); } @@ -420,14 +435,12 @@ OEMCryptoResult SessionContext::LoadKeys( // Decrypt and install keys in key object // Each key will have a key control block. They will all have the same nonce. - bool status = true; + 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; - std::vector pstv; - if (pst_length > 0) pstv.assign(pst, pst + pst_length); for (unsigned int i = 0; i < num_keys; i++) { key_id.assign(key_array[i].key_id, key_array[i].key_id + key_array[i].key_id_length); @@ -436,7 +449,7 @@ OEMCryptoResult SessionContext::LoadKeys( key_data_iv.assign(key_array[i].key_data_iv, key_array[i].key_data_iv + wvcdm::KEY_IV_SIZE); if (key_array[i].key_control == NULL) { - status = false; + status = OEMCrypto_ERROR_UNKNOWN_FAILURE; break; } key_control.assign(key_array[i].key_control, @@ -444,15 +457,16 @@ OEMCryptoResult SessionContext::LoadKeys( key_control_iv.assign(key_array[i].key_control_iv, key_array[i].key_control_iv + wvcdm::KEY_IV_SIZE); - if (!InstallKey(key_id, enc_key_data, key_data_iv, key_control, - key_control_iv, pstv, - key_array[i].cipher_mode == OEMCrypto_CipherMode_CTR)) { - status = false; + OEMCryptoResult result = InstallKey( + key_id, enc_key_data, key_data_iv, key_control, key_control_iv, + key_array[i].cipher_mode == OEMCrypto_CipherMode_CTR); + if (result != OEMCrypto_SUCCESS) { + status = result; break; } } FlushNonces(); - if (!status) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (status != OEMCrypto_SUCCESS) return status; // enc_mac_key can be NULL if license renewal is not supported if (enc_mac_keys != NULL) { @@ -467,68 +481,88 @@ OEMCryptoResult SessionContext::LoadKeys( return OEMCrypto_ERROR_UNKNOWN_FAILURE; } } - if (pst_length > 0) { - if (!usage_entry_) { - LOGE("Usage table entry not found.\n"); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (!usage_entry_->VerifyOrSetMacKeys(mac_key_server_, mac_key_client_)) { - LOGE("Usage table entry does not match.\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 does not match.\n"); + return OEMCrypto_ERROR_WRONG_KEYS; + } + if (usage_entry_->Inactive()) return OEMCrypto_ERROR_LICENSE_INACTIVE; + break; } } return OEMCrypto_SUCCESS; } -bool 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, - const std::vector& pst, - bool ctr_mode) { +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) { // 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 false; + 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()); + 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 false; + return OEMCrypto_ERROR_INVALID_CONTEXT; } if (key_control_iv.empty()) { LOGE("[Installkey(): ERROR: No Key Control IV]"); - return false; + 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 false; + return OEMCrypto_ERROR_UNKNOWN_FAILURE; } KeyControlBlock key_control_block(key_control_str); if (!key_control_block.valid()) { LOGE("Error parsing key control."); - return false; + 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 false; + return OEMCrypto_ERROR_UNKNOWN_FAILURE; } uint8_t minimum_patch_level = (key_control_block.control_bits() & kControlSecurityPatchLevelMask) >> @@ -536,17 +570,18 @@ bool SessionContext::InstallKey(const KeyId& key_id, if (minimum_patch_level > OEMCrypto_Security_Patch_Level()) { LOGE("[InstallKey(): security patch level: %d. Minimum:%d]", OEMCrypto_Security_Patch_Level(), minimum_patch_level); - return false; + return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - if (!CheckNonceOrEntry(key_control_block, pst)) { - LOGE("Failed Nonce/PST check."); - return false; + OEMCryptoResult result = CheckNonceOrEntry(key_control_block); + if (result != OEMCrypto_SUCCESS) { + LOGE("LoadKeys: Failed Nonce/PST check."); + return result; } Key key(content_key, key_control_block, ctr_mode); session_keys_.Insert(key_id, key); - return true; + return OEMCrypto_SUCCESS; } bool SessionContext::InstallRSAEncryptedKey( @@ -691,7 +726,7 @@ bool SessionContext::LoadRSAKey(const uint8_t* pkcs8_rsa_key, return rsa_key_.LoadPkcs8RsaKey(pkcs8_rsa_key, rsa_key_length); } -OEMCryptoResult SessionContext::AllowKeyUse(const std::string& log_string, +OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, uint32_t use_type, OEMCryptoBufferType buffer_type) { const KeyControlBlock& control = current_content_key()->control(); @@ -707,7 +742,7 @@ OEMCryptoResult SessionContext::AllowKeyUse(const std::string& log_string, } } if (control.control_bits() & kControlReplayMask) { - if (!IsUsageEntryValid()) { + if (!CheckUsageEntry()) { LOGE("[%s(): usage entry not valid]", log_string.c_str()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -762,7 +797,7 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - OEMCryptoResult result = AllowKeyUse("Generic_Encrypt", kControlAllowEncrypt, + OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", kControlAllowEncrypt, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { @@ -802,7 +837,7 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - OEMCryptoResult result = AllowKeyUse("Generic_Decrypt", kControlAllowDecrypt, + OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", kControlAllowDecrypt, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; @@ -847,7 +882,7 @@ OEMCryptoResult SessionContext::Generic_Sign(const uint8_t* in_buffer, LOGE("[Generic_Sign(): CONTENT_KEY has wrong size; %d", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - OEMCryptoResult result = AllowKeyUse("Generic_Sign", kControlAllowSign, + OEMCryptoResult result = CheckKeyUse("Generic_Sign", kControlAllowSign, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { @@ -883,7 +918,7 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %d", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - OEMCryptoResult result = AllowKeyUse("Generic_Verify", kControlAllowVerify, + OEMCryptoResult result = CheckKeyUse("Generic_Verify", kControlAllowVerify, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { @@ -973,23 +1008,80 @@ bool SessionContext::CheckNonce(uint32_t nonce) { void SessionContext::FlushNonces() { nonce_table_.Flush(); } -bool SessionContext::IsUsageEntryValid() { +bool SessionContext::CheckUsageEntry() { if (!usage_entry_) return false; - return usage_entry_->UpdateTime(); + return usage_entry_->CheckForUse(); } -void SessionContext::ReleaseUsageEntry() { usage_entry_ = NULL; } +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); +} CryptoEngine::CryptoEngine(wvcdm::FileSystem* file_system) : root_of_trust_(config_provisioning_method()), file_system_(file_system), - usage_table_(new UsageTable(this)) { + usage_table_(this, file_system) { ERR_load_crypto_strings(); } CryptoEngine::~CryptoEngine() { + wvcdm::AutoLock lock(session_table_lock_); + ActiveSessions::iterator it; + for (it = sessions_.begin(); it != sessions_.end(); ++it) { + delete it->second; + } sessions_.clear(); - if (usage_table_) delete usage_table_; } void CryptoEngine::Terminate() {} @@ -1066,7 +1158,7 @@ OEMCryptoResult SessionContext::DecryptCENC( return OEMCrypto_ERROR_DECRYPT_FAILED; } - OEMCryptoResult result = AllowKeyUse("DecryptCENC", 0, buffer_type); + OEMCryptoResult result = CheckKeyUse("DecryptCENC", 0, buffer_type); if (result != OEMCrypto_SUCCESS) return result; const std::vector& content_key = current_content_key()->value(); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 2d34c7d2..49e5ad66 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -18,6 +18,7 @@ #include "oemcrypto_auth_mock.h" #include "oemcrypto_key_mock.h" #include "oemcrypto_rsa_key_shared.h" +#include "oemcrypto_usage_table_mock.h" #include "wv_cdm_types.h" namespace wvoec_mock { @@ -86,7 +87,8 @@ class SessionContext { current_content_key_(NULL), rsa_key_(rsa_key), allowed_schemes_(kSign_RSASSA_PSS), - usage_entry_(NULL) {} + usage_entry_(NULL), + usage_entry_status_(kNoUsageEntry) {} ~SessionContext(); bool isValid() { return valid_; } @@ -137,11 +139,12 @@ class SessionContext { const uint8_t* enc_mac_keys, size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, size_t pst_length); - bool 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, - const std::vector& pst, bool ctr_mode); + 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 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, @@ -175,7 +178,19 @@ class SessionContext { void AddNonce(uint32_t nonce); bool CheckNonce(uint32_t nonce); void FlushNonces(); - void ReleaseUsageEntry(); + + 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, @@ -185,9 +200,16 @@ class SessionContext { const std::vector& iv, const std::vector& message, std::vector* decrypted); - bool CheckNonceOrEntry(const KeyControlBlock& key_control_block, - const std::vector& pst); - bool IsUsageEntryValid(); + // 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, @@ -200,7 +222,9 @@ class SessionContext { 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); - OEMCryptoResult AllowKeyUse(const std::string& log_string, uint32_t use_type, + // 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(); } @@ -219,6 +243,12 @@ class SessionContext { 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); }; @@ -275,7 +305,7 @@ class CryptoEngine { OEMCrypto_HDCP_Capability config_current_hdcp_capability(); OEMCrypto_HDCP_Capability config_maximum_hdcp_capability(); - UsageTable* usage_table() { return usage_table_; } + UsageTable& usage_table() { return usage_table_; } wvcdm::FileSystem* file_system() { return file_system_; } bool config_local_display_only(); @@ -295,7 +325,7 @@ class CryptoEngine { AuthenticationRoot root_of_trust_; wvcdm::Lock session_table_lock_; wvcdm::FileSystem* file_system_; - UsageTable* usage_table_; + UsageTable usage_table_; CORE_DISALLOW_COPY_AND_ASSIGN(CryptoEngine); }; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index d7e2aa13..2c8b6d98 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -45,7 +45,14 @@ extern "C" OEMCryptoResult OEMCrypto_Initialize(void) { if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { LOGI("------------------------- OEMCrypto_Initialize(void)\n"); } - + if (crypto_engine) { + LOGE("------------------------- Calling Initialize without Terminate\n"); + if (crypto_engine->Initialized()) { + crypto_engine->Terminate(); + } + delete crypto_engine; + crypto_engine = NULL; + } // NOTE: This requires a compatible Filesystem implementation. wvcdm::FileSystem* fs = new wvcdm::FileSystem(); crypto_engine = new CryptoEngine(fs); @@ -1400,6 +1407,13 @@ extern "C" bool OEMCrypto_IsAntiRollbackHwPresent() { } extern "C" uint32_t OEMCrypto_SupportedCertificates() { + if (!crypto_engine) { + LOGE("OEMCrypto_GetProvisioningMethod: OEMCrypto Not Initialized."); + return 0; + } + if (crypto_engine->config_provisioning_method() == OEMCrypto_DrmCertificate) { + return 0; + } return OEMCrypto_Supports_RSA_2048bit | OEMCrypto_Supports_RSA_3072bit | OEMCrypto_Supports_RSA_CAST; if (!crypto_engine) { @@ -1575,14 +1589,7 @@ extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() { if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { LOGI("-- OEMCryptoResult OEMCrypto_UpdateUsageTable();\n"); } - if (!crypto_engine) { - LOGE("OEMCrypto_UpdateUsageTable: OEMCrypto Not Initialized."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (!crypto_engine->config_supports_usage_table()) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - return crypto_engine->usage_table()->UpdateTable(); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; } extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry( @@ -1600,8 +1607,13 @@ extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry( if (!crypto_engine->config_supports_usage_table()) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_DeactivateUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } std::vector pstv(pst, pst + pst_length); - return crypto_engine->usage_table()->DeactivateEntry(pstv); + return session_ctx->DeactivateUsageEntry(pstv); } extern "C" OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, @@ -1622,20 +1634,16 @@ extern "C" OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, if (!crypto_engine->config_supports_usage_table()) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } + if (!buffer_length) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } SessionContext* session_ctx = crypto_engine->FindSession(session); if (!session_ctx || !session_ctx->isValid()) { LOGE("[OEMCrypto_ReportUsage(): ERROR_INVALID_SESSION]"); return OEMCrypto_ERROR_INVALID_SESSION; } std::vector pstv(pst, pst + pst_length); - UsageTableEntry* entry = crypto_engine->usage_table()->FindEntry(pstv); - if (!entry) { - LOGE("[OEMCrypto_ReportUsage(): Usage Table Entry not found]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - OEMCryptoResult sts = - entry->ReportUsage(session_ctx, pstv, buffer, buffer_length); - crypto_engine->usage_table()->UpdateTable(); + OEMCryptoResult sts = session_ctx->ReportUsage(pstv, buffer, buffer_length); if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { dump_hex("usage buffer", buffer, *buffer_length); @@ -1648,68 +1656,14 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry( OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length) { - if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { - LOGI("-- OEMCryptoResult OEMCrypto_DeleteUsageEntry(\n"); - if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { - dump_hex("pst", pst, pst_length); - dump_hex("message", message, message_length); - dump_hex("signature", signature, signature_length); - } - } - if (!crypto_engine) { - LOGE("OEMCrypto_DeleteUsageEntry: OEMCrypto Not Initialized."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (!crypto_engine->config_supports_usage_table()) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - SessionContext* session_ctx = crypto_engine->FindSession(session); - if (!session_ctx || !session_ctx->isValid()) { - LOGE("[OEMCrypto_DeleteUsageEntry(): ERROR_INVALID_SESSION]"); - return OEMCrypto_ERROR_INVALID_SESSION; - } - if (message == NULL || message_length == 0 || signature == NULL || - signature_length == 0 || pst == NULL || pst_length == 0) { - LOGE("[OEMCrypto_DeleteUsageEntry(): OEMCrypto_ERROR_INVALID_CONTEXT]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - if (!RangeCheck(message, message_length, pst, pst_length, false)) { - LOGE("[OEMCrypto_DeleteUsageEntry(): range check.]"); - return OEMCrypto_ERROR_INVALID_CONTEXT; - } - // Validate message signature - if (!session_ctx->ValidateMessage(message, message_length, signature, - signature_length)) { - LOGE("[OEMCrypto_DeleteUsageEntry(): OEMCrypto_ERROR_SIGNATURE_FAILURE.]"); - return OEMCrypto_ERROR_SIGNATURE_FAILURE; - } - std::vector pstv(pst, pst + pst_length); - if (crypto_engine->usage_table()->DeleteEntry(pstv)) { - return OEMCrypto_SUCCESS; - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; + // TODO(fredgc): delete this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; } extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t* pst, size_t pst_length) { - if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { - LOGI("-- OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry()\n"); - if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { - dump_hex("pst", pst, pst_length); - } - } - if (!crypto_engine) { - LOGE("OEMCrypto_ForceDeleteUsageEntry: OEMCrypto Not Initialized."); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - if (!crypto_engine->config_supports_usage_table()) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; - } - std::vector pstv(pst, pst + pst_length); - if (crypto_engine->usage_table()->DeleteEntry(pstv)) { - return OEMCrypto_SUCCESS; - } - return OEMCrypto_ERROR_UNKNOWN_FAILURE; + // TODO(fredgc): delete this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; } extern "C" OEMCryptoResult OEMCrypto_DeleteOldUsageTable() { @@ -1723,9 +1677,7 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteOldUsageTable() { if (!crypto_engine->config_supports_usage_table()) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } - crypto_engine->usage_table()->Clear(); - crypto_engine->usage_table()->UpdateTable(); - return OEMCrypto_SUCCESS; + return crypto_engine->usage_table().DeleteOldUsageTable(); } extern "C" bool OEMCrypto_IsSRMUpdateSupported() { return false; } @@ -1744,48 +1696,183 @@ extern "C" OEMCryptoResult OEMCrypto_RemoveSRM() { } extern "C" OEMCryptoResult OEMCrypto_CreateUsageTableHeader( - uint8_t* header_buffer, size_t* header_buffer_length) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + uint8_t* header_buffer, + size_t* header_buffer_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CreateUsageTableHeader()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CreateUsageTableHeader: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + LOGE("OEMCrypto_CreateUsageTableHeader: Configured without Usage Tables."); + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + return crypto_engine->usage_table() + .CreateUsageTableHeader(header_buffer, header_buffer_length); } extern "C" OEMCryptoResult OEMCrypto_LoadUsageTableHeader( const uint8_t* buffer, size_t buffer_length) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadUsageTableHeader()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadUsageTableHeader: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!buffer) { + LOGE("OEMCrypto_LoadUsageTableHeader: buffer null."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + std::vector bufferv(buffer, buffer + buffer_length); + return crypto_engine->usage_table().LoadUsageTableHeader(bufferv); } extern "C" OEMCryptoResult OEMCrypto_CreateNewUsageEntry( OEMCrypto_SESSION session, uint32_t* usage_entry_number) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CreateNewUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CreateNewUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_CreateNewUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (!usage_entry_number) { + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + OEMCryptoResult sts = session_ctx->CreateNewUsageEntry(usage_entry_number); + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- usage_entry_number = %d", *usage_entry_number); + } + return sts; } extern "C" OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, uint32_t index, const uint8_t* buffer, size_t buffer_size) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_LoadUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_LoadUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_LoadUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (!buffer) { + LOGE("[OEMCrypto_LoadUsageEntry(): buffer null]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + std::vector bufferv(buffer, buffer + buffer_size); + return session_ctx->LoadUsageEntry(index, bufferv); } extern "C" OEMCryptoResult OEMCrypto_UpdateUsageEntry( OEMCrypto_SESSION session, uint8_t* header_buffer, size_t* header_buffer_length, uint8_t* entry_buffer, size_t* entry_buffer_length) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_UpdateUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_UpdateUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!header_buffer_length || !entry_buffer_length) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_UpdateUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + return session_ctx->UpdateUsageEntry(header_buffer, header_buffer_length, + entry_buffer, entry_buffer_length); } extern "C" OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader( uint32_t new_table_size, uint8_t* header_buffer, size_t* header_buffer_length) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader()\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_ShrinkUsageTableHeader: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + if (!header_buffer) { + LOGE("OEMCrypto_ShrinkUsageTableHeader: buffer null."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return crypto_engine->usage_table().ShrinkUsageTableHeader( + new_table_size, header_buffer, header_buffer_length); } extern "C" OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, uint32_t new_index) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_MoveEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_MoveEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_MoveEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + return session_ctx->MoveEntry(new_index); } extern "C" OEMCryptoResult OEMCrypto_CopyOldUsageEntry( OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length) { - return OEMCrypto_ERROR_NOT_IMPLEMENTED; + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_CopyOldUsageEntry(\n"); + } + if (!crypto_engine) { + LOGE("OEMCrypto_CopyOldUsageEntry: OEMCrypto Not Initialized."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (!crypto_engine->config_supports_usage_table()) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; + } + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_CopyOldUsageEntry(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + std::vector pstv(pst, pst + pst_length); + return session_ctx->CopyOldUsageEntry(pstv); } } // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp index ee27f61b..7bd50cea 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.cpp @@ -24,83 +24,118 @@ #include "string_conversions.h" #include "wv_cdm_constants.h" +namespace { +const size_t kMagicLength = 8; +const char* kEntryVerification = "USEENTRY"; +const char* kHeaderVerification = "USEHEADR"; +// Offset into a signed block where we start encrypting. We need to +// skip the signature and the iv. +const size_t kEncryptionOffset = SHA256_DIGEST_LENGTH + SHA256_DIGEST_LENGTH; +// A structure that holds an usage entry and its signature. +struct SignedEntryBlock { + uint8_t signature[SHA256_DIGEST_LENGTH]; + uint8_t iv[SHA256_DIGEST_LENGTH]; + uint8_t verification[kMagicLength]; + wvoec_mock::StoredUsageEntry data; +}; +// This has the data in the header of constant size. There is also an array +// of generation numbers. +struct SignedHeaderBlock { + uint8_t signature[SHA256_DIGEST_LENGTH]; + uint8_t iv[SHA256_DIGEST_LENGTH]; + uint8_t verification[kMagicLength]; + int64_t master_generation; + uint64_t count; +}; + +} // namespace + namespace wvoec_mock { -UsageTableEntry::UsageTableEntry(const std::vector &pst_hash, - SessionContext *ctx) - : pst_hash_(pst_hash), - time_of_license_received_(time(NULL)), - time_of_first_decrypt_(0), - time_of_last_decrypt_(0), - status_(kUnused), - session_(ctx) {} - -UsageTableEntry::~UsageTableEntry() { - if (session_) session_->ReleaseUsageEntry(); +UsageTableEntry::UsageTableEntry(UsageTable* table, uint32_t index, + int64_t generation) + : usage_table_(table), recent_decrypt_(false), forbid_report_(true) { + memset(&data_, 0, sizeof(data_)); + data_.generation_number = generation; + data_.index = index; } -UsageTableEntry::UsageTableEntry(const StoredUsageEntry *buffer) { - pst_hash_.assign(buffer->pst_hash, buffer->pst_hash + SHA256_DIGEST_LENGTH); - time_of_license_received_ = buffer->time_of_license_received; - time_of_first_decrypt_ = buffer->time_of_first_decrypt; - time_of_last_decrypt_ = buffer->time_of_last_decrypt; - status_ = buffer->status; - mac_key_server_.assign(buffer->mac_key_server, - buffer->mac_key_server + wvcdm::MAC_KEY_SIZE); - mac_key_client_.assign(buffer->mac_key_client, - buffer->mac_key_client + wvcdm::MAC_KEY_SIZE); - session_ = NULL; +UsageTableEntry::~UsageTableEntry() { usage_table_->ReleaseEntry(data_.index); } + +OEMCryptoResult UsageTableEntry::SetPST(const uint8_t* pst, size_t pst_length) { + if (pst_length > kMaxPSTLength) return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + data_.pst_length = pst_length; + if (!pst) return OEMCrypto_ERROR_INVALID_CONTEXT; + memcpy(data_.pst, pst, pst_length); + data_.time_of_license_received = time(NULL); + return OEMCrypto_SUCCESS; } -void UsageTableEntry::SaveToBuffer(StoredUsageEntry *buffer) { - if (pst_hash_.size() != sizeof(buffer->pst_hash)) { - LOGE("Coding Error: pst hash has wrong size."); - return; +bool UsageTableEntry::VerifyPST(const uint8_t* pst, size_t pst_length) { + if (pst_length > kMaxPSTLength) return false; + if (data_.pst_length != pst_length) return false; + if (!pst) return false; + return 0 == memcmp(pst, data_.pst, pst_length); +} + +bool UsageTableEntry::VerifyMacKeys(const std::vector& server, + const std::vector& client) { + return (server.size() == wvcdm::MAC_KEY_SIZE) && + (client.size() == wvcdm::MAC_KEY_SIZE) && + (0 == memcmp(&server[0], data_.mac_key_server, wvcdm::MAC_KEY_SIZE)) && + (0 == memcmp(&client[0], data_.mac_key_client, wvcdm::MAC_KEY_SIZE)); +} + +bool UsageTableEntry::SetMacKeys(const std::vector& server, + const std::vector& client) { + if ((server.size() != wvcdm::MAC_KEY_SIZE) || + (client.size() != wvcdm::MAC_KEY_SIZE)) + return false; + memcpy(data_.mac_key_server, &server[0], wvcdm::MAC_KEY_SIZE); + memcpy(data_.mac_key_client, &client[0], wvcdm::MAC_KEY_SIZE); + return true; +} + +bool UsageTableEntry::CheckForUse() { + if (Inactive()) return false; + recent_decrypt_ = true; + if (data_.status == kUnused) { + data_.status = kActive; + data_.time_of_first_decrypt = time(NULL); + data_.generation_number++; + usage_table_->IncrementGeneration(); } - memcpy(buffer->pst_hash, &pst_hash_[0], pst_hash_.size()); - buffer->time_of_license_received = time_of_license_received_; - buffer->time_of_first_decrypt = time_of_first_decrypt_; - buffer->time_of_last_decrypt = time_of_last_decrypt_; - buffer->status = status_; - memcpy(buffer->mac_key_server, &mac_key_server_[0], wvcdm::MAC_KEY_SIZE); - memcpy(buffer->mac_key_client, &mac_key_client_[0], wvcdm::MAC_KEY_SIZE); + return true; } -void UsageTableEntry::Deactivate() { - if (status_ == kUnused) { - status_ = kInactiveUnused; - } else if (status_ == kActive) { - status_ = kInactiveUsed; - } - if (session_) { - session_->ReleaseUsageEntry(); - session_ = NULL; +void UsageTableEntry::Deactivate(const std::vector& pst) { + if (data_.status == kUnused) { + data_.status = kInactiveUnused; + } else if (data_.status == kActive) { + data_.status = kInactiveUsed; } + forbid_report_ = false; + data_.generation_number++; + usage_table_->IncrementGeneration(); } -bool UsageTableEntry::UpdateTime() { - int64_t now = time(NULL); - switch (status_) { - case kUnused: - status_ = kActive; - time_of_first_decrypt_ = now; - time_of_last_decrypt_ = now; - return true; - case kActive: - time_of_last_decrypt_ = now; - return true; - case kInactive: - case kInactiveUsed: - case kInactiveUnused: - return false; +OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, + uint8_t* buffer, + size_t* buffer_length) { + if (forbid_report_) return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE; + if (recent_decrypt_) return OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE; + if (pst.size() == 0 || pst.size() > kMaxPSTLength || + pst.size() != data_.pst_length) { + LOGE("ReportUsage: bad pst length = %d, should be %d.", + pst.size(), data_.pst_length); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (memcmp(&pst[0], data_.pst, data_.pst_length)) { + LOGE("ReportUsage: wrong pst %s, should be %s.", + wvcdm::b2a_hex(pst).c_str(), + wvcdm::HexEncode(data_.pst, data_.pst_length).c_str()); + return OEMCrypto_ERROR_INVALID_CONTEXT; } - return false; -} - -OEMCryptoResult UsageTableEntry::ReportUsage(SessionContext *session, - const std::vector &pst, - uint8_t *buffer, - size_t *buffer_length) { size_t length_needed = wvcdm::Unpacked_PST_Report::report_size(pst.size()); if (*buffer_length < length_needed) { *buffer_length = length_needed; @@ -112,206 +147,426 @@ OEMCryptoResult UsageTableEntry::ReportUsage(SessionContext *session, } wvcdm::Unpacked_PST_Report pst_report(buffer); int64_t now = time(NULL); - pst_report.set_seconds_since_license_received(now - time_of_license_received_); - pst_report.set_seconds_since_first_decrypt(now - time_of_first_decrypt_); - pst_report.set_seconds_since_last_decrypt(now - time_of_last_decrypt_); - pst_report.set_status(status_); + pst_report.set_seconds_since_license_received(now - + data_.time_of_license_received); + pst_report.set_seconds_since_first_decrypt(now - data_.time_of_first_decrypt); + pst_report.set_seconds_since_last_decrypt(now - data_.time_of_last_decrypt); + pst_report.set_status(data_.status); pst_report.set_clock_security_level(kSecureTimer); - pst_report.set_pst_length(static_cast(pst.size())); - memcpy(pst_report.pst(), &pst[0], pst.size()); + pst_report.set_pst_length(data_.pst_length); + memcpy(pst_report.pst(), data_.pst, data_.pst_length); unsigned int md_len = SHA_DIGEST_LENGTH; - if (!HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(), + if (!HMAC(EVP_sha1(), data_.mac_key_client, wvcdm::MAC_KEY_SIZE, buffer + SHA_DIGEST_LENGTH, length_needed - SHA_DIGEST_LENGTH, pst_report.signature(), &md_len)) { - LOGE("UsageTableEntry: could not compute signature."); + LOGE("ReportUsage: could not compute signature."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - session->set_mac_key_server(mac_key_server_); - session->set_mac_key_client(mac_key_client_); - return OEMCrypto_SUCCESS; } -bool UsageTableEntry::VerifyOrSetMacKeys(const std::vector &server, - const std::vector &client) { - if (mac_key_server_.size() == 0) { // Not set yet, so set it now. - mac_key_server_ = server; - mac_key_client_ = client; - return true; - } else { - return (mac_key_server_ == server && mac_key_client_ == client); +void UsageTableEntry::UpdateAndIncrement() { + if (recent_decrypt_) { + data_.time_of_last_decrypt = time(NULL); + recent_decrypt_ = false; } + data_.generation_number++; + usage_table_->IncrementGeneration(); + forbid_report_ = false; } -UsageTable::UsageTable(CryptoEngine *ce) { - ce_ = ce; - generation_ = 0; - table_.clear(); - - // Load saved table. - wvcdm::FileSystem *file_system = ce->file_system(); - wvcdm::File *file; - std::string path; - // Note: this path is OK for a real implementation, but using security level 1 - // would be better. - if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, - &path)) { - LOGE("UsageTable: Unable to get base path"); - return; - } - - std::string filename = path + "UsageTable.dat"; - if (!file_system->Exists(filename)) { - if (LogCategoryEnabled(kLoggingTraceUsageTable)) { - LOGI("UsageTable: No saved usage table. Creating new table."); - } - return; - } - - size_t file_size = file_system->FileSize(filename); - std::vector encrypted_buffer(file_size); - std::vector buffer(file_size); - StoredUsageTable *stored_table = - reinterpret_cast(&buffer[0]); - StoredUsageTable *encrypted_table = - reinterpret_cast(&encrypted_buffer[0]); - - file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly); - if (!file) { - LOGE("UsageTable: File open failed: %s", path.c_str()); - return; - } - file->Read(reinterpret_cast(&encrypted_buffer[0]), file_size); - file->Close(); - - // Verify the signature of the usage table file. +OEMCryptoResult UsageTableEntry::SaveData(CryptoEngine* ce, + SessionContext* session, + uint8_t* signed_buffer, + size_t buffer_size) { + // buffer_size was determined by calling function. + if (buffer_size != SignedEntrySize()) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + std::vector clear_buffer(buffer_size); + memset(&clear_buffer[0], 0, buffer_size); + memset(signed_buffer, 0, buffer_size); + SignedEntryBlock* clear = + reinterpret_cast(&clear_buffer[0]); + SignedEntryBlock* encrypted = + reinterpret_cast(signed_buffer); + clear->data = this->data_; // Copy the current data. + memcpy(clear->verification, kEntryVerification, kMagicLength); // This should be encrypted and signed with a device specific key. // For the reference implementation, I'm just going to use the keybox key. const bool override_to_real = true; - const std::vector &key = ce_->DeviceRootKey(override_to_real); + const std::vector& key = ce->DeviceRootKey(override_to_real); - uint8_t computed_signature[SHA256_DIGEST_LENGTH]; - unsigned int sig_length = sizeof(computed_signature); - if (!HMAC(EVP_sha256(), &key[0], key.size(), - &encrypted_buffer[SHA256_DIGEST_LENGTH], - file_size - SHA256_DIGEST_LENGTH, computed_signature, - &sig_length)) { - LOGE("UsageTable: Could not recreate signature."); - table_.clear(); - return; - } - if (memcmp(encrypted_table->signature, computed_signature, sig_length)) { - LOGE("UsageTable: Invalid signature given: %s", - wvcdm::HexEncode(&encrypted_buffer[0], sig_length).c_str()); - LOGE("UsageTable: Invalid signature computed: %s", - wvcdm::HexEncode(computed_signature, sig_length).c_str()); - table_.clear(); - return; - } - - // Next, decrypt the table. - uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; - memcpy(iv_buffer, encrypted_table->iv, wvcdm::KEY_IV_SIZE); - AES_KEY aes_key; - AES_set_decrypt_key(&key[0], 128, &aes_key); - AES_cbc_encrypt(&encrypted_buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE], - &buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE], - file_size - SHA256_DIGEST_LENGTH - wvcdm::KEY_IV_SIZE, - &aes_key, iv_buffer, AES_DECRYPT); - - // Next, read the generation number from a different location. - // On a real implementation, you should NOT put the generation number in - // a file in user space. It should be stored in secure memory. For the - // reference implementation, we'll just pretend this is secure. - std::string filename2 = path + "GenerationNumber.dat"; - file = file_system->Open(filename2, wvcdm::FileSystem::kReadOnly); - if (!file) { - LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str()); - generation_ = 0; - table_.clear(); - return; - } - file->Read(reinterpret_cast(&generation_), sizeof(int64_t)); - file->Close(); - if (stored_table->generation == generation_ + 1) { - if (LogCategoryEnabled(kLoggingTraceUsageTable)) { - LOGW("UsageTable: File is one generation old. Acceptable rollback."); - } - } else if (stored_table->generation == generation_ - 1) { - if (LogCategoryEnabled(kLoggingTraceUsageTable)) { - LOGW("UsageTable: File is one generation new. Acceptable rollback."); - } - // This might happen if the generation number was rolled back? - } else if (stored_table->generation != generation_) { - LOGE("UsageTable: Rollback detected. Clearing Usage Table. %lx -> %lx", - generation_, stored_table->generation); - table_.clear(); - generation_ = 0; - return; - } - - // At this point, the stored table looks valid. We can load in all the - // entries. - for (uint64_t i = 0; i < stored_table->count; i++) { - UsageTableEntry *entry = - new UsageTableEntry(&stored_table->entries[i].entry); - table_[entry->pst_hash()] = entry; - } - if (LogCategoryEnabled(kLoggingTraceUsageTable)) { - LOGI("UsageTable: loaded %d entries.", stored_table->count); - } -} - -bool UsageTable::SaveToFile() { - // This is always called by a locking function. - // Update the generation number so we can detect rollback. - generation_++; - // Now save data to the file as seen in the constructor, above. - size_t file_size = sizeof(StoredUsageTable) + - table_.size() * sizeof(AlignedStoredUsageEntry); - std::vector buffer(file_size); - std::vector encrypted_buffer(file_size); - StoredUsageTable *stored_table = - reinterpret_cast(&buffer[0]); - StoredUsageTable *encrypted_table = - reinterpret_cast(&encrypted_buffer[0]); - stored_table->generation = generation_; - stored_table->count = 0; - for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) { - UsageTableEntry *entry = i->second; - entry->SaveToBuffer(&stored_table->entries[stored_table->count].entry); - stored_table->count++; - } - - // This should be encrypted and signed with a device specific key. - // For the reference implementation, I'm just going to use the keybox key. - const bool override_to_real = true; - const std::vector &key = ce_->DeviceRootKey(override_to_real); - - // Encrypt the table. - RAND_bytes(encrypted_table->iv, wvcdm::KEY_IV_SIZE); - uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; - memcpy(iv_buffer, encrypted_table->iv, wvcdm::KEY_IV_SIZE); + // Encrypt the entry. + RAND_bytes(encrypted->iv, wvcdm::KEY_IV_SIZE); + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; // working iv buffer. + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); AES_KEY aes_key; AES_set_encrypt_key(&key[0], 128, &aes_key); - AES_cbc_encrypt(&buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE], - &encrypted_buffer[SHA256_DIGEST_LENGTH + wvcdm::KEY_IV_SIZE], - file_size - SHA256_DIGEST_LENGTH - wvcdm::KEY_IV_SIZE, - &aes_key, iv_buffer, AES_ENCRYPT); + AES_cbc_encrypt( + &clear_buffer[kEncryptionOffset], &signed_buffer[kEncryptionOffset], + buffer_size - kEncryptionOffset, &aes_key, iv_buffer, AES_ENCRYPT); - // Sign the table. - unsigned int sig_length = sizeof(stored_table->signature); + // Sign the entry. + unsigned int sig_length = SHA256_DIGEST_LENGTH; if (!HMAC(EVP_sha256(), &key[0], key.size(), - &encrypted_buffer[SHA256_DIGEST_LENGTH], - file_size - SHA256_DIGEST_LENGTH, encrypted_table->signature, + &signed_buffer[SHA256_DIGEST_LENGTH], + buffer_size - SHA256_DIGEST_LENGTH, encrypted->signature, &sig_length)) { - LOGE("UsageTable: Could not sign table."); - return false; + LOGE("SaveUsageEntry: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, + const std::vector& buffer) { + if (buffer.size() < SignedEntrySize()) return OEMCrypto_ERROR_SHORT_BUFFER; + if (buffer.size() > SignedEntrySize()) + LOGW("LoadUsageTableEntry: buffer is large. %d > %d", buffer.size(), + SignedEntrySize()); + std::vector clear_buffer(buffer.size()); + SignedEntryBlock* clear = + reinterpret_cast(&clear_buffer[0]); + const SignedEntryBlock* encrypted = + reinterpret_cast(&buffer[0]); + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector& key = ce->DeviceRootKey(override_to_real); + + // Verify the signature of the usage entry. Sign encrypted into clear buffer. + unsigned int sig_length = SHA256_DIGEST_LENGTH; + if (!HMAC(EVP_sha256(), &key[0], key.size(), &buffer[SHA256_DIGEST_LENGTH], + buffer.size() - SHA256_DIGEST_LENGTH, clear->signature, + &sig_length)) { + LOGE("LoadUsageEntry: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) { + LOGE("LoadUsageEntry: Signature did not match."); + LOGE("LoadUsageEntry: Invalid signature given: %s", + wvcdm::HexEncode(encrypted->signature, sig_length).c_str()); + LOGE("LoadUsageEntry: Invalid signature computed: %s", + wvcdm::HexEncode(clear->signature, sig_length).c_str()); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; } - wvcdm::FileSystem *file_system = ce_->file_system(); - wvcdm::File *file; + // Next, decrypt the entry. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&buffer[kEncryptionOffset], &clear_buffer[kEncryptionOffset], + buffer.size() - kEncryptionOffset, &aes_key, iv_buffer, + AES_DECRYPT); + + // Check the verification string is correct. + if (memcmp(kEntryVerification, clear->verification, kMagicLength)) { + LOGE("LoadUsageEntry: Invalid magic: %s=%8.8s expected: %s=%8.8s", + wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), + clear->verification, + wvcdm::HexEncode(reinterpret_cast(kEntryVerification), + kMagicLength).c_str(), + reinterpret_cast(kEntryVerification)); + return OEMCrypto_ERROR_BAD_MAGIC; + } + + // Check that the index is correct. + if (index != clear->data.index) { + LOGE("LoadUsageEntry: entry says index is %d, not %d", clear->data.index, + index); + return OEMCrypto_ERROR_INVALID_SESSION; + } + if (clear->data.status > kInactiveUnused) { + LOGE("LoadUsageEntry: entry has bad status %d", clear->data.status); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + this->data_ = clear->data; + return OEMCrypto_SUCCESS; +} + +size_t UsageTableEntry::SignedEntrySize() { + size_t base = sizeof(SignedEntryBlock); + // round up to make even number of blocks: + size_t blocks = (base - 1) / wvcdm::KEY_IV_SIZE + 1; + return blocks * wvcdm::KEY_IV_SIZE; +} + +size_t UsageTable::SignedHeaderSize(size_t count) { + size_t base = sizeof(SignedHeaderBlock) + count * sizeof(int64_t); + // round up to make even number of blocks: + size_t blocks = (base - 1) / wvcdm::KEY_IV_SIZE + 1; + return blocks * wvcdm::KEY_IV_SIZE; +} + +OEMCryptoResult UsageTable::UpdateUsageEntry(SessionContext* session, + UsageTableEntry* entry, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length) { + size_t signed_header_size = SignedHeaderSize(generation_numbers_.size()); + if (*entry_buffer_length < UsageTableEntry::SignedEntrySize() || + *header_buffer_length < signed_header_size) { + *entry_buffer_length = UsageTableEntry::SignedEntrySize(); + *header_buffer_length = signed_header_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *entry_buffer_length = UsageTableEntry::SignedEntrySize(); + *header_buffer_length = signed_header_size; + if ((!header_buffer) || (!entry_buffer)) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + entry->UpdateAndIncrement(); + generation_numbers_[entry->index()] = entry->generation_number(); + OEMCryptoResult result = + entry->SaveData(ce_, session, entry_buffer, *entry_buffer_length); + if (result != OEMCrypto_SUCCESS) return result; + result = SaveUsageTableHeader(header_buffer, *header_buffer_length); + return result; +} + +OEMCryptoResult UsageTable::CreateNewUsageEntry(SessionContext* session, + UsageTableEntry** entry, + uint32_t* usage_entry_number) { + if (!header_loaded_) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (!usage_entry_number) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + uint32_t index = generation_numbers_.size(); + UsageTableEntry* new_entry = + new UsageTableEntry(this, index, master_generation_number_); + generation_numbers_.push_back(master_generation_number_); + sessions_.push_back(session); + master_generation_number_++; + *entry = new_entry; + *usage_entry_number = index; + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTable::LoadUsageEntry(SessionContext* session, + UsageTableEntry** entry, + uint32_t index, + const std::vector& buffer) { + if (!header_loaded_) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (index >= generation_numbers_.size()) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + if (sessions_[index]) { + LOGE("LoadUsageEntry: index %d used by other session.", index); + return OEMCrypto_ERROR_INVALID_SESSION; + } + UsageTableEntry* new_entry = + new UsageTableEntry(this, index, master_generation_number_); + + OEMCryptoResult status = new_entry->LoadData(ce_, index, buffer); + if (status != OEMCrypto_SUCCESS) { + delete new_entry; + return status; + } + if (new_entry->generation_number() != generation_numbers_[index]) { + LOGE("Generation SKEW: %ld -> %ld", new_entry->generation_number(), + generation_numbers_[index]); + if ((new_entry->generation_number() + 1 < generation_numbers_[index]) || + (new_entry->generation_number() - 1 > generation_numbers_[index])) { + delete new_entry; + return OEMCrypto_ERROR_GENERATION_SKEW; + } + status = OEMCrypto_WARNING_GENERATION_SKEW; + } + sessions_[index] = session; + *entry = new_entry; + return status; +} + +OEMCryptoResult UsageTable::ShrinkUsageTableHeader( + uint32_t new_table_size, uint8_t* header_buffer, + size_t* header_buffer_length) { + if (new_table_size > generation_numbers_.size()) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + size_t signed_header_size = SignedHeaderSize(new_table_size); + if (*header_buffer_length < signed_header_size) { + *header_buffer_length = signed_header_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *header_buffer_length = signed_header_size; + for (size_t i = new_table_size; i < sessions_.size(); i++) { + if (sessions_[i]) { + LOGE("ShrinkUsageTableHeader: session open for %d", i); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + } + generation_numbers_.resize(new_table_size); + sessions_.resize(new_table_size); + master_generation_number_++; + return SaveUsageTableHeader(header_buffer, *header_buffer_length); +} + +OEMCryptoResult UsageTable::SaveUsageTableHeader(uint8_t* signed_buffer, + size_t buffer_size) { + if (!SaveGenerationNumber()) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + size_t count = generation_numbers_.size(); + // buffer_size was determined by calling function. + if (buffer_size != SignedHeaderSize(count)) + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + std::vector clear_buffer(buffer_size); + memset(&clear_buffer[0], 0, buffer_size); + memset(signed_buffer, 0, buffer_size); + SignedHeaderBlock* clear = + reinterpret_cast(&clear_buffer[0]); + SignedHeaderBlock* encrypted = + reinterpret_cast(signed_buffer); + + // Pack the clear data into the clear buffer. + memcpy(clear->verification, kHeaderVerification, kMagicLength); + clear->master_generation = master_generation_number_; + clear->count = count; + // This points to the variable size part of the buffer. + int64_t* stored_generations = + reinterpret_cast(&clear_buffer[sizeof(SignedHeaderBlock)]); + std::copy(generation_numbers_.begin(), generation_numbers_.begin() + count, + stored_generations); + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector& key = ce_->DeviceRootKey(override_to_real); + + // Encrypt the entry. + RAND_bytes(encrypted->iv, wvcdm::KEY_IV_SIZE); + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; // working iv buffer. + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt( + &clear_buffer[kEncryptionOffset], &signed_buffer[kEncryptionOffset], + buffer_size - kEncryptionOffset, &aes_key, iv_buffer, AES_ENCRYPT); + + // Sign the entry. + unsigned int sig_length = SHA256_DIGEST_LENGTH; + if (!HMAC(EVP_sha256(), &key[0], key.size(), + &signed_buffer[SHA256_DIGEST_LENGTH], + buffer_size - SHA256_DIGEST_LENGTH, encrypted->signature, + &sig_length)) { + LOGE("SaveUsageHeader: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult UsageTable::LoadUsageTableHeader( + const std::vector& buffer) { + if (!LoadGenerationNumber(false)) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + + if (buffer.size() < SignedHeaderSize(0)) return OEMCrypto_ERROR_SHORT_BUFFER; + std::vector clear_buffer(buffer.size()); + SignedHeaderBlock* clear = + reinterpret_cast(&clear_buffer[0]); + const SignedHeaderBlock* encrypted = + reinterpret_cast(&buffer[0]); + + // This should be encrypted and signed with a device specific key. + // For the reference implementation, I'm just going to use the keybox key. + const bool override_to_real = true; + const std::vector& key = ce_->DeviceRootKey(override_to_real); + + // Verify the signature of the usage entry. Sign encrypted into clear buffer. + unsigned int sig_length = SHA256_DIGEST_LENGTH; + if (!HMAC(EVP_sha256(), &key[0], key.size(), &buffer[SHA256_DIGEST_LENGTH], + buffer.size() - SHA256_DIGEST_LENGTH, clear->signature, + &sig_length)) { + LOGE("LoadUsageTableHeader: Could not sign entry."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + if (memcmp(clear->signature, encrypted->signature, SHA256_DIGEST_LENGTH)) { + LOGE("LoadUsageTableHeader: Signature did not match."); + LOGE("LoadUsageTableHeader: Invalid signature given: %s", + wvcdm::HexEncode(encrypted->signature, sig_length).c_str()); + LOGE("LoadUsageTableHeader: Invalid signature computed: %s", + wvcdm::HexEncode(clear->signature, sig_length).c_str()); + return OEMCrypto_ERROR_SIGNATURE_FAILURE; + } + + // Next, decrypt the entry. + uint8_t iv_buffer[wvcdm::KEY_IV_SIZE]; + memcpy(iv_buffer, encrypted->iv, wvcdm::KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_decrypt_key(&key[0], 128, &aes_key); + AES_cbc_encrypt(&buffer[kEncryptionOffset], &clear_buffer[kEncryptionOffset], + buffer.size() - kEncryptionOffset, &aes_key, iv_buffer, + AES_DECRYPT); + + // Check the verification string is correct. + if (memcmp(kHeaderVerification, clear->verification, kMagicLength)) { + LOGE("LoadUsageTableHeader: Invalid magic: %s=%8.8s expected: %s=%8.8s", + wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), + clear->verification, + wvcdm::HexEncode(reinterpret_cast(kHeaderVerification), + kMagicLength).c_str(), + reinterpret_cast(kHeaderVerification)); + return OEMCrypto_ERROR_BAD_MAGIC; + } + + // Check that size is correct, now that we know what it should be. + if (buffer.size() < SignedHeaderSize(clear->count)) { + return OEMCrypto_ERROR_SHORT_BUFFER; + } + if (buffer.size() > SignedHeaderSize(clear->count)) { + LOGW("LoadUsageTableHeader: buffer is large. %d > %d", buffer.size(), + SignedHeaderSize(clear->count)); + } + + OEMCryptoResult status = OEMCrypto_SUCCESS; + if (clear->master_generation != master_generation_number_) { + LOGE("Generation SKEW: %ld -> %ld", clear->master_generation, + master_generation_number_); + if ((clear->master_generation + 1 < master_generation_number_) || + (clear->master_generation - 1 > master_generation_number_)) { + return OEMCrypto_ERROR_GENERATION_SKEW; + } + status = OEMCrypto_WARNING_GENERATION_SKEW; + } + int64_t* stored_generations = + reinterpret_cast(&clear_buffer[0] + sizeof(SignedHeaderBlock)); + generation_numbers_.assign(stored_generations, + stored_generations + clear->count); + sessions_.clear(); + sessions_.resize(clear->count); + header_loaded_ = true; + return status; +} + +OEMCryptoResult UsageTable::MoveEntry(UsageTableEntry* entry, + uint32_t new_index) { + if (new_index >= generation_numbers_.size()) { + LOGE("MoveEntry: index beyond end of usage table %d >= %d", new_index, + generation_numbers_.size()); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (sessions_[new_index]) { + LOGE("MoveEntry: session open for %d", new_index); + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + if (!entry) { + LOGE("MoveEntry: null entry"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + sessions_[new_index] = sessions_[entry->index()]; + sessions_[entry->index()] = 0; + + entry->set_index(new_index); + generation_numbers_[new_index] = master_generation_number_; + entry->set_generation_number(master_generation_number_); + master_generation_number_++; + return OEMCrypto_SUCCESS; +} + +void UsageTable::IncrementGeneration() { + master_generation_number_++; + SaveGenerationNumber(); +} + +bool UsageTable::SaveGenerationNumber() { + wvcdm::FileSystem* file_system = ce_->file_system(); std::string path; // Note: this path is OK for a real implementation, but using security level 1 // would be better. @@ -320,115 +575,76 @@ bool UsageTable::SaveToFile() { LOGE("UsageTable: Unable to get base path"); return false; } - - std::string filename = path + "UsageTable.dat"; - if (!file_system->Exists(filename)) { - if (LogCategoryEnabled(kLoggingTraceUsageTable)) { - LOGI("UsageTable: No saved usage table. Creating new table."); - } - } - - file = file_system->Open( - filename, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate); - if (!file) { - LOGE("UsageTable: Could not save usage table: %s", path.c_str()); - return false; - } - file->Write(reinterpret_cast(&encrypted_buffer[0]), file_size); - file->Close(); - // On a real implementation, you should NOT put the generation number in // a file in user space. It should be stored in secure memory. - std::string filename2 = path + "GenerationNumber.dat"; - file = file_system->Open( - filename2, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate); + std::string filename = path + "GenerationNumber.dat"; + wvcdm::File* file = file_system->Open( + filename, wvcdm::FileSystem::kCreate | wvcdm::FileSystem::kTruncate); if (!file) { LOGE("UsageTable: File open failed: %s", path.c_str()); return false; } - file->Write(reinterpret_cast(&generation_), sizeof(int64_t)); + file->Write(reinterpret_cast(&master_generation_number_), + sizeof(int64_t)); file->Close(); return true; } -UsageTableEntry *UsageTable::FindEntry(const std::vector &pst) { - wvcdm::AutoLock lock(lock_); - return FindEntryLocked(pst); -} - -UsageTableEntry *UsageTable::FindEntryLocked(const std::vector &pst) { - std::vector pst_hash; - if (!ComputeHash(pst, pst_hash)) { - LOGE("UsageTable: Could not compute hash of pst."); - return NULL; - } - EntryMap::iterator it = table_.find(pst_hash); - if (it == table_.end()) { - return NULL; - } - return it->second; -} - -UsageTableEntry *UsageTable::CreateEntry(const std::vector &pst, - SessionContext *ctx) { - std::vector pst_hash; - if (!ComputeHash(pst, pst_hash)) { - LOGE("UsageTable: Could not compute hash of pst."); - return NULL; - } - UsageTableEntry *entry = new UsageTableEntry(pst_hash, ctx); - wvcdm::AutoLock lock(lock_); - table_[pst_hash] = entry; - return entry; -} - -OEMCryptoResult UsageTable::UpdateTable() { - wvcdm::AutoLock lock(lock_); - if (SaveToFile()) return OEMCrypto_SUCCESS; - return OEMCrypto_ERROR_UNKNOWN_FAILURE; -} - -OEMCryptoResult UsageTable::DeactivateEntry(const std::vector &pst) { - wvcdm::AutoLock lock(lock_); - UsageTableEntry *entry = FindEntryLocked(pst); - if (!entry) return OEMCrypto_ERROR_INVALID_CONTEXT; - entry->Deactivate(); - if (SaveToFile()) return OEMCrypto_SUCCESS; - return OEMCrypto_ERROR_UNKNOWN_FAILURE; -} - -bool UsageTable::DeleteEntry(const std::vector &pst) { - std::vector pst_hash; - if (!ComputeHash(pst, pst_hash)) { - LOGE("UsageTable: Could not compute hash of pst."); +bool UsageTable::LoadGenerationNumber(bool or_make_new_one) { + wvcdm::FileSystem* file_system = ce_->file_system(); + std::string path; + // Note: this path is OK for a real implementation, but using security level 1 + // would be better. + if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, + &path)) { + LOGE("UsageTable: Unable to get base path"); return false; } - wvcdm::AutoLock lock(lock_); - EntryMap::iterator it = table_.find(pst_hash); - if (it == table_.end()) return false; - if (it->second) delete it->second; - table_.erase(it); - return SaveToFile(); -} - -void UsageTable::Clear() { - wvcdm::AutoLock lock(lock_); - for (EntryMap::iterator i = table_.begin(); i != table_.end(); ++i) { - if (i->second) delete i->second; + // On a real implementation, you should NOT put the generation number in + // a file in user space. It should be stored in secure memory. + std::string filename = path + "GenerationNumber.dat"; + wvcdm::File* file = file_system->Open(filename, wvcdm::FileSystem::kReadOnly); + if (!file) { + if (or_make_new_one) { + RAND_bytes(reinterpret_cast(&master_generation_number_), + sizeof(int64_t)); + master_generation_number_ = 0; // TODO(fredgc): remove after debugging. + return true; + } + LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str()); + master_generation_number_ = 0; + return false; } - table_.clear(); + file->Read(reinterpret_cast(&master_generation_number_), + sizeof(int64_t)); + file->Close(); + return true; } -bool UsageTable::ComputeHash(const std::vector &pst, - std::vector &pst_hash) { - // The PST is not fixed size, and we have no promises that it is reasonbly - // sized, so we compute a hash of it, and store that instead. - pst_hash.resize(SHA256_DIGEST_LENGTH); - SHA256_CTX context; - if (!SHA256_Init(&context)) return false; - if (!SHA256_Update(&context, &pst[0], pst.size())) return false; - if (!SHA256_Final(&pst_hash[0], &context)) return false; - return true; +OEMCryptoResult UsageTable::CreateUsageTableHeader( + uint8_t* header_buffer, size_t* header_buffer_length) { + size_t signed_header_size = SignedHeaderSize(0); + if (*header_buffer_length < signed_header_size) { + *header_buffer_length = signed_header_size; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *header_buffer_length = signed_header_size; + if (!LoadGenerationNumber(true)) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + sessions_.clear(); + generation_numbers_.clear(); + header_loaded_ = true; + return SaveUsageTableHeader(header_buffer, *header_buffer_length); +} + +OEMCryptoResult UsageTable::CopyOldUsageEntry(UsageTableEntry* entry, + const std::vector& pst) { + // TODO(fredgc): add this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult UsageTable::DeleteOldUsageTable() { + // TODO(fredgc): add this. + return OEMCrypto_ERROR_NOT_IMPLEMENTED; } } // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.h index 03c0f8e2..fc51c489 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_usage_table_mock.h @@ -10,8 +10,9 @@ #include #include -#include "lock.h" #include "OEMCryptoCENC.h" +#include "file_store.h" +#include "lock.h" #include "openssl/sha.h" #include "wv_cdm_constants.h" @@ -19,85 +20,104 @@ namespace wvoec_mock { class SessionContext; class CryptoEngine; +class UsageTable; +const size_t kMaxPSTLength = 255; +// This is the data we store offline. struct StoredUsageEntry { - // To save disk space, we only store a hash of the pst. - uint8_t pst_hash[SHA256_DIGEST_LENGTH]; + int64_t generation_number; int64_t time_of_license_received; int64_t time_of_first_decrypt; int64_t time_of_last_decrypt; enum OEMCrypto_Usage_Entry_Status status; uint8_t mac_key_server[wvcdm::MAC_KEY_SIZE]; uint8_t mac_key_client[wvcdm::MAC_KEY_SIZE]; -}; -typedef union { - struct StoredUsageEntry entry; - uint8_t padding[128]; // multiple of block size and bigger than entry size. -} AlignedStoredUsageEntry; - -struct StoredUsageTable { - uint8_t signature[SHA256_DIGEST_LENGTH]; - uint8_t iv[wvcdm::KEY_IV_SIZE]; - int64_t generation; - uint64_t count; - AlignedStoredUsageEntry entries[]; + uint32_t index; + uint8_t pst[kMaxPSTLength+1]; // add 1 for padding. + uint8_t pst_length; }; class UsageTableEntry { public: - UsageTableEntry(const std::vector &pst_hash, SessionContext *ctx); - UsageTableEntry(const StoredUsageEntry *buffer); - ~UsageTableEntry(); - void SaveToBuffer(StoredUsageEntry *buffer); - OEMCrypto_Usage_Entry_Status status() const { return status_; } - bool inactive() const { return status_ >= kInactive; } - void Deactivate(); - bool UpdateTime(); - OEMCryptoResult ReportUsage(SessionContext *session, - const std::vector &pst, - uint8_t *buffer, - size_t *buffer_length); - // Set them if not set, verify if already set. - bool VerifyOrSetMacKeys(const std::vector &server, - const std::vector &client); - const std::vector &pst_hash() const { return pst_hash_; } - void set_session(SessionContext *session) { session_ = session; } + UsageTableEntry(UsageTable* table, uint32_t index, int64_t generation); + // owner_(owner), session_(session), loaded_(false) {} + ~UsageTableEntry(); // Free memory, remove reference in header. + bool Inactive() { return data_.status >= kInactive; } + OEMCryptoResult SetPST(const uint8_t* pst, size_t pst_length); + bool VerifyPST(const uint8_t* pst, size_t pst_length); + bool VerifyMacKeys(const std::vector& server, + const std::vector& client); + bool SetMacKeys(const std::vector& server, + const std::vector& client); + // Returns false if the entry is inactive. Otherwise, returns true. + // If the status was unused, it is updated, and decrypt times are flaged + // for update. + bool CheckForUse(); + void Deactivate(const std::vector& pst); + OEMCryptoResult ReportUsage(const std::vector& pst, uint8_t* buffer, + size_t* buffer_length); + void UpdateAndIncrement(); + OEMCryptoResult SaveData(CryptoEngine* ce, SessionContext* session, + uint8_t* signed_buffer, size_t buffer_size); + OEMCryptoResult LoadData(CryptoEngine* ce, uint32_t index, + const std::vector& buffer); + int64_t generation_number() { return data_.generation_number; } + void set_generation_number(int64_t value) { data_.generation_number = value; } + void set_index(int32_t index) { data_.index = index; } + uint32_t index() { return data_.index; } + static size_t SignedEntrySize(); private: - std::vector pst_hash_; - int64_t time_of_license_received_; - int64_t time_of_first_decrypt_; - int64_t time_of_last_decrypt_; - enum OEMCrypto_Usage_Entry_Status status_; - std::vector mac_key_server_; - std::vector mac_key_client_; - - SessionContext *session_; + UsageTable* usage_table_; // Owner of this object. + bool recent_decrypt_; + bool forbid_report_; + StoredUsageEntry data_; }; class UsageTable { public: - UsageTable(CryptoEngine *ce); - ~UsageTable() { Clear(); } - UsageTableEntry *FindEntry(const std::vector &pst); - UsageTableEntry *CreateEntry(const std::vector &pst, - SessionContext *ctx); - OEMCryptoResult UpdateTable(); - OEMCryptoResult DeactivateEntry(const std::vector &pst); - bool DeleteEntry(const std::vector &pst); - void Clear(); + UsageTable(CryptoEngine* ce, wvcdm::FileSystem* file_system) + : ce_(ce), file_system_(file_system), header_loaded_(false){}; + + OEMCryptoResult CreateNewUsageEntry(SessionContext* session, + UsageTableEntry** entry, + uint32_t* usage_entry_number); + OEMCryptoResult LoadUsageEntry(SessionContext* session, + UsageTableEntry** entry, uint32_t index, + const std::vector& buffer); + OEMCryptoResult UpdateUsageEntry(SessionContext* session, + UsageTableEntry* entry, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length); + OEMCryptoResult MoveEntry(UsageTableEntry* entry, uint32_t new_index); + OEMCryptoResult CopyOldUsageEntry(UsageTableEntry* entry, + const std::vector& pst); + OEMCryptoResult DeleteOldUsageTable(); + OEMCryptoResult CreateUsageTableHeader(uint8_t* header_buffer, + size_t* header_buffer_length); + OEMCryptoResult LoadUsageTableHeader(const std::vector& buffer); + OEMCryptoResult ShrinkUsageTableHeader(uint32_t new_table_size, + uint8_t* header_buffer, + size_t* header_buffer_length); + // Diassociates this entry with it's session. + void ReleaseEntry(uint32_t index) { sessions_[index] = 0; } + void IncrementGeneration(); + static size_t SignedHeaderSize(size_t count); private: - UsageTableEntry *FindEntryLocked(const std::vector &pst); - bool SaveToFile(); - bool ComputeHash(const std::vector &pst, - std::vector &pst_hash); + OEMCryptoResult SaveUsageTableHeader(uint8_t* signed_buffer, + size_t buffer_size); + bool SaveGenerationNumber(); + bool LoadGenerationNumber(bool or_make_new_one); - typedef std::map, UsageTableEntry *> EntryMap; - EntryMap table_; - wvcdm::Lock lock_; - int64_t generation_; - CryptoEngine *ce_; + CryptoEngine* ce_; + wvcdm::FileSystem* file_system_; + bool header_loaded_; + int64_t master_generation_number_; + std::vector generation_numbers_; + std::vector sessions_; }; } // namespace wvoec_mock diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index be99018b..ae5d584b 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -819,8 +819,47 @@ void Session::InstallRSASessionTestKey(const vector& wrapped_rsa_key) { GenerateDerivedKeysFromSessionKey(); } +void Session::CreateNewUsageEntry() { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_)); +} + +void Session::UpdateUsageEntry(std::vector* header_buffer) { + size_t header_buffer_length = 0; + size_t entry_buffer_length = 0; + ASSERT_EQ( + OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_UpdateUsageEntry(session_id(), NULL, &header_buffer_length, + NULL, &entry_buffer_length)); + ASSERT_LT(0u, header_buffer_length); + header_buffer->resize(header_buffer_length); + ASSERT_LT(0u, entry_buffer_length); + encrypted_usage_entry_.resize(entry_buffer_length); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry( + session_id(), &(header_buffer->front()), &header_buffer_length, + &encrypted_usage_entry_[0], &entry_buffer_length)); +} + +void Session::DeactivateUsageEntry(const std::string& pst, + OEMCryptoResult expect_result) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_DeactivateUsageEntry( + session_id(), reinterpret_cast(pst.c_str()), + pst.length())); +} + +void Session::LoadUsageEntry(uint32_t index, const vector& buffer) { + usage_entry_number_ = index; + encrypted_usage_entry_ = buffer; + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(session_id(), index, &buffer[0], buffer.size())); +} + void Session::GenerateReport(const std::string& pst, bool expect_success, Session* other) { + ASSERT_TRUE(open_); if (other) { // If other is specified, copy mac keys. mac_key_server_ = other->mac_key_server_; mac_key_client_ = other->mac_key_client_; @@ -834,7 +873,7 @@ void Session::GenerateReport(const std::string& pst, bool expect_success, } if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { ASSERT_EQ(wvcdm::Unpacked_PST_Report::report_size(pst.length()), length); - pst_report_buffer_.resize(length); + pst_report_buffer_.assign(length, 0xFF); // Fill with garbage values. } sts = OEMCrypto_ReportUsage(session_id(), reinterpret_cast(pst.c_str()), @@ -843,6 +882,7 @@ void Session::GenerateReport(const std::string& pst, bool expect_success, ASSERT_NE(OEMCrypto_SUCCESS, sts); return; } + ASSERT_EQ(pst_report_buffer_.size(), length); ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector computed_signature(SHA_DIGEST_LENGTH); unsigned int sig_len = SHA_DIGEST_LENGTH; @@ -857,23 +897,6 @@ void Session::GenerateReport(const std::string& pst, bool expect_success, EXPECT_EQ(0, memcmp(pst.c_str(), pst_report().pst(), pst.length())); } -void Session::DeleteEntry(const std::string& pst) { - uint8_t* pst_ptr = encrypted_license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(license_.pst), pst.length())); - ServerSignBuffer(reinterpret_cast(&padded_message_), - message_size_, &signature_); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(session_id(), pst_ptr, pst.length(), - message_ptr(), message_size_, - &signature_[0], signature_.size())); -} - -void Session::ForceDeleteEntry(const std::string& pst) { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_ForceDeleteUsageEntry( - reinterpret_cast(pst.c_str()), pst.length())); -} - const uint8_t* Session::message_ptr() { return reinterpret_cast(&encrypted_license()); } diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index 285bd64a..29de6223 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -248,6 +248,29 @@ class Session { // Loads the specified wrapped_rsa_key into OEMCrypto, and then runs // GenerateDerivedKeysFromSessionKey to install known encryption and mac keys. void InstallRSASessionTestKey(const vector& wrapped_rsa_key); + // Creates a new usage entry, and keeps track of the index. + void CreateNewUsageEntry(); + // Copy encrypted usage entry from other session, and then load it. + // This session must already be open. + void LoadUsageEntry(uint32_t index, const vector& buffer); + // Copy encrypted usage entry from other session. + // This session must already be open. + void LoadUsageEntry(const Session& other) { + LoadUsageEntry(other.usage_entry_number(), other.encrypted_usage_entry()); + } + // Reload previously used usage entry. + void ReloadUsageEntry() { LoadUsageEntry(*this); } + // Update the usage entry and save the header to the specified buffer. + void UpdateUsageEntry(std::vector* header_buffer); + // Deactivate this sessions usage entry. + void DeactivateUsageEntry(const std::string& pst, + OEMCryptoResult expect_result = OEMCrypto_SUCCESS); + // The usage entry number for this session's usage entry. + uint32_t usage_entry_number() const { return usage_entry_number_; } + // The encrypted buffer holding the recently updated and saved usage entry. + const vector& encrypted_usage_entry() const { + return encrypted_usage_entry_; + } // Generates a usage report for the specified pst. If expect_success is true, // the report's signature is verified, and several fields are given sanity // checks. If other is not null, then the mac keys are copied from other in @@ -259,12 +282,6 @@ class Session { wvcdm::Unpacked_PST_Report pst_report() { return wvcdm::Unpacked_PST_Report(&pst_report_buffer_[0]); } - // Creates a signed delete usage table entry message and calls - // OEMCrypto_DeleteUsageEntry on it. - void DeleteEntry(const std::string& pst); - // Calls OEMCrypto_ForceDeleteUsageEntry to delete a usage table entry without - // a signed message. - void ForceDeleteEntry(const std::string& pst); // The unencrypted license response or license renewal response. MessageData& license() { return license_; } @@ -310,6 +327,8 @@ class Session { OEMCrypto_KeyObject key_array_[kMaxNumKeys]; std::vector signature_; int num_keys_; + vector encrypted_usage_entry_; + uint32_t usage_entry_number_; }; } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index fbf06efb..2dd4feca 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -610,7 +610,19 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { virtual void SetUp() { OEMCryptoClientTest::SetUp(); EnsureTestKeys(); - if (global_features.usage_table) OEMCrypto_DeleteOldUsageTable(); + if (global_features.usage_table) { + CreateUsageTableHeader(); + } + } + + void CreateUsageTableHeader() { + size_t header_buffer_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_CreateUsageTableHeader(NULL, &header_buffer_length)); + encrypted_usage_header_.resize(header_buffer_length); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_CreateUsageTableHeader(&encrypted_usage_header_[0], + &header_buffer_length)); } void EnsureTestKeys() { @@ -734,6 +746,7 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); } + vector encrypted_usage_header_; std::vector encoded_rsa_key_; std::vector wrapped_rsa_key_; }; @@ -1212,6 +1225,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeysWithNoDerivedKeys) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +// TODO(fredgc): also add shared license bit. // To prevent initial loading shared licenses without usage table or nonce, // LoadKeys should reject an empty list of keys. TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { @@ -4352,9 +4366,10 @@ TEST_F(GenericCryptoKeyIdLengthTest, UniformLongKeyId) { TestWithKey(2); } -TEST_F(OEMCryptoClientTest, UpdateUsageTableTest) { - EXPECT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); -} +// TODO(fredgc): write a new test for this. -- save reload, add skew error. +// TEST_F(OEMCryptoClientTest, UpdateUsageTableTest) { +// EXPECT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); +// } class UsageTableTest : public GenericCryptoTest { public: @@ -4363,22 +4378,15 @@ class UsageTableTest : public GenericCryptoTest { new_mac_keys_ = true; } - void DeactivatePST(const std::string& pst) { - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_DeactivateUsageEntry( - 0, reinterpret_cast(pst.c_str()), pst.length())); - } - void LoadOfflineLicense(Session& s, const std::string& pst) { ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); s.GenerateReport(pst); EXPECT_EQ(kUnused, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), @@ -4420,7 +4428,6 @@ class UsageTableTestWithMAC : public UsageTableTest, TEST_P(UsageTableTestWithMAC, OnlineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4428,9 +4435,9 @@ TEST_P(UsageTableTestWithMAC, OnlineLicense) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); s.GenerateReport(pst); s.GenerateReport(pst); // test repeated report generation s.GenerateReport(pst); @@ -4439,24 +4446,28 @@ TEST_P(UsageTableTestWithMAC, OnlineLicense) { EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kTimeTolerance); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); s.GenerateReport(pst); EXPECT_EQ(kActive, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kTimeTolerance); EXPECT_NEAR(0, s.pst_report().seconds_since_first_decrypt(), kTimeTolerance); EXPECT_NEAR(0, s.pst_report().seconds_since_last_decrypt(), kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); + // Flag the entry as inactive. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); + // It should report as inactive. EXPECT_EQ(kInactiveUsed, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kTimeTolerance); + // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4464,6 +4475,7 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); time_t loaded = time(NULL); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); @@ -4475,7 +4487,8 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { kAllKeys, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), OEMCrypto_SUCCESS)); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); time_t report_generated = time(NULL); EXPECT_EQ(kActive, s.pst_report().status()); // license received at LoadKeys, not at RefreshKeys. @@ -4490,7 +4503,6 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { TEST_F(UsageTableTest, RepeatOnlineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4498,13 +4510,15 @@ TEST_F(UsageTableTest, RepeatOnlineLicense) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); uint8_t* pst_ptr = s.encrypted_license().pst; + s2.LoadUsageEntry(s); // Use the same entry. // Trying to reuse a PST is bad. We use session ID for s2, everything else // reused from s. ASSERT_NE( @@ -4519,7 +4533,6 @@ TEST_F(UsageTableTest, RepeatOnlineLicense) { // A license with non-zero replay control bits needs a valid pst.. TEST_F(UsageTableTest, OnlineEmptyPST) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4527,6 +4540,7 @@ TEST_F(UsageTableTest, OnlineEmptyPST) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, @@ -4536,8 +4550,27 @@ TEST_F(UsageTableTest, OnlineEmptyPST) { ASSERT_NO_FATAL_FAILURE(s.close()); } +// A license with non-zero replay control bits needs a valid pst.. +TEST_F(UsageTableTest, OnlineMissingEntry) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + // ENTRY NOT CREATED: ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), + s.encrypted_license().pst, pst.length(), NULL); + ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + TEST_F(UsageTableTest, EmptyTable) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4546,337 +4579,27 @@ TEST_F(UsageTableTest, EmptyTable) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); ASSERT_NO_FATAL_FAILURE(s.close()); OEMCrypto_DeleteOldUsageTable(); Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, false); + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); + ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(pst, false)); ASSERT_NO_FATAL_FAILURE(s2.close()); } -TEST_F(UsageTableTest, FiftyEntries) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s1; - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); - std::string pst1 = "pst saved"; - ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s1.get_nonce(), pst1)); - ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); - sleep(kShortSleep); - - const size_t ENTRY_COUNT = 49; // API says should hold at least 50 entries. - vector sessions(ENTRY_COUNT); - for (size_t i = 0; i < ENTRY_COUNT; i++) { - ASSERT_NO_FATAL_FAILURE(sessions[i].open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); - std::string pst = "pst "; - char c = 'A' + i; - pst = pst + c; - ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - sessions[i].get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)); - sessions[i].GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(sessions[i].close()); - } - for (size_t i = 0; i < ENTRY_COUNT; i++) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - std::string pst = "pst "; - char c = 'A' + i; - pst = pst + c; - s.GenerateReport(pst, true, &sessions[i]); - EXPECT_EQ(kUnused, s.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s.close()); - } - sleep(kShortSleep); - // If I add too many entries, it can delete the older ones first, except - // it shouldn't delete the one attached to an open session. (s1) - for (size_t i = 0; i < ENTRY_COUNT; i++) { - ASSERT_NO_FATAL_FAILURE(sessions[i].open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); - std::string pst = "newer pst "; - char c = 'A' + i; - pst = pst + c; - ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - sessions[i].get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)); - sessions[i].GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(sessions[i].close()); - } - for (int i = 0; i < 49; i++) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - std::string pst = "newer pst "; - char c = 'A' + i; - pst = pst + c; - s.GenerateReport(pst, true, &sessions[i]); - EXPECT_EQ(kUnused, s.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s.close()); - } - ASSERT_NO_FATAL_FAILURE(s1.close()); - ASSERT_NO_FATAL_FAILURE( - s1.open()); // Make sure s1's entry is still in the table. - s1.GenerateReport(pst1); - EXPECT_EQ(kUnused, s1.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s1.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteUnusedEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s2.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, false); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteActiveEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(pst, true, &s)); - EXPECT_EQ(kActive, s2.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, ForceDeleteActiveEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); - ASSERT_NO_FATAL_FAILURE(s.close()); - - ASSERT_NO_FATAL_FAILURE(s.ForceDeleteEntry(pst)); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteInactiveEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - EXPECT_EQ(kInactiveUsed, s2.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, false); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteEntryBadSignature) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - uint8_t* pst_ptr = s2.encrypted_license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(s2.license().pst), pst.length())); - vector signature(SHA256_DIGEST_LENGTH); - // Cannot delete without correct signature. - // ServerSignMessage(s2.encrypted_license(), &signature); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(s2.session_id(), pst_ptr, pst.length(), - s2.message_ptr(), s2.message_size(), - &signature[0], signature.size())); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // The session is not deleted, we can still generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s3.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteEntryWrongSession) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should not be able to delete without using GenerateReport - // to load mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - // s2.GenerateReport(pst, true, &s); - uint8_t* pst_ptr = s2.encrypted_license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(s2.license().pst), pst.length())); - std::vector signature(SHA256_DIGEST_LENGTH); - s2.ServerSignBuffer(reinterpret_cast(&s2.encrypted_license()), - sizeof(s2.encrypted_license()), &signature); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(s2.session_id(), pst_ptr, pst.length(), - s2.message_ptr(), s2.message_size(), - &signature[0], signature.size())); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // The session is not deleted, we can still generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s3.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteEntryBadRange) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should not be able to delete if pst doesn't point into - // message. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - uint8_t* pst_ptr = s2.license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(s2.license().pst), pst.length())); - std::vector signature(SHA256_DIGEST_LENGTH); - s2.ServerSignBuffer(reinterpret_cast(&s2.encrypted_license()), - sizeof(s2.encrypted_license()), &signature); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(s2.session_id(), pst_ptr, pst.length(), - s2.message_ptr(), s2.message_size(), - &signature[0], signature.size())); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // The session is not deleted, we can still generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s3.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeactivateBadPST) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - std::string pst = "nonexistant pst"; - OEMCryptoResult sts = OEMCrypto_DeactivateUsageEntry( - 0, reinterpret_cast(pst.c_str()), pst.length()); - EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); - std::string null_pst = ""; - sts = OEMCrypto_DeactivateUsageEntry( - 0, reinterpret_cast(null_pst.c_str()), null_pst.length()); - EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); -} - TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { std::string pst = "A PST"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; unsigned int key_index = 0; @@ -4892,7 +4615,8 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0]); ASSERT_EQ(OEMCrypto_SUCCESS, sts); EXPECT_EQ(expected_encrypted, encrypted); - session_.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kActive, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), kTimeTolerance); @@ -4900,7 +4624,8 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { kTimeTolerance); EXPECT_NEAR(0, session_.pst_report().seconds_since_last_decrypt(), kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kInactiveUsed, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), @@ -4915,12 +4640,12 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; unsigned int key_index = 1; @@ -4936,7 +4661,8 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0]); ASSERT_EQ(OEMCrypto_SUCCESS, sts); EXPECT_EQ(clear_buffer_, resultant); - session_.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kActive, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), kTimeTolerance); @@ -4944,8 +4670,9 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { kTimeTolerance); EXPECT_NEAR(0, session_.pst_report().seconds_since_last_decrypt(), kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - session_.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kInactiveUsed, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), kTimeTolerance); @@ -4959,12 +4686,12 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; @@ -4989,7 +4716,8 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(expected_signature, signature); - session_.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kActive, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), kTimeTolerance); @@ -4997,7 +4725,8 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { kTimeTolerance); EXPECT_NEAR(0, session_.pst_report().seconds_since_last_decrypt(), kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kInactiveUsed, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), @@ -5014,12 +4743,12 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; @@ -5035,8 +4764,8 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { clear_buffer_.size(), OEMCrypto_HMAC_SHA256, &signature[0], signature.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - session_.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kActive, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), kTimeTolerance); @@ -5044,7 +4773,8 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { kTimeTolerance); EXPECT_NEAR(0, session_.pst_report().seconds_since_last_decrypt(), kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); EXPECT_EQ(kInactiveUsed, session_.pst_report().status()); EXPECT_NEAR(0, session_.pst_report().seconds_since_license_received(), @@ -5058,20 +4788,19 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { TEST_P(UsageTableTestWithMAC, OfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); } TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); time_t loaded = time(NULL); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); @@ -5081,7 +4810,8 @@ TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { size_t kAllKeys = 1; ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( kAllKeys, wvoec_mock::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); time_t report_generated = time(NULL); EXPECT_EQ(kActive, s.pst_report().status()); // license received at LoadKeys, not at RefreshKeys. @@ -5096,7 +4826,6 @@ TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5104,14 +4833,16 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kUnused, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kTimeTolerance); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kActive, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kTimeTolerance); @@ -5122,7 +4853,6 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); time_t loaded = time(NULL); @@ -5131,15 +4861,17 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kUnused, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kTimeTolerance); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); time_t decrypt_time = time(NULL); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); time_t report_generated = time(NULL); EXPECT_EQ(kActive, s.pst_report().status()); // license received at first LoadKeys. @@ -5153,7 +4885,8 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { size_t kAllKeys = 1; ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( kAllKeys, wvoec_mock::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); report_generated = time(NULL); EXPECT_EQ(kActive, s.pst_report().status()); // license received at LoadKeys, not at RefreshKeys. @@ -5169,7 +4902,6 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5180,6 +4912,7 @@ TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s2.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); uint8_t* pst_ptr = s2.encrypted_license().pst; ASSERT_NE( OEMCrypto_SUCCESS, @@ -5193,18 +4926,20 @@ TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { // Offline license with same mac keys should still be OK. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kUnused, s.pst_report().status()); } // An offline license should not load on the first call if the nonce is bad. TEST_P(UsageTableTestWithMAC, OfflineBadNonce) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry, 42, pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -5220,10 +4955,10 @@ TEST_P(UsageTableTestWithMAC, OfflineBadNonce) { // An offline license needs a valid pst. TEST_P(UsageTableTestWithMAC, OfflineEmptyPST) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry, s.get_nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -5238,20 +4973,21 @@ TEST_P(UsageTableTestWithMAC, OfflineEmptyPST) { TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kInactiveUsed, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kTimeTolerance); @@ -5260,6 +4996,7 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); // Offile license can not be reused if it has been deactivated. uint8_t* pst_ptr = s.encrypted_license().pst; EXPECT_NE( @@ -5269,19 +5006,22 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { s.encrypted_license().mac_key_iv, s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), pst_ptr, pst.length(), NULL)); + s2.close(); // But we can still generate a report. Session s3; ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); + ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, true, &s)); EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } TEST_P(UsageTableTestWithMAC, BadRange) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -5296,7 +5036,6 @@ TEST_P(UsageTableTestWithMAC, BadRange) { } TEST_F(UsageTableTest, TimingTest) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); std::string pst1 = "my_pst_1"; std::string pst2 = "my_pst_2"; std::string pst3 = "my_pst_3"; @@ -5313,12 +5052,14 @@ TEST_F(UsageTableTest, TimingTest) { sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); time_t first_decrypt1 = time(NULL); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); time_t first_decrypt2 = time(NULL); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); @@ -5329,7 +5070,9 @@ TEST_F(UsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); sleep(kLongSleep); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst1)); + ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(pst1)); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); ASSERT_NO_FATAL_FAILURE(s2.close()); @@ -5341,6 +5084,9 @@ TEST_F(UsageTableTest, TimingTest) { sleep(kShortSleep); OEMCrypto_Initialize(); EnsureTestKeys(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&encrypted_usage_header_[0], + encrypted_usage_header_.size())); // Test teardown expects session_ to be open. ASSERT_NO_FATAL_FAILURE(session_.open()); @@ -5349,20 +5095,28 @@ TEST_F(UsageTableTest, TimingTest) { time_t third_decrypt = time(NULL); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.close()); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s3.ReloadUsageEntry()); sleep(kLongSleep); time_t report_generated1 = time(NULL); - s1.GenerateReport(pst1); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s1.GenerateReport(pst1)); time_t report_generated2 = time(NULL); - s2.GenerateReport(pst2); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(pst2)); time_t report_generated3 = time(NULL); - s3.GenerateReport(pst3); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst3)); EXPECT_EQ(kInactiveUsed, s1.pst_report().status()); EXPECT_NEAR(report_generated1 - loaded1, @@ -5388,7 +5142,6 @@ TEST_F(UsageTableTest, TimingTest) { TEST_F(UsageTableTest, VerifyUsageTimes) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -5396,11 +5149,12 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); const int kLicenseReceivedTimeTolerance = kSpeedMultiplier; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kUnused, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), kLicenseReceivedTimeTolerance); @@ -5420,8 +5174,8 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kUnused, s.pst_report().status()); EXPECT_NEAR(s.pst_report().seconds_since_license_received(), kIdleInSeconds, kLicenseReceivedTimeTolerance); @@ -5432,7 +5186,8 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { time_t start_time = time(NULL); do { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kActive, s.pst_report().status()); playback_time = time(NULL) - start_time; ASSERT_LE(0, playback_time); @@ -5444,8 +5199,8 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { } while (playback_time < kPlaybackLoopInSeconds); cout << "\nSimulated playback time = " << playback_time << " seconds.\n"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_NEAR(s.pst_report().seconds_since_license_received(), playback_time + kIdleInSeconds, kLicenseReceivedTimeTolerance); EXPECT_NEAR(s.pst_report().seconds_since_first_decrypt(), playback_time, @@ -5468,8 +5223,8 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { // |<--->| = seconds_since_last_decrypt // |<----------------------------->| = seconds_since_first_decrypt // |<------------------------------------| = seconds_since_license_received - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_NEAR(s.pst_report().seconds_since_license_received(), playback_time + 2 * kIdleInSeconds, kLicenseReceivedTimeTolerance); @@ -5478,7 +5233,8 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { EXPECT_NEAR(s.pst_report().seconds_since_last_decrypt(), kIdleInSeconds, kUsageTableTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kInactiveUsed, s.pst_report().status()); ASSERT_NO_FATAL_FAILURE( @@ -5488,6 +5244,7 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { // This is a special case where a collection of licenses can be shared with // multiple devices. In order for this to work, a single session must first // load a device specific license, and then a shared content license. +#if 0 // TODO(fredgc,jfore): fix this in http://go/wvgerrit/23184/ TEST_F(UsageTableTest, LoadSharedLicense) { // session_.generatersasignature. // session_.GenerateNonce @@ -5496,15 +5253,15 @@ TEST_F(UsageTableTest, LoadSharedLicense) { // LoadKeys replay control = 0. uses same mac key. // check second loadkeys without first fails. std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, true)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - // The second set of keys are not loaded. + // The second set of keys are in the shared license. for (unsigned int i = 0; i < s.num_keys(); i++) { memset(s.license().keys[i].key_id, 'A' + i, s.license().keys[i].key_id_length); @@ -5512,26 +5269,28 @@ TEST_F(UsageTableTest, LoadSharedLicense) { // TODO(fredgc,jfore): Decide if first set of keys need to stay loaded, or if // they are replaced. ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, false)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.close()); } +#endif TEST_F(UsageTableTest, PSTLargeBuffer) { std::string pst(kMaxPSTLength, 'a'); // A large PST. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); EXPECT_EQ(kInactiveUsed, s.pst_report().status()); EXPECT_NEAR(0, s.pst_report().seconds_since_license_received(), @@ -5543,6 +5302,7 @@ TEST_F(UsageTableTest, PSTLargeBuffer) { ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); // Offile license can not be reused if it has been deactivated. uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); EXPECT_NE( OEMCrypto_SUCCESS, OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), @@ -5550,67 +5310,16 @@ TEST_F(UsageTableTest, PSTLargeBuffer) { s.encrypted_license().mac_key_iv, s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), pst_ptr, pst.length(), NULL)); + s2.close(); // But we can still generate a report. Session s3; ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, true, &s)); EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } -TEST_F(UsageTableTest, DeleteEntryLargeBuffer) { - std::string pst(kMaxPSTLength, 'a'); // A large PST. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(pst, true, &s)); - EXPECT_EQ(kActive, s2.pst_report().status()); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_F(UsageTableTest, ForceDeleteLargeBuffer) { - std::string pst(kMaxPSTLength, 'a'); // A large PST. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); - ASSERT_NO_FATAL_FAILURE(s.close()); - - ASSERT_NO_FATAL_FAILURE(s.ForceDeleteEntry(pst)); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - INSTANTIATE_TEST_CASE_P(TestUsageTables, UsageTableTestWithMAC, Values(true, false)); // With and without new_mac_keys. } // namespace wvoec