Add Entitlement License to OEMCrypto
This CL adds entitlement license features and moves cipher mode from LoadKeys to SelectKeys. Merge from Widevine repo of http://go/wvgerrit/41660 bug: 70334840 Entitlement License - cdm layer bug: 70334345 Entitlement License - reference code and unit tests test: Entitlement license unit tests pass. Change-Id: Ic7d7f42c15e6d83ef7fcfd8a866c778adc4c8095
This commit is contained in:
@@ -56,11 +56,124 @@ void dump_boringssl_error() {
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
/***************************************/
|
||||
|
||||
class ContentKeysContext : public SessionContextKeys {
|
||||
public:
|
||||
explicit ContentKeysContext() {}
|
||||
virtual ~ContentKeysContext() {}
|
||||
virtual size_t size() { return session_keys_.size(); }
|
||||
bool Insert(const KeyId& key_id, const Key& key_data);
|
||||
virtual Key* Find(const KeyId& key_id);
|
||||
virtual void Remove(const KeyId& key_id);
|
||||
virtual void UpdateDuration(const KeyControlBlock& control);
|
||||
|
||||
virtual OEMCrypto_LicenseType type() { return OEMCrypto_ContentLicense; }
|
||||
|
||||
virtual bool SetContentKey(const KeyId& entitlement_id,
|
||||
const KeyId& content_key_id,
|
||||
const std::vector<uint8_t>& content_key);
|
||||
virtual bool GetEntitlementKey(
|
||||
const KeyId& entitlement_id,
|
||||
const std::vector<uint8_t>** entitlement_key);
|
||||
|
||||
private:
|
||||
SessionKeyTable session_keys_;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(ContentKeysContext);
|
||||
};
|
||||
|
||||
bool ContentKeysContext::Insert(const KeyId& key_id, const Key& key_data) {
|
||||
return session_keys_.Insert(key_id, key_data);
|
||||
}
|
||||
|
||||
Key* ContentKeysContext::Find(const KeyId& key_id) {
|
||||
return session_keys_.Find(key_id);
|
||||
}
|
||||
|
||||
void ContentKeysContext::Remove(const KeyId& key_id) {
|
||||
session_keys_.Remove(key_id);
|
||||
}
|
||||
|
||||
void ContentKeysContext::UpdateDuration(const KeyControlBlock& control) {
|
||||
session_keys_.UpdateDuration(control);
|
||||
}
|
||||
|
||||
bool ContentKeysContext::SetContentKey(
|
||||
const KeyId& entitlement_id, const KeyId& content_key_id,
|
||||
const std::vector<uint8_t>& content_key) {
|
||||
// Unsupported action for this type.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContentKeysContext::GetEntitlementKey(
|
||||
const KeyId& entitlement_id, const std::vector<uint8_t>** key) {
|
||||
// Unsupported action for this type.
|
||||
return false;
|
||||
};
|
||||
|
||||
/***************************************/
|
||||
|
||||
class EntitlementKeysContext : public SessionContextKeys {
|
||||
public:
|
||||
EntitlementKeysContext() {}
|
||||
virtual ~EntitlementKeysContext() {}
|
||||
virtual size_t size() { return session_keys_.size(); }
|
||||
bool Insert(const KeyId& key_id, const Key& key_data);
|
||||
virtual Key* Find(const KeyId& key_id);
|
||||
virtual void Remove(const KeyId& key_id);
|
||||
virtual void UpdateDuration(const KeyControlBlock& control);
|
||||
virtual bool SetContentKey(const KeyId& entitlement_id,
|
||||
const KeyId& content_key_id,
|
||||
const std::vector<uint8_t>& content_key);
|
||||
virtual bool GetEntitlementKey(const KeyId& entitlement_id,
|
||||
const std::vector<uint8_t>** key);
|
||||
|
||||
virtual OEMCrypto_LicenseType type() { return OEMCrypto_EntitlementLicense; }
|
||||
|
||||
private:
|
||||
EntitlementKeyTable session_keys_;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(EntitlementKeysContext);
|
||||
};
|
||||
|
||||
bool EntitlementKeysContext::Insert(const KeyId& key_id, const Key& key_data) {
|
||||
return session_keys_.Insert(key_id, key_data);
|
||||
}
|
||||
|
||||
Key* EntitlementKeysContext::Find(const KeyId& key_id) {
|
||||
return session_keys_.Find(key_id);
|
||||
}
|
||||
|
||||
void EntitlementKeysContext::Remove(const KeyId& key_id) {
|
||||
session_keys_.Remove(key_id);
|
||||
}
|
||||
|
||||
void EntitlementKeysContext::UpdateDuration(const KeyControlBlock& control) {
|
||||
session_keys_.UpdateDuration(control);
|
||||
}
|
||||
|
||||
bool EntitlementKeysContext::SetContentKey(
|
||||
const KeyId& entitlement_id, const KeyId& content_key_id,
|
||||
const std::vector<uint8_t>& content_key) {
|
||||
return session_keys_.SetContentKey(entitlement_id, content_key_id,
|
||||
content_key);
|
||||
}
|
||||
|
||||
bool EntitlementKeysContext::GetEntitlementKey(
|
||||
const KeyId& entitlement_id, const std::vector<uint8_t>** out_key) {
|
||||
return session_keys_.GetEntitlementKey(entitlement_id, out_key);
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
|
||||
SessionContext::~SessionContext() {
|
||||
if (usage_entry_) {
|
||||
delete usage_entry_;
|
||||
usage_entry_ = NULL;
|
||||
}
|
||||
if (session_keys_) {
|
||||
delete session_keys_;
|
||||
session_keys_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
@@ -398,7 +511,7 @@ OEMCryptoResult SessionContext::CheckNonceOrEntry(
|
||||
return CheckStatusOnline(key_control_block.nonce(),
|
||||
key_control_block.control_bits());
|
||||
break;
|
||||
case kControlNonceOrEntry: // Offline license. Nonce required on first use.
|
||||
case kControlNonceOrEntry: // Offline license. Nonce required on first use.
|
||||
return CheckStatusOffline(key_control_block.nonce(),
|
||||
key_control_block.control_bits());
|
||||
break;
|
||||
@@ -423,13 +536,32 @@ OEMCryptoResult SessionContext::LoadKeys(
|
||||
const uint8_t* message, size_t message_length, const uint8_t* signature,
|
||||
size_t signature_length, const uint8_t* enc_mac_key_iv,
|
||||
const uint8_t* enc_mac_keys, size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
|
||||
size_t pst_length, const uint8_t* srm_requirement) {
|
||||
const OEMCrypto_KeyObject* key_array, const uint8_t* pst, size_t pst_length,
|
||||
const uint8_t* srm_requirement, OEMCrypto_LicenseType license_type) {
|
||||
// Validate message signature
|
||||
if (!ValidateMessage(message, message_length, signature, signature_length)) {
|
||||
return OEMCrypto_ERROR_SIGNATURE_FAILURE;
|
||||
}
|
||||
|
||||
if (!session_keys_) {
|
||||
switch (license_type) {
|
||||
case OEMCrypto_ContentLicense:
|
||||
session_keys_ = new ContentKeysContext();
|
||||
break;
|
||||
|
||||
case OEMCrypto_EntitlementLicense:
|
||||
session_keys_ = new EntitlementKeysContext();
|
||||
break;
|
||||
|
||||
default:
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
} else {
|
||||
if (session_keys_->type() != license_type) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
}
|
||||
|
||||
StartTimer();
|
||||
|
||||
if (srm_requirement) {
|
||||
@@ -454,15 +586,15 @@ OEMCryptoResult SessionContext::LoadKeys(
|
||||
LOGW("[LoadKeys: SRM blacklisted device attached]");
|
||||
srm_requirements_status_ = InvalidSRMVersion;
|
||||
} else {
|
||||
LOGI("[LoadKeys: SRM Versions is %d, required: %d]",
|
||||
current_version, minimum_version);
|
||||
LOGI("[LoadKeys: SRM Versions is %d, required: %d]", current_version,
|
||||
minimum_version);
|
||||
srm_requirements_status_ = ValidSRMVersion;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are already keys installed in this session, then we can load
|
||||
// a shared license.
|
||||
bool second_license = (session_keys_.size() > 0);
|
||||
bool second_license = (session_keys_->size() > 0);
|
||||
|
||||
// Decrypt and install keys in key object
|
||||
// Each key will have a key control block. They will all have the same nonce.
|
||||
@@ -490,7 +622,7 @@ OEMCryptoResult SessionContext::LoadKeys(
|
||||
|
||||
OEMCryptoResult result = InstallKey(
|
||||
key_id, enc_key_data, key_data_iv, key_control, key_control_iv,
|
||||
key_array[i].cipher_mode == OEMCrypto_CipherMode_CTR, second_license);
|
||||
second_license);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
status = result;
|
||||
break;
|
||||
@@ -550,11 +682,57 @@ OEMCryptoResult SessionContext::LoadKeys(
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::LoadEntitledContentKeys(
|
||||
size_t num_keys,
|
||||
const OEMCrypto_EntitledContentKeyObject* key_array) {
|
||||
if (!key_array) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!session_keys_ || session_keys_->type() != OEMCrypto_EntitlementLicense) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
const OEMCrypto_EntitledContentKeyObject* key_data = &key_array[i];
|
||||
std::vector<uint8_t> entitlement_key_id;
|
||||
entitlement_key_id.assign(key_data->entitlement_key_id,
|
||||
key_data->entitlement_key_id +
|
||||
key_data->entitlement_key_id_length);
|
||||
|
||||
const std::vector<uint8_t>* entitlement_key = NULL;
|
||||
if (!session_keys_->GetEntitlementKey(entitlement_key_id,
|
||||
&entitlement_key)) {
|
||||
return OEMCrypto_KEY_NOT_ENTITLED;
|
||||
}
|
||||
std::vector<uint8_t> content_key;
|
||||
std::vector<uint8_t> iv;
|
||||
std::vector<uint8_t> encrypted_content_key;
|
||||
std::vector<uint8_t> content_key_id;
|
||||
|
||||
iv.assign(key_data->content_key_data_iv,
|
||||
key_data->content_key_data_iv + 16);
|
||||
encrypted_content_key.assign(
|
||||
key_data->content_key_data,
|
||||
key_data->content_key_data + key_data->content_key_data_length);
|
||||
content_key_id.assign(
|
||||
key_data->content_key_id,
|
||||
key_data->content_key_id + key_data->content_key_id_length);
|
||||
if (!DecryptEntitlement(*entitlement_key, iv,
|
||||
encrypted_content_key, &content_key)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!session_keys_->SetContentKey(
|
||||
entitlement_key_id, content_key_id, content_key)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
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,
|
||||
const std::vector<uint8_t>& key_control_iv,
|
||||
bool second_license) {
|
||||
// Decrypt encrypted key_data using derived encryption key and offered iv
|
||||
std::vector<uint8_t> content_key;
|
||||
@@ -566,12 +744,11 @@ OEMCryptoResult SessionContext::InstallKey(
|
||||
}
|
||||
|
||||
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
|
||||
@@ -633,8 +810,11 @@ OEMCryptoResult SessionContext::InstallKey(
|
||||
}
|
||||
}
|
||||
|
||||
Key key(content_key, key_control_block, ctr_mode);
|
||||
session_keys_.Insert(key_id, key);
|
||||
Key key(content_key, key_control_block);
|
||||
if (!session_keys_) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
session_keys_->Insert(key_id, key);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -663,6 +843,9 @@ bool SessionContext::InstallRSAEncryptedKey(
|
||||
OEMCryptoResult SessionContext::RefreshKey(
|
||||
const KeyId& key_id, const std::vector<uint8_t>& key_control,
|
||||
const std::vector<uint8_t>& key_control_iv) {
|
||||
if (!session_keys_) {
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
if (key_id.empty()) {
|
||||
// Key control is not encrypted if key id is NULL
|
||||
KeyControlBlock key_control_block(key_control);
|
||||
@@ -676,11 +859,11 @@ OEMCryptoResult SessionContext::RefreshKey(
|
||||
return OEMCrypto_ERROR_INVALID_NONCE;
|
||||
}
|
||||
// Apply duration to all keys in this session
|
||||
session_keys_.UpdateDuration(key_control_block);
|
||||
session_keys_->UpdateDuration(key_control_block);
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
Key* content_key = session_keys_.Find(key_id);
|
||||
Key* content_key = session_keys_->Find(key_id);
|
||||
|
||||
if (NULL == content_key) {
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
@@ -1014,7 +1197,10 @@ bool SessionContext::UpdateMacKeys(const std::vector<uint8_t>& enc_mac_keys,
|
||||
}
|
||||
|
||||
bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) {
|
||||
const Key* content_key = session_keys_.Find(key_id);
|
||||
if (!session_keys_) {
|
||||
return false;
|
||||
}
|
||||
const Key* content_key = session_keys_->Find(key_id);
|
||||
if (LogCategoryEnabled(kLoggingTraceDecryption)) {
|
||||
LOGI(("Select Key: key_id = " + wvcdm::b2a_hex(key_id)).c_str());
|
||||
if (content_key) {
|
||||
@@ -1035,25 +1221,34 @@ bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::SelectContentKey(const KeyId& key_id) {
|
||||
const Key* content_key = session_keys_.Find(key_id);
|
||||
|
||||
OEMCryptoResult SessionContext::SelectContentKey(
|
||||
const KeyId& key_id, OEMCryptoCipherMode cipher_mode) {
|
||||
if (LogCategoryEnabled(kLoggingTraceDecryption)) {
|
||||
LOGI((" Select Key: key_id = " + wvcdm::b2a_hex(key_id)).c_str());
|
||||
LOGI((" Select Key: key = " + wvcdm::b2a_hex(content_key->value()))
|
||||
.c_str());
|
||||
LOGI(" Select Key: key_id = %s", wvcdm::b2a_hex(key_id).c_str());
|
||||
LOGI(" Select Key: cipher_mode = %s",
|
||||
(cipher_mode == OEMCrypto_CipherMode_CTR) ? "CTR" : "CBC");
|
||||
}
|
||||
|
||||
if (!session_keys_) {
|
||||
LOGE("Select Key: no session keys.");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
}
|
||||
Key* content_key = session_keys_->Find(key_id);
|
||||
if (NULL == content_key) {
|
||||
LOGE("[SelectContentKey(): No key matches key id]");
|
||||
return OEMCrypto_ERROR_NO_CONTENT_KEY;
|
||||
}
|
||||
if (LogCategoryEnabled(kLoggingTraceDecryption)) {
|
||||
LOGI((" Select Key: key = " + wvcdm::b2a_hex(content_key->value()))
|
||||
.c_str());
|
||||
}
|
||||
content_key->set_ctr_mode(cipher_mode == OEMCrypto_CipherMode_CTR);
|
||||
current_content_key_ = content_key;
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
|
||||
if (control.duration() > 0) {
|
||||
if (control.duration() < CurrentTimer()) {
|
||||
LOGE("[SelectContentKey(): KEY_EXPIRED %d versus %d]",
|
||||
control.duration(), CurrentTimer());
|
||||
LOGE("[SelectContentKey(): KEY_EXPIRED %d versus %d]", control.duration(),
|
||||
CurrentTimer());
|
||||
return OEMCrypto_ERROR_KEY_EXPIRED;
|
||||
}
|
||||
}
|
||||
@@ -1131,7 +1326,6 @@ OEMCryptoResult SessionContext::CopyOldUsageEntry(
|
||||
return usage_entry_->CopyOldUsageEntry(pst);
|
||||
}
|
||||
|
||||
|
||||
// Internal utility function to decrypt the message
|
||||
bool SessionContext::DecryptMessage(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
@@ -1151,6 +1345,25 @@ bool SessionContext::DecryptMessage(const std::vector<uint8_t>& key,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SessionContext::DecryptEntitlement(
|
||||
const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<uint8_t>& message,
|
||||
std::vector<uint8_t>* decrypted) {
|
||||
if (key.empty() || iv.empty() || message.empty() || !decrypted) {
|
||||
LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return false;
|
||||
}
|
||||
decrypted->resize(message.size());
|
||||
uint8_t iv_buffer[16];
|
||||
memcpy(iv_buffer, &iv[0], 16);
|
||||
AES_KEY aes_key;
|
||||
AES_set_decrypt_key(&key[0], 256, &aes_key);
|
||||
AES_cbc_encrypt(&message[0], &(decrypted->front()), message.size(), &aes_key,
|
||||
iv_buffer, AES_DECRYPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
OEMCryptoResult SessionContext::DecryptCENC(
|
||||
const uint8_t* iv, size_t block_offset,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern, const uint8_t* cipher_data,
|
||||
|
||||
Reference in New Issue
Block a user