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:
@@ -227,7 +227,8 @@ void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) {
|
||||
&signature_[0], signature_.size(),
|
||||
encrypted_license().mac_key_iv,
|
||||
encrypted_license().mac_keys, num_keys_,
|
||||
key_array_, pst_ptr, pst.length(), NULL));
|
||||
key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_ContentLicense));
|
||||
// Update new generated keys.
|
||||
memcpy(&mac_key_server_[0], license_.mac_keys, wvcdm::MAC_KEY_SIZE);
|
||||
memcpy(&mac_key_client_[0], license_.mac_keys + wvcdm::MAC_KEY_SIZE,
|
||||
@@ -237,11 +238,113 @@ void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) {
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
&signature_[0], signature_.size(), NULL, NULL,
|
||||
num_keys_, key_array_, pst_ptr, pst.length(), NULL));
|
||||
num_keys_, key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_ContentLicense));
|
||||
}
|
||||
VerifyTestKeys();
|
||||
}
|
||||
|
||||
void Session::LoadEnitlementTestKeys(const std::string& pst,
|
||||
bool new_mac_keys,
|
||||
OEMCryptoResult expected_sts) {
|
||||
uint8_t* pst_ptr = NULL;
|
||||
if (pst.length() > 0) {
|
||||
pst_ptr = encrypted_license().pst;
|
||||
}
|
||||
if (new_mac_keys) {
|
||||
ASSERT_EQ(expected_sts,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
&signature_[0], signature_.size(),
|
||||
encrypted_license().mac_key_iv,
|
||||
encrypted_license().mac_keys, num_keys_,
|
||||
key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_EntitlementLicense));
|
||||
// Update new generated keys.
|
||||
memcpy(&mac_key_server_[0], license_.mac_keys, wvcdm::MAC_KEY_SIZE);
|
||||
memcpy(&mac_key_client_[0], license_.mac_keys + wvcdm::MAC_KEY_SIZE,
|
||||
wvcdm::MAC_KEY_SIZE);
|
||||
} else {
|
||||
ASSERT_EQ(
|
||||
expected_sts,
|
||||
OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_,
|
||||
&signature_[0], signature_.size(), NULL, NULL,
|
||||
num_keys_, key_array_, pst_ptr, pst.length(), NULL,
|
||||
OEMCrypto_EntitlementLicense));
|
||||
}
|
||||
}
|
||||
|
||||
void Session::FillEntitledKeyArray() {
|
||||
for (size_t i = 0; i < num_keys_; ++i) {
|
||||
EntitledContentKeyData* key_data = &entitled_key_data_[i];
|
||||
|
||||
entitled_key_array_[i].entitlement_key_id = key_array_[i].key_id;
|
||||
entitled_key_array_[i].entitlement_key_id_length =
|
||||
key_array_[i].key_id_length;
|
||||
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(key_data->content_key_id,
|
||||
sizeof(key_data->content_key_id)));
|
||||
entitled_key_array_[i].content_key_id = key_data->content_key_id;
|
||||
entitled_key_array_[i].content_key_id_length =
|
||||
sizeof(key_data->content_key_id);
|
||||
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(key_data->content_key_data,
|
||||
sizeof(key_data->content_key_data)));
|
||||
entitled_key_array_[i].content_key_data = key_data->content_key_data;
|
||||
entitled_key_array_[i].content_key_data_length =
|
||||
sizeof(key_data->content_key_data);
|
||||
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(entitled_key_data_[i].content_key_data_iv,
|
||||
sizeof(entitled_key_data_[i].content_key_data_iv)));
|
||||
entitled_key_array_[i].content_key_data_iv = key_data->content_key_data_iv;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::LoadEntitledContentKeys(OEMCryptoResult expected_sts) {
|
||||
// Create a copy of the stored |entitled_key_array_|.
|
||||
std::vector<OEMCrypto_EntitledContentKeyObject> encrypted_entitled_key_array;
|
||||
encrypted_entitled_key_array.resize(num_keys_);
|
||||
memcpy(&encrypted_entitled_key_array[0], &entitled_key_array_[0],
|
||||
sizeof(OEMCrypto_EntitledContentKeyObject) * num_keys_);
|
||||
|
||||
// Create a encrypted version of all of the content keys stored in
|
||||
// |entitled_key_array_|.
|
||||
std::vector<std::vector<uint8_t> > encrypted_content_keys;
|
||||
encrypted_content_keys.resize(num_keys_);
|
||||
|
||||
for (size_t i = 0; i < num_keys_; ++i) {
|
||||
// Load the entitlement key from |key_array_|.
|
||||
AES_KEY aes_key;
|
||||
AES_set_encrypt_key(&key_array_[i].key_data[0], 256, &aes_key);
|
||||
encrypted_content_keys[i].resize(
|
||||
encrypted_entitled_key_array[i].content_key_data_length);
|
||||
|
||||
// Encrypt the content key with the entitlement key.
|
||||
uint8_t iv[16];
|
||||
memcpy(&iv[0], &encrypted_entitled_key_array[i].content_key_data[0], 16);
|
||||
AES_cbc_encrypt(
|
||||
&entitled_key_array_[i].content_key_data[0],
|
||||
const_cast<uint8_t*>(
|
||||
&encrypted_entitled_key_array[i].content_key_data[0]),
|
||||
encrypted_entitled_key_array[i].content_key_data_length,
|
||||
&aes_key, iv, AES_ENCRYPT);
|
||||
|
||||
// Set the |encrypted_entitled_key_array| to point to the encrypted copy
|
||||
// of the content key.
|
||||
encrypted_entitled_key_array[i].content_key_data =
|
||||
encrypted_content_keys[i].data();
|
||||
}
|
||||
ASSERT_EQ(expected_sts,
|
||||
OEMCrypto_LoadEntitledContentKeys(
|
||||
session_id(), num_keys_, &encrypted_entitled_key_array[0]));
|
||||
if (expected_sts != OEMCrypto_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
VerifyEntitlementTestKeys();
|
||||
}
|
||||
|
||||
void Session::VerifyTestKeys() {
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
KeyControlBlock block;
|
||||
@@ -264,6 +367,29 @@ void Session::VerifyTestKeys() {
|
||||
}
|
||||
}
|
||||
|
||||
void Session::VerifyEntitlementTestKeys() {
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
KeyControlBlock block;
|
||||
size_t size = sizeof(block);
|
||||
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
|
||||
session_id(), entitled_key_array_[i].content_key_id,
|
||||
entitled_key_array_[i].content_key_id_length,
|
||||
reinterpret_cast<uint8_t*>(&block), &size);
|
||||
if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_EQ(sizeof(block), size);
|
||||
// control duration and bits stored in network byte order. For printing
|
||||
// we change to host byte order.
|
||||
ASSERT_EQ((htonl_fnc(license_.keys[i].control.duration)),
|
||||
(htonl_fnc(block.duration)))
|
||||
<< "For key " << i;
|
||||
ASSERT_EQ(htonl_fnc(license_.keys[i].control.control_bits),
|
||||
htonl_fnc(block.control_bits))
|
||||
<< "For key " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce, OEMCryptoResult expected_result) {
|
||||
// Note: we store the message in encrypted_license_, but the refresh key
|
||||
@@ -319,7 +445,57 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control,
|
||||
sizeof(license_.keys[i].key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv,
|
||||
sizeof(license_.keys[i].control_iv)));
|
||||
if (global_features.api_version == 13) {
|
||||
if (global_features.api_version == 14) {
|
||||
// For version 14, we require OEMCrypto to handle kc14 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc14", 4);
|
||||
} else if (global_features.api_version == 13) {
|
||||
// For version 13, we require OEMCrypto to handle kc13 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc13", 4);
|
||||
} else if (global_features.api_version == 12) {
|
||||
// For version 12, we require OEMCrypto to handle kc12 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc12", 4);
|
||||
} else if (control & wvoec_mock::kControlSecurityPatchLevelMask) {
|
||||
// For versions before 12, we require the special key control block only
|
||||
// when there are newer features present.
|
||||
memcpy(license_.keys[i].control.verification, "kc11", 4);
|
||||
} else if (control & wvoec_mock::kControlRequireAntiRollbackHardware) {
|
||||
memcpy(license_.keys[i].control.verification, "kc10", 4);
|
||||
} else if (control & (wvoec_mock::kControlHDCPVersionMask |
|
||||
wvoec_mock::kControlReplayMask)) {
|
||||
memcpy(license_.keys[i].control.verification, "kc09", 4);
|
||||
} else {
|
||||
memcpy(license_.keys[i].control.verification, "kctl", 4);
|
||||
}
|
||||
license_.keys[i].control.duration = htonl(duration);
|
||||
license_.keys[i].control.nonce = htonl(nonce);
|
||||
license_.keys[i].control.control_bits = htonl(control);
|
||||
license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR;
|
||||
}
|
||||
memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length()));
|
||||
pst_ = pst;
|
||||
}
|
||||
|
||||
void Session::FillSimpleEntitlementMessage(
|
||||
uint32_t duration, uint32_t control, uint32_t nonce,
|
||||
const std::string& pst) {
|
||||
EXPECT_EQ(
|
||||
1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys)));
|
||||
for (unsigned int i = 0; i < num_keys_; i++) {
|
||||
memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength);
|
||||
license_.keys[i].key_id_length = kDefaultKeyIdLength;
|
||||
memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length);
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data,
|
||||
sizeof(license_.keys[i].key_data)));
|
||||
license_.keys[i].key_data_length = wvcdm::KEY_SIZE * 2; // AES-256 keys
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv,
|
||||
sizeof(license_.keys[i].key_iv)));
|
||||
EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv,
|
||||
sizeof(license_.keys[i].control_iv)));
|
||||
if (global_features.api_version == 14) {
|
||||
// For version 13, we require OEMCrypto to handle kc14 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc14", 4);
|
||||
} else if (global_features.api_version == 13) {
|
||||
// For version 13, we require OEMCrypto to handle kc13 for all licenses.
|
||||
memcpy(license_.keys[i].control.verification, "kc13", 4);
|
||||
} else if (global_features.api_version == 12) {
|
||||
@@ -464,7 +640,6 @@ void Session::FillKeyArray(const MessageData& data,
|
||||
key_array[i].key_control_iv = data.keys[i].control_iv;
|
||||
key_array[i].key_control =
|
||||
reinterpret_cast<const uint8_t*>(&data.keys[i].control);
|
||||
key_array[i].cipher_mode = data.keys[i].cipher_mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,7 +689,8 @@ void Session::TestDecryptCTR(bool select_key_first,
|
||||
if (select_key_first) {
|
||||
// Select the key (from FillSimpleMessage)
|
||||
sts = OEMCrypto_SelectKey(session_id(), license_.keys[key_index].key_id,
|
||||
license_.keys[key_index].key_id_length);
|
||||
license_.keys[key_index].key_id_length,
|
||||
OEMCrypto_CipherMode_CTR);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
}
|
||||
|
||||
@@ -578,7 +754,8 @@ void Session::TestSelectExpired(unsigned int key_index) {
|
||||
if (global_features.api_version >= 13) {
|
||||
OEMCryptoResult status =
|
||||
OEMCrypto_SelectKey(session_id(), license().keys[key_index].key_id,
|
||||
license().keys[key_index].key_id_length);
|
||||
license().keys[key_index].key_id_length,
|
||||
OEMCrypto_CipherMode_CTR);
|
||||
// It is OK for SelectKey to succeed with an expired key, but if there is
|
||||
// an error, it must be OEMCrypto_ERROR_KEY_EXIRED.
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
|
||||
Reference in New Issue
Block a user