// Copyright 2013 Google Inc. All Rights Reserved. // // Mock implementation of OEMCrypto APIs // #include "oemcrypto_usage_table_mock.h" #include #include #include #include #include #include #include #include #include "file_store.h" #include "log.h" #include "oemcrypto_engine_mock.h" #include "oemcrypto_logging.h" #include "oemcrypto_old_usage_table_mock.h" #include "properties.h" #include "pst_report.h" #include "string_conversions.h" #include "wv_cdm_constants.h" namespace wvoec_mock { 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]; 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 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() { 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; } 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(); } return true; } void UsageTableEntry::Deactivate(const std::vector& pst) { if (data_.status == kUnused) { data_.status = kInactiveUnused; } else if (data_.status == kActive) { data_.status = kInactiveUsed; } forbid_report_ = true; data_.generation_number++; usage_table_->IncrementGeneration(); } 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_WRONG_PST; } 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_WRONG_PST; } size_t length_needed = wvcdm::Unpacked_PST_Report::report_size(pst.size()); if (*buffer_length < length_needed) { *buffer_length = length_needed; return OEMCrypto_ERROR_SHORT_BUFFER; } if (!buffer) { LOGE("ReportUsage: buffer was null pointer."); return OEMCrypto_ERROR_INVALID_CONTEXT; } wvcdm::Unpacked_PST_Report pst_report(buffer); int64_t now = time(NULL); 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(data_.pst_length); memcpy(pst_report.pst(), data_.pst, data_.pst_length); unsigned int md_len = SHA_DIGEST_LENGTH; 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("ReportUsage: could not compute signature."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } return OEMCrypto_SUCCESS; } 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; } 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); // 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("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; } // 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; } OEMCryptoResult UsageTableEntry::CopyOldUsageEntry( const std::vector& pst) { OldUsageTableEntry* old_entry = usage_table_->FindOldUsageEntry(pst); if (!old_entry) return OEMCrypto_ERROR_WRONG_PST; data_.time_of_license_received = old_entry->time_of_license_received_; data_.time_of_first_decrypt = old_entry->time_of_first_decrypt_; data_.time_of_last_decrypt = old_entry->time_of_last_decrypt_; data_.status = old_entry->status_; if (old_entry->mac_key_server_.size() != wvcdm::MAC_KEY_SIZE) { LOGE("CopyOldEntry: Old entry has bad server mac key."); } else { memcpy(data_.mac_key_server, &(old_entry->mac_key_server_[0]), wvcdm::MAC_KEY_SIZE); } if (old_entry->mac_key_client_.size() != wvcdm::MAC_KEY_SIZE) { LOGE("CopyOldEntry: Old entry has bad client mac key."); } else { memcpy(data_.mac_key_client, &(old_entry->mac_key_client_[0]), wvcdm::MAC_KEY_SIZE); } if (pst.size() > kMaxPSTLength) { LOGE("CopyOldEntry: PST Length was too large. Truncating."); data_.pst_length = kMaxPSTLength; } else { data_.pst_length = pst.size(); } memcpy(data_.pst, &pst[0], wvcdm::MAC_KEY_SIZE); 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; } UsageTable::~UsageTable() { if (old_table_) { delete old_table_; old_table_ = NULL; } } 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_) { LOGE("CreateNewUsageEntry: Header not 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_) { LOGE("CreateNewUsageEntry: Header not 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()) { LOGE("OEMCrypto_ShrinkUsageTableHeader: %d > %zd.", 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; if (!header_buffer) { LOGE("OEMCrypto_ShrinkUsageTableHeader: buffer null."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } for (size_t i = new_table_size; i < sessions_.size(); ++i) { if (sessions_[i]) { LOGE("ShrinkUsageTableHeader: session open for %d", i); return OEMCrypto_ERROR_ENTRY_IN_USE; } } 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_ENTRY_IN_USE; } 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. if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3, &path)) { LOGE("UsageTable: Unable to get base path"); return false; } // 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::kCreate | wvcdm::FileSystem::kTruncate); if (!file) { LOGE("UsageTable: File open failed: %s", path.c_str()); return false; } file->Write(reinterpret_cast(&master_generation_number_), sizeof(int64_t)); file->Close(); return true; } 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; } // 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)); return true; } LOGE("UsageTable: File open failed: %s (clearing table)", path.c_str()); master_generation_number_ = 0; return false; } file->Read(reinterpret_cast(&master_generation_number_), sizeof(int64_t)); file->Close(); 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; // Make sure there are no entries that are currently tied to an open session. for (size_t i=0; i < sessions_.size(); ++i) { if (sessions_[i] != NULL) { LOGE("CreateUsageTableHeader: index %d used by session.", i); return OEMCrypto_ERROR_INVALID_SESSION; } } sessions_.clear(); generation_numbers_.clear(); header_loaded_ = true; return SaveUsageTableHeader(header_buffer, *header_buffer_length); } OldUsageTableEntry* UsageTable::FindOldUsageEntry( const std::vector& pst) { if (!old_table_) old_table_ = new OldUsageTable(ce_); return old_table_->FindEntry(pst); } OEMCryptoResult UsageTable::DeleteOldUsageTable() { if (old_table_) { old_table_->Clear(); delete old_table_; old_table_ = NULL; } OldUsageTable::DeleteFile(ce_); return OEMCrypto_SUCCESS; } OEMCryptoResult UsageTable::CreateOldUsageEntry( uint64_t time_since_license_received, uint64_t time_since_first_decrypt, uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status, uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst, size_t pst_length) { if (!old_table_) old_table_ = new OldUsageTable(ce_); std::vector pstv(pst, pst+pst_length); OldUsageTableEntry *old_entry = old_table_->CreateEntry(pstv); int64_t now = time(NULL); old_entry->time_of_license_received_ = now - time_since_license_received; old_entry->time_of_first_decrypt_ = now - time_since_first_decrypt; old_entry->time_of_last_decrypt_ = now - time_since_last_decrypt; old_entry->status_ = status; old_entry->mac_key_server_.assign(server_mac_key, server_mac_key + wvcdm::MAC_KEY_SIZE); old_entry->mac_key_client_.assign(client_mac_key, client_mac_key + wvcdm::MAC_KEY_SIZE); return OEMCrypto_SUCCESS; } } // namespace wvoec_mock