Reference Code for Big Usage Tables

Merge from widevine of http://go/wvgerrit/23283

This CL adds some big usage table functionality to the oemcrypto
mock and unit tests.

Still missing are: backwards compatibility, defragging the table,
haystack code, and lots of new unit tests.

The haystack now reports it doesn't support usage tables, so that
the unit tests will pass.  This will be fixed in a future CL.

b/31458046
b/32554171
b/34173776
b/34174907

Change-Id: I6e08e76f7612ffb77e413151e00f830339298c62
This commit is contained in:
Fred Gylys-Colwell
2017-01-25 18:56:01 -08:00
parent 1c5b4175aa
commit 3d977d999c
9 changed files with 1259 additions and 1061 deletions

View File

@@ -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<uint8_t>& 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<uint8_t> key_id;
std::vector<uint8_t> enc_key_data;
std::vector<uint8_t> key_data_iv;
std::vector<uint8_t> key_control;
std::vector<uint8_t> key_control_iv;
std::vector<uint8_t> pstv;
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<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv,
const std::vector<uint8_t>& pst,
bool ctr_mode) {
OEMCryptoResult SessionContext::InstallKey(
const KeyId& key_id, const std::vector<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,
const std::vector<uint8_t>& key_control,
const std::vector<uint8_t>& key_control_iv, bool ctr_mode) {
// Decrypt encrypted key_data using derived encryption key and offered iv
std::vector<uint8_t> content_key;
std::vector<uint8_t> 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<uint8_t>& 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<uint8_t>& pst) {
if (!usage_entry_) return OEMCrypto_ERROR_INVALID_CONTEXT;
usage_entry_->Deactivate(pst);
return OEMCrypto_SUCCESS;
}
OEMCryptoResult SessionContext::ReportUsage(const std::vector<uint8_t>& 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<uint8_t>& 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<uint8_t>& content_key = current_content_key()->value();