Regular update

Plugin:
1. Process ECM v3 and send fingerprinting/service_blocking events
2. Rmove unused function Ctr128Add
3. Add support for ECM v3

OEMCrypto:
1. Update API description of OEMCrypto_LoadCasECMKeys
2. Fix android build files for ODK
3. Load content keys to shared memory
4. Move KCB check to LoadCasKeys call
5. Support even/odd content keys to share entitlement key
This commit is contained in:
Lu Chen
2021-01-05 10:16:26 -08:00
parent 66d8498d2c
commit 00785b2ccd
38 changed files with 2234 additions and 747 deletions

View File

@@ -268,10 +268,10 @@ typedef struct {
* padding.
* entitlement_key_id - entitlement key id to be matched to key table.
* content_key_id - content key id to be loaded into key table.
* content_key_data_iv - the IV for performing AES-256-CBC decryption of the key data.
* content_key_data - encrypted content key data.
* content_iv - the 16 byte iv used to decrypt content.
* cipher_mode - the cipher mode to be used to decrypt the content.
* content_key_data_iv - the IV for performing AES-256-CBC decryption of the key
* data. content_key_data - encrypted content key data. content_iv - the 16 byte
* iv used to decrypt content. cipher_mode - the cipher mode to be used to
* decrypt the content.
*/
typedef struct {
OEMCrypto_Substring entitlement_key_id;
@@ -1737,42 +1737,44 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
* OEMCrypto_LoadCasECMKeys
*
* Description:
* Load content keys into a session which already has entitlement
* keys loaded. This function will only be called for a session after a call
* to OEMCrypto_LoadKeys with the parameter type license_type equal to
* OEMCrypto_EntitlementLicense. This function may be called multiple times
* for the same session.
* The OEMCrypto_LoadCasECMKeys method is added to load content keys into an
* entitled key session, which already has entitlement keys loaded.
*
* If the session does not have license_type equal to
* OEMCrypto_EntitlementLicense, return OEMCrypto_ERROR_INVALID_CONTEXT and
* perform no work.
* This function will only be called for a session after a call to
* OEMCrypto_LoadKeys with the license_type equal to
* OEMCrypto_EntitlementLicense, and a call to
* OEMCrypto_CreateEntitledKeySession initializing the entitled key session.
* This function may be called multiple times for the same session.
*
* For each key object in key_array, OEMCrypto shall look up the entry in the
* key table with the corresponding entitlement_key_id.
* For each key object, odd and even, OEMCrypto shall look up the entry in the
* key table with the corresponding entitlement_key_id. Before the
* entitlement_key is used:
* 1) If no entry is found, return OEMCrypto_KEY_NOT_ENTITLED.
* 2) If the entry already has a content_key_id and content_key_data, that id
* and data are erased.
* 3) The content_key_id from the key_array is copied to the entry's
* content_key_id.
* 2) Check the entitlement keys key control block use. If failed, return
* corresponding error code such as OEMCrypto_ERROR_ANALOG_OUTPUT,
* OEMCrypto_ERROR_INSUFFICIENT_HDCP.
* 3) If the entitlement keys control block has a nonzero Duration field,
* then the API shall verify that the duration is greater than the
* sessions elapsed time clock before the key is used. OEMCrypto will
* return OEMCrypto_ERROR_KEY_EXPIRED.
* 4) The content_key_data decrypted using the entitlement_key_data as a key
* for AES-256-CBC with an IV of content_key_data_iv, and using PKCS#7
* padding. Notice that the entitlement key will be an AES 256 bit key.
* The clear content key data will be stored in the entry's
* content_key_data.
* 5) The decrypted content key data may be set in a hardware descrambler
* if present.
*
* Entries in the key table that do not correspond to anything in the
* key_array are not modified or removed.
*
* For devices that use a hardware key ladder, it may be more appropriate to
* store the encrypted content key data in the key table, and defer decrypting
* it until the function SelectKey is called.
* for AES-256-CBC with an IV of content_key_data_iv. Wrapped content is
* padded using PKCS#7 padding. Notice that the entitlement key will be an
* AES 256 bit key. The clear content key data will be stored in the
* entrys content_key_data.
* 5) The decrypted content key data may be set in a hardware KeySlot,
* together with content iv and cipher mode information, which can be used
* by the Descrambler in TunerHal. The entitled key session ID may be used
* as the key token to uniquely identify the content key in KeySlot.
*
* Parameters:
* session (in) - handle for the session to be used.
* even_key (in) - key update for the even ecm key.
* odd_key (in) - key update for the odd ecm key.
* [in] session: handle for the entitled key session to be used.
* [in] message: pointer to memory containing message to be verified.
* [in] message_length: length of the message, in bytes.
* [in] even_key: key update for the even ecm key. May be null if the key
* does not change.
* [in] odd_key: key update for the odd ecm key. May be null if the key does
* not change.
*
* Returns
* OEMCrypto_SUCCESS success
@@ -1781,6 +1783,11 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys(
* OEMCrypto_ERROR_INSUFFICIENT_RESOURCES
* OEMCrypto_ERROR_UNKNOWN_FAILURE
* OEMCrypto_KEY_NOT_ENTITLED
* OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION
* OEMCrypto_ERROR_KEY_EXPIRED
* OEMCrypto_ERROR_ANALOG_OUTPUT
* OEMCrypto_ERROR_INSUFFICIENT_HDCP
*
* Threading
*
* This function may be called simultaneously with functions on other

View File

@@ -3,7 +3,7 @@
// License Agreement.
cc_defaults {
name: "odk_fuzz_library_defaults",
name: "cas_odk_fuzz_library_defaults",
srcs: [
"odk_fuzz_helper.cpp",
],
@@ -15,7 +15,7 @@ cc_defaults {
}
cc_fuzz {
name: "odk_license_request_fuzz",
name: "cas_odk_license_request_fuzz",
srcs: [
"odk_license_request_fuzz.cpp",
],
@@ -24,15 +24,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/license_request_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_renewal_request_fuzz",
name: "cas_odk_renewal_request_fuzz",
srcs: [
"odk_renewal_request_fuzz.cpp",
],
@@ -41,15 +41,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/renewal_request_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_provisioning_request_fuzz",
name: "cas_odk_provisioning_request_fuzz",
srcs: [
"odk_provisioning_request_fuzz.cpp",
],
@@ -58,15 +58,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/provisioning_request_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_license_response_fuzz",
name: "cas_odk_license_response_fuzz",
srcs: [
"odk_license_response_fuzz.cpp",
],
@@ -75,15 +75,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/license_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_renewal_response_fuzz",
name: "cas_odk_renewal_response_fuzz",
srcs: [
"odk_renewal_response_fuzz.cpp",
],
@@ -92,15 +92,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_provisioning_response_fuzz",
name: "cas_odk_provisioning_response_fuzz",
srcs: [
"odk_provisioning_response_fuzz.cpp",
],
@@ -109,15 +109,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_license_response_fuzz_with_mutator",
name: "cas_odk_license_response_fuzz_with_mutator",
srcs: [
"odk_license_response_fuzz_with_mutator.cpp",
],
@@ -126,15 +126,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/license_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_renewal_response_fuzz_with_mutator",
name: "cas_odk_renewal_response_fuzz_with_mutator",
srcs: [
"odk_renewal_response_fuzz_with_mutator.cpp",
],
@@ -143,15 +143,15 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/renewal_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}
cc_fuzz {
name: "odk_provisioning_response_fuzz_with_mutator",
name: "cas_odk_provisioning_response_fuzz_with_mutator",
srcs: [
"odk_provisioning_response_fuzz_with_mutator.cpp",
],
@@ -160,9 +160,9 @@ cc_fuzz {
},
corpus: ["corpus/little_endian_64bit/provisioning_response_corpus/*"],
static_libs: [
"libwv_kdo",
"libwv_odk",
"libwvcas_kdo",
"libwvcas_odk",
],
defaults: ["odk_fuzz_library_defaults"],
defaults: ["cas_odk_fuzz_library_defaults"],
proprietary: true,
}

View File

@@ -10,7 +10,7 @@
// Builds libwv_odk.so, The ODK shared Library (libwv_odk) is used
// by the OEMCrypto unit tests to generate corpus for ODK fuzz scrips.
cc_library_shared {
name: "libwv_odk_corpus_generator",
name: "libwvcas_odk_corpus_generator",
include_dirs: [
"vendor/widevine/libwvmediacas/oemcrypto/include",
"vendor/widevine/libwvmediacas/oemcrypto/odk/include",

View File

@@ -4,22 +4,87 @@
#include "oemcrypto_entitled_key_session.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <cstring>
#include "log.h"
namespace wvoec_ref {
namespace {
constexpr size_t kContentKeySize = 16;
constexpr size_t kContentIvSize = 16;
// Returns the open file descriptor.
int OpenHardwareKeySlot() {
// Must be hardware location in production, instead of a file that is publicly
// visible.
// Not able to create (permission denied). The file must already exist.
int fd = open("/data/local/tmp/wv", O_RDWR);
if (fd < 0) {
LOGE("File for shared memory open failed: %d", errno);
}
return fd;
}
// Returns true if the lock is success.
bool LockHardwareKeySlot(int fd) {
flock lock;
lock.l_type = F_WRLCK; /* read/write (exclusive versus shared) lock */
lock.l_whence = SEEK_SET; /* base for seek offsets */
lock.l_start = 0; /* 1st byte in file */
lock.l_len = 0; /* 0 here means 'until EOF' */
lock.l_pid = getpid(); /* process id */
// Wait until we get the lock.
if (fcntl(fd, F_SETLKW, &lock) < 0) {
LOGE("Failed to lock file: %d", errno);
return false;
}
return true;
}
void UnlockAndCloseHardwareKeySlot(int fd) {
if (fd < 0) {
return;
}
flock lock;
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET; /* base for seek offsets */
lock.l_start = 0; /* 1st byte in file */
lock.l_len = 0; /* 0 here means 'until EOF' */
lock.l_pid = getpid(); /* process id */
if (fcntl(fd, F_SETLK, &lock) < 0) {
LOGE("explicit unlocking failed.");
}
close(fd); /* close the file: would unlock if needed */
}
} // namespace
EntitledKeySession::~EntitledKeySession() { ClearKeySlot(); }
const Key* EntitledKeySession::GetEntitlementKey(
const KeyId& content_key_id) const {
if (entitlement_key_map_.find(content_key_id) == entitlement_key_map_.end()) {
if (content_key_info_map_.find(content_key_id) ==
content_key_info_map_.end()) {
return nullptr;
}
return entitlement_key_map_.at(content_key_id);
return content_key_info_map_.at(content_key_id)->entitlement_key;
}
EntitledKey* EntitledKeySession::GetContentKey(
const KeyId& content_key_id) const {
if (content_key_map_.find(content_key_id) == content_key_map_.end()) {
if (content_key_info_map_.find(content_key_id) ==
content_key_info_map_.end()) {
return nullptr;
}
return content_key_map_.at(content_key_id).get();
return content_key_info_map_.at(content_key_id)->content_key.get();
}
bool EntitledKeySession::AddOrUpdateContentKey(
@@ -29,32 +94,151 @@ bool EntitledKeySession::AddOrUpdateContentKey(
content_key == nullptr) {
return false;
}
// Remove the entry if |entitlement_key| already exists. Each entitlement key
// can only be referred by one content key within an entitled key session.
for (auto const& content_id_entitlement : entitlement_key_map_) {
if (content_id_entitlement.second == entitlement_key) {
RemoveContentKey(content_id_entitlement.first);
// Remove the entry if |entitlement_key| already referenced by a content key
// with the same parity. Each entitlement key can only be referred by one
// even and one odd content key within an entitled key session.
for (auto const& content_key_entry : content_key_info_map_) {
const ContentKeyInfo* const key_info = content_key_entry.second.get();
if (key_info->entitlement_key == entitlement_key &&
key_info->content_key->is_even_key() == content_key->is_even_key()) {
RemoveContentKey(content_key_entry.first);
break;
}
}
// |content_key_id| must be unique.
if (content_key_map_.find(content_key_id) != content_key_map_.end()) {
if (content_key_info_map_.find(content_key_id) !=
content_key_info_map_.end()) {
return false;
}
content_key_map_[content_key_id] = std::move(content_key);
entitlement_key_map_[content_key_id] = entitlement_key;
content_key_info_map_[content_key_id] =
std::unique_ptr<ContentKeyInfo>(new ContentKeyInfo());
content_key_info_map_[content_key_id]->content_key = std::move(content_key);
content_key_info_map_[content_key_id]->entitlement_key = entitlement_key;
return true;
}
bool EntitledKeySession::RemoveContentKey(const KeyId& content_key_id) {
if (content_key_map_.find(content_key_id) == content_key_map_.end()) {
if (content_key_info_map_.find(content_key_id) ==
content_key_info_map_.end()) {
return false;
}
content_key_map_.erase(content_key_id);
entitlement_key_map_.erase(content_key_id);
content_key_info_map_.erase(content_key_id);
return true;
}
EntitledKeySession::ContentKeySlot* EntitledKeySession::GetKeySlotToUpdate(
uint8_t* buffer) const {
if (buffer == nullptr) {
return nullptr;
}
// Read the current content to know where to write.
auto content_key_slots = reinterpret_cast<ContentKeySlots*>(buffer);
int use_index = -1;
for (size_t i = 0; i < sizeof(ContentKeySlots) / sizeof(ContentKeySlot);
++i) {
const ContentKeySlot& current_key_slot = content_key_slots->key_slots[i];
// Use the existing slot, or the first available slot if there is no
// existing one.
if (current_key_slot.entitled_key_session_id == key_sid_) {
use_index = i;
break;
}
if (current_key_slot.entitled_key_session_id == 0 && use_index < 0) {
use_index = i;
}
}
if (use_index < 0) {
LOGE("No key slot available to write keys.");
return nullptr;
}
return &content_key_slots->key_slots[use_index];
}
bool EntitledKeySession::WriteContentKeyToKeySlot(
const KeyId& content_key_id, ContentKeySlot* key_slot) const {
if (key_slot == nullptr) {
LOGE("key_slot is null");
return false;
}
EntitledKey* entitled_key = GetContentKey(content_key_id);
if (entitled_key == nullptr) {
LOGE("Content key is not found.");
return false;
}
key_slot->entitled_key_session_id = key_sid_;
key_slot->cipher_mode = static_cast<uint8_t>(entitled_key->cipher_mode());
memcpy(entitled_key->is_even_key() ? &key_slot->even_key[0]
: &key_slot->odd_key[0],
entitled_key->value().data(),
std::min(entitled_key->value().size(), kContentKeySize));
memcpy(entitled_key->is_even_key() ? &key_slot->even_iv[0]
: &key_slot->odd_iv[0],
entitled_key->content_iv().data(),
std::min(entitled_key->content_iv().size(), kContentIvSize));
msync(reinterpret_cast<void*>(key_slot), sizeof(ContentKeySlot), MS_SYNC);
return true;
}
bool EntitledKeySession::WriteToKeySlot(const KeyId& content_key_id) const {
bool result = false;
const int fd = OpenHardwareKeySlot();
if (fd < 0) {
return result;
}
if (!LockHardwareKeySlot(fd)) {
close(fd);
return result;
}
ftruncate(fd, sizeof(ContentKeySlots));
uint8_t* buffer = static_cast<uint8_t*>(mmap(nullptr, sizeof(ContentKeySlots),
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, /*offset=*/0));
if (buffer == MAP_FAILED) {
LOGE("Fail to get mmap buffer %d", errno);
close(fd);
return result;
}
ContentKeySlot* key_slot = GetKeySlotToUpdate(buffer);
if (key_slot != nullptr) {
result = WriteContentKeyToKeySlot(content_key_id, key_slot);
}
munmap(reinterpret_cast<void*>(buffer), sizeof(ContentKeySlots));
UnlockAndCloseHardwareKeySlot(fd);
return result;
}
void EntitledKeySession::ClearKeySlot() const {
const int fd = OpenHardwareKeySlot();
if (fd < 0) {
return;
}
if (!LockHardwareKeySlot(fd)) {
close(fd);
return;
}
ftruncate(fd, sizeof(ContentKeySlots));
uint8_t* buffer = static_cast<uint8_t*>(mmap(nullptr, sizeof(ContentKeySlots),
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, /*offset=*/0));
if (buffer == MAP_FAILED) {
LOGE("Fail to get mmap buffer %d", errno);
close(fd);
return;
}
ContentKeySlot* key_slot = GetKeySlotToUpdate(buffer);
if (key_slot != nullptr) {
memset(key_slot, 0, sizeof(ContentKeySlot));
msync(reinterpret_cast<void*>(key_slot), sizeof(ContentKeySlot), MS_SYNC);
}
munmap(reinterpret_cast<void*>(buffer), sizeof(ContentKeySlots));
UnlockAndCloseHardwareKeySlot(fd);
}
/***************************************/
EntitledKeySession* EntitledKeySessionTable::CreateEntitledKeySession(

View File

@@ -28,7 +28,7 @@ class EntitledKeySession {
current_entitlement_key_(nullptr){};
EntitledKeySession(const EntitledKeySession&) = delete;
EntitledKeySession(EntitledKeySession&&) = delete;
~EntitledKeySession() = default;
~EntitledKeySession();
// Get id of this entitled key session .
SessionId GetSessionId() const { return key_sid_; }
@@ -55,17 +55,47 @@ class EntitledKeySession {
// Remove a content key |content_key_id|.
bool RemoveContentKey(const KeyId& content_key_id);
// Write the content key info to hardware key slot, which can be retrieved
// from Android TV Tuner Hal implementation.
bool WriteToKeySlot(const KeyId& content_key_id) const;
// Clear the content keys written to hardware key slots by this entitled key
// session. The function is called by destructor.
void ClearKeySlot() const;
// Total number of content keys this entitled key session holds.
size_t size() const { return content_key_map_.size(); }
size_t size() const { return content_key_info_map_.size(); }
private:
struct ContentKeyInfo {
std::unique_ptr<EntitledKey> content_key;
Key* entitlement_key;
};
// Must have the same exact definition on the tuner hal side.
typedef struct {
uint32_t entitled_key_session_id;
uint8_t cipher_mode;
uint8_t even_key[16];
uint8_t even_iv[16];
uint8_t odd_key[16];
uint8_t odd_iv[16];
} ContentKeySlot;
// Must have the same exact definition on the tuner hal side.
typedef struct {
// Assume max number of slots is 16.
ContentKeySlot key_slots[16];
} ContentKeySlots;
ContentKeySlot* GetKeySlotToUpdate(uint8_t* buffer) const;
bool WriteContentKeyToKeySlot(const KeyId& content_key_id,
ContentKeySlot* key_slot) const;
const SessionId key_sid_;
EntitledKey* current_content_key_;
const Key* current_entitlement_key_;
// Map from content key id to content key.
std::map<KeyId, std::unique_ptr<EntitledKey>> content_key_map_;
// Map from content key id to referenced entitlement key.
std::map<KeyId, Key*> entitlement_key_map_;
// Map from content key id to content key info.
std::map<KeyId, std::unique_ptr<ContentKeyInfo>> content_key_info_map_;
};
class EntitledKeySessionTable {

View File

@@ -68,7 +68,9 @@ class Key {
class EntitledKey {
public:
explicit EntitledKey(std::vector<uint8_t> key_string)
: value_(std::move(key_string)), cipher_mode_(OEMCrypto_CipherMode_CTR){};
: value_(std::move(key_string)),
cipher_mode_(OEMCrypto_CipherMode_CTR),
is_even_key_(true){};
EntitledKey(const EntitledKey&) = default;
EntitledKey(EntitledKey&&) = default;
~EntitledKey() = default;
@@ -78,11 +80,14 @@ class EntitledKey {
void set_cipher_mode(OEMCryptoCipherMode mode) { cipher_mode_ = mode; }
void set_content_iv(const std::vector<uint8_t>& iv) { content_iv_ = iv; }
const std::vector<uint8_t>& content_iv() const { return content_iv_; }
bool is_even_key() const { return is_even_key_; }
void set_is_even_key(bool is_even_key) { is_even_key_ = is_even_key; }
private:
std::vector<uint8_t> value_;
OEMCryptoCipherMode cipher_mode_;
std::vector<uint8_t> content_iv_;
bool is_even_key_;
};
} // namespace wvoec_ref

View File

@@ -470,16 +470,16 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadCasECMKeys(
LOGE("[OEMCrypto_LoadCasECMKeys(): ERROR_INVALID_SESSION]");
return OEMCrypto_ERROR_INVALID_SESSION;
}
std::vector<OEMCrypto_EntitledCasKeyObject> key_array;
std::vector<const OEMCrypto_EntitledCasKeyObject*> key_array;
key_array.reserve(2);
if (even_key) key_array.push_back(*even_key);
if (odd_key) key_array.push_back(*odd_key);
if (even_key) key_array.push_back(even_key);
if (odd_key) key_array.push_back(odd_key);
for (unsigned int i = 0; i < key_array.size(); i++) {
if (!RangeCheck(message_length, key_array[i].entitlement_key_id, false) ||
!RangeCheck(message_length, key_array[i].content_key_id, false) ||
!RangeCheck(message_length, key_array[i].content_key_data_iv, false) ||
!RangeCheck(message_length, key_array[i].content_key_data, false) ||
!RangeCheck(message_length, key_array[i].content_iv, false)) {
if (!RangeCheck(message_length, key_array[i]->entitlement_key_id, false) ||
!RangeCheck(message_length, key_array[i]->content_key_id, false) ||
!RangeCheck(message_length, key_array[i]->content_key_data_iv, false) ||
!RangeCheck(message_length, key_array[i]->content_key_data, false) ||
!RangeCheck(message_length, key_array[i]->content_iv, true)) {
LOGE(
"[OEMCrypto_LoadCasECMKeys(): "
"OEMCrypto_ERROR_INVALID_CONTEXT -range "
@@ -489,8 +489,7 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadCasECMKeys(
}
}
return session_ctx->LoadEntitledCasKeys(entitled_key_session, message,
message_length, key_array.size(),
&key_array[0]);
message_length, even_key, odd_key);
}
OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session,

View File

@@ -840,9 +840,9 @@ OEMCryptoResult SessionContext::LoadEntitledContentKeys(
OEMCryptoResult SessionContext::LoadEntitledCasKeys(
EntitledKeySession* key_session, const uint8_t* message,
size_t message_length, size_t key_array_length,
const OEMCrypto_EntitledCasKeyObject* key_array) {
if (!key_array) {
size_t message_length, const OEMCrypto_EntitledCasKeyObject* even_key,
const OEMCrypto_EntitledCasKeyObject* odd_key) {
if (even_key == nullptr && odd_key == nullptr) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (session_keys_ == nullptr ||
@@ -853,8 +853,13 @@ OEMCryptoResult SessionContext::LoadEntitledCasKeys(
return OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION;
}
for (size_t i = 0; i < key_array_length; ++i) {
const OEMCrypto_EntitledCasKeyObject* key_data = &key_array[i];
const std::vector<const OEMCrypto_EntitledCasKeyObject*> key_array = {
even_key, odd_key};
for (size_t i = 0; i < key_array.size(); ++i) {
if (key_array[i] == nullptr) {
continue;
}
const OEMCrypto_EntitledCasKeyObject* key_data = key_array[i];
std::vector<uint8_t> entitlement_key_id;
entitlement_key_id.assign(message + key_data->entitlement_key_id.offset,
message + key_data->entitlement_key_id.offset +
@@ -864,6 +869,15 @@ OEMCryptoResult SessionContext::LoadEntitledCasKeys(
if (entitlement_key == nullptr) {
return OEMCrypto_KEY_NOT_ENTITLED;
}
// Assume in Tuner Hal, buffer type is always secure.
OEMCryptoResult result = CheckKeyControlBlockUse(
entitlement_key->control(), "LoadEntitledCasKeys", /*use_type=*/0,
OEMCrypto_BufferType_Secure);
if (result != OEMCrypto_SUCCESS) {
return result;
}
std::vector<uint8_t> content_key;
std::vector<uint8_t> iv;
std::vector<uint8_t> encrypted_content_key;
@@ -893,11 +907,19 @@ OEMCryptoResult SessionContext::LoadEntitledCasKeys(
content_key_obj->set_content_iv(content_iv);
}
content_key_obj->set_cipher_mode(key_data->cipher_mode);
content_key_obj->set_is_even_key(i % 2 == 0);
if (!key_session->AddOrUpdateContentKey(entitlement_key, content_key_id,
std::move(content_key_obj))) {
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
}
if (!key_session->WriteToKeySlot(content_key_id)) {
// Currently assume this is acceptable.
LOGE("Failed to update content keys to hardware KeySlots.");
} else {
LOGI("Updated content keys to hardware KeySlots.");
}
}
return OEMCrypto_SUCCESS;
}

View File

@@ -118,8 +118,8 @@ class SessionContext {
const OEMCrypto_EntitledContentKeyObject* key_array);
virtual OEMCryptoResult LoadEntitledCasKeys(
EntitledKeySession* key_session, const uint8_t* message,
size_t message_length, size_t key_array_length,
const OEMCrypto_EntitledCasKeyObject* key_array);
size_t message_length, const OEMCrypto_EntitledCasKeyObject* even_key,
const OEMCrypto_EntitledCasKeyObject* odd_key);
virtual OEMCryptoResult InstallKey(
const KeyId& key_id, const std::vector<uint8_t>& key_data,
const std::vector<uint8_t>& key_data_iv,

View File

@@ -794,33 +794,98 @@ void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) {
VerifyEntitlementTestKeys();
}
void EntitledMessage::LoadCasKeys(OEMCryptoResult expected_sts, bool load_even,
bool load_odd) {
for (size_t i = 0; i < num_keys_; ++i) {
EntitledContentKeyData* key_data = &entitled_key_data_[i];
const size_t entitlement_key_index = key_data->key_index;
MessageKeyData* entitlement_key =
&license_messages_->response_data().keys[entitlement_key_index];
// Load the entitlement key from |key_array_|.
AES_KEY aes_key;
AES_set_encrypt_key(entitlement_key->key_data, 256, &aes_key);
// Encrypt the content key with the entitlement key.
uint8_t iv[16];
memcpy(&iv[0], key_data->content_key_data_iv, KEY_IV_SIZE);
AES_cbc_encrypt(key_data->content_key_data,
key_data->encrypted_content_key_data, KEY_SIZE, &aes_key,
iv, AES_ENCRYPT);
}
// Convert the OEMCrypto_EntitledContentKeyObject to
// OEMCrypto_EntitledCasKeyObject. Only the first two key object is used.
OEMCrypto_EntitledCasKeyObject even_key;
OEMCrypto_EntitledCasKeyObject odd_key;
bool has_even = load_even && num_keys_ >= 1;
bool has_odd = load_odd && num_keys_ >= 2;
if (has_even) {
even_key.entitlement_key_id = entitled_key_array_[0].entitlement_key_id;
even_key.content_key_id = entitled_key_array_[0].content_key_id;
even_key.content_key_data_iv = entitled_key_array_[0].content_key_data_iv;
even_key.content_key_data = entitled_key_array_[0].content_key_data;
even_key.content_iv.length = 0;
}
if (has_odd) {
odd_key.entitlement_key_id = entitled_key_array_[1].entitlement_key_id;
odd_key.content_key_id = entitled_key_array_[1].content_key_id;
odd_key.content_key_data_iv = entitled_key_array_[1].content_key_data_iv;
odd_key.content_key_data = entitled_key_array_[1].content_key_data;
odd_key.content_iv.length = 0;
}
ASSERT_EQ(
expected_sts,
OEMCrypto_LoadCasECMKeys(
key_session_, reinterpret_cast<const uint8_t*>(entitled_key_data_),
sizeof(entitled_key_data_), has_even ? &even_key : nullptr,
has_odd ? &odd_key : nullptr));
if (expected_sts != OEMCrypto_SUCCESS) {
return;
}
if (has_even) {
VerifyEntitlementTestKey(0);
}
if (has_odd) {
VerifyEntitlementTestKey(1);
}
}
// This function verifies that the key control block reported by OEMCrypto agree
// with the truth key control block. Failures in this function probably
// indicate the OEMCrypto_LoadEntitledKeys did not correctly process the key
// control block.
void EntitledMessage::VerifyEntitlementTestKeys() {
for (unsigned int i = 0; i < num_keys_; i++) {
EntitledContentKeyData* key_data = &entitled_key_data_[i];
const size_t entitlement_key_index = key_data->key_index;
MessageKeyData* entitlement_key =
&license_messages_->response_data().keys[entitlement_key_index];
KeyControlBlock block;
size_t size = sizeof(block);
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
key_session_, key_data->content_key_id, key_data->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(entitlement_key->control.duration)),
(htonl_fnc(block.duration)))
<< "For key " << i;
ASSERT_EQ(htonl_fnc(entitlement_key->control.control_bits),
htonl_fnc(block.control_bits))
<< "For key " << i;
}
VerifyEntitlementTestKey(i);
}
}
void EntitledMessage::VerifyEntitlementTestKey(size_t index) {
ASSERT_GE(num_keys_, index);
EntitledContentKeyData* key_data = &entitled_key_data_[index];
const size_t entitlement_key_index = key_data->key_index;
MessageKeyData* entitlement_key =
&license_messages_->response_data().keys[entitlement_key_index];
KeyControlBlock block;
size_t size = sizeof(block);
OEMCryptoResult sts = OEMCrypto_QueryKeyControl(
key_session_, key_data->content_key_id, key_data->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(entitlement_key->control.duration)),
(htonl_fnc(block.duration)))
<< "For key " << index;
ASSERT_EQ(htonl_fnc(entitlement_key->control.control_bits),
htonl_fnc(block.control_bits))
<< "For key " << index;
}
}

View File

@@ -419,12 +419,14 @@ class EntitledMessage {
key_session_ = key_session;
}
void LoadKeys(OEMCryptoResult expected_sts);
void LoadCasKeys(OEMCryptoResult expected_sts, bool load_even, bool load_odd);
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
uint32_t num_keys() const { return num_keys_; }
void SetEntitlementKeyId(unsigned int index, const std::string& key_id);
void SetContentKeyId(unsigned int index, const std::string& key_id);
// Verify that key control blocks of the loaded keys.
void VerifyEntitlementTestKeys();
void VerifyEntitlementTestKey(size_t index);
private:
// Find the offset of the give pointer, relative to |entitled_key_data_|.

View File

@@ -896,7 +896,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwiceAPI16) {
}
// This verifies that entitlement keys and entitled content keys can be loaded.
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysAPI17) {
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -906,25 +906,24 @@ TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysAPI17) {
uint32_t key_session_id = 0;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
EntitledMessage entitled_message_2(&license_messages_);
entitled_message_2.FillKeyArray();
entitled_message_2.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
}
// This verifies that entitled content keys cannot be loaded if we have not yet
// loaded the entitlement keys.
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysNoEntitlementKeysAPI17) {
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysNoEntitlementKeysAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -933,22 +932,19 @@ TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysNoEntitlementKeysAPI17) {
uint32_t key_session_id = 0;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(
entitled_message_1.LoadKeys(OEMCrypto_ERROR_INVALID_CONTEXT));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_ERROR_INVALID_CONTEXT, /*load_even=*/true, /*load_odd=*/true));
}
// This verifies that entitled content keys cannot be loaded if we have loaded
// the wrong entitlement keys.
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI17) {
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -958,9 +954,6 @@ TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI17) {
uint32_t key_session_id = 0;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NE(key_session_id, 0u);
@@ -969,13 +962,13 @@ TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitlementKeysAPI17) {
const std::string key_id = "no_key";
entitled_message_1.SetEntitlementKeyId(0, key_id);
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(
entitled_message_1.LoadKeys(OEMCrypto_KEY_NOT_ENTITLED));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_KEY_NOT_ENTITLED, /*load_even=*/true, /*load_odd=*/true));
}
// This verifies that entitled content keys cannot be loaded if we specify an
// entitled key session that has not been created.
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitledKeySessionAPI17) {
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitledKeySessionAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -985,22 +978,20 @@ TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysWrongEntitledKeySessionAPI17) {
uint32_t key_session_id = 0;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(0);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION, /*load_even=*/true,
/*load_odd=*/true));
}
// This verifies that entitled content keys cannot be loaded if we specify an
// entitled key session that is actually an oemcrypto session.
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysOemcryptoSessionAPI17) {
TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysOemcryptoSessionAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1010,17 +1001,15 @@ TEST_P(OEMCryptoLicenseTest, LoadEntitlementKeysOemcryptoSessionAPI17) {
uint32_t key_session_id = 0;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
ASSERT_NE(key_session_id, 0u);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(session_.session_id());
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_ERROR_INVALID_ENTITLED_KEY_SESSION, /*load_even=*/true,
/*load_odd=*/true));
}
// This tests load license with an 8k license response.
@@ -1479,7 +1468,7 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI16) {
}
}
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyAPI17) {
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1490,16 +1479,14 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyAPI17) {
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
const char* content_key_id = "content_key_id";
entitled_message_1.SetContentKeyId(0, content_key_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/false));
ASSERT_EQ(
OEMCrypto_SUCCESS,
@@ -1510,7 +1497,7 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyAPI17) {
// SelectEntitledKey should fail if we attempt to select a key that has not been
// loaded. Also, the error should be NO_CONTENT_KEY.
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI17) {
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1521,14 +1508,12 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI17) {
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
const char* content_key_id = "no_key";
ASSERT_EQ(
@@ -1540,7 +1525,7 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI17) {
// Select key with entitlement license fails if the key id is entitilement key
// id.
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) {
TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1551,14 +1536,12 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) {
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
ASSERT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT,
OEMCrypto_SelectKey(session_.session_id(),
@@ -1568,7 +1551,7 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) {
}
// This verifies that entitled key sessions can be created and removed.
TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsAPI17) {
TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1579,9 +1562,6 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsAPI17) {
uint32_t key_session_id_1;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id_1);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_NE(key_session_id_1, 0u); // 0 is a reserved id number.
uint32_t key_session_id_2;
@@ -1599,7 +1579,7 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionsAPI17) {
// This verifies that multiple entitled key sessions can be created. They can
// load and select keys independently.
TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) {
TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1610,16 +1590,14 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) {
uint32_t key_session_id_1;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id_1);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id_1);
const char* content_key_id_1 = "content_key_id_1";
entitled_message_1.SetContentKeyId(0, content_key_id_1);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
// We can select content key 1 in entitled key session 1.
ASSERT_EQ(
OEMCrypto_SUCCESS,
@@ -1638,7 +1616,8 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) {
entitled_message_2.SetEntitledKeySession(key_session_id_2);
const char* content_key_id_2 = "content_key_id_2";
entitled_message_2.SetContentKeyId(0, content_key_id_2);
ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_2.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
// We can select content key 2 in entitled key session 2.
ASSERT_EQ(
OEMCrypto_SUCCESS,
@@ -1660,9 +1639,9 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) {
}
// This verifies that within an entitled key session, each entitlement key can
// corresponds to only one content key at most.
// corresponds to only one even content key at most.
TEST_P(OEMCryptoLicenseTest,
EntitledKeySessionOneContentKeyPerEntitlementAPI17) {
EntitledKeySessionOneContentKeyPerEntitlementAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
@@ -1673,9 +1652,6 @@ TEST_P(OEMCryptoLicenseTest,
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// Construct and load content keys to entitled key session.
EntitledMessage entitled_message_1(&license_messages_);
@@ -1683,7 +1659,8 @@ TEST_P(OEMCryptoLicenseTest,
entitled_message_1.SetEntitledKeySession(key_session_id);
const char* content_key_id_1 = "content_key_id_1";
entitled_message_1.SetContentKeyId(0, content_key_id_1);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
// We can select content key 1 in entitled key session.
ASSERT_EQ(
OEMCrypto_SUCCESS,
@@ -1693,7 +1670,8 @@ TEST_P(OEMCryptoLicenseTest,
// Load content key with new content id.
const char* content_key_id_2 = "content_key_id_2";
entitled_message_1.SetContentKeyId(0, content_key_id_2);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadKeys(OEMCrypto_SUCCESS));
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
// We can select content key 2 in entitled key session.
ASSERT_EQ(
OEMCrypto_SUCCESS,
@@ -1709,6 +1687,55 @@ TEST_P(OEMCryptoLicenseTest,
strlen(content_key_id_1), OEMCrypto_CipherMode_CTR));
}
// This verifies that within an entitled key session, each entitlement key can
// be shared by both even and odd content keys.
TEST_P(OEMCryptoLicenseTest,
EntitledKeySessionEvenOddContentKeysPerEntitlementAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
// Construct and load even content keys to entitled key session.
EntitledMessage entitled_message_1(&license_messages_);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
const char* content_key_id_1 = "content_key_id_1";
entitled_message_1.SetContentKeyId(0, content_key_id_1);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/false));
// We can select content key 1 (even key) in entitled key session.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id_1),
strlen(content_key_id_1), OEMCrypto_CipherMode_CTR));
// Load odd content key with new content id.
const char* content_key_id_2 = "content_key_id_2";
entitled_message_1.SetContentKeyId(1, content_key_id_2);
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/false, /*load_odd=*/true));
// We can select content key 2 (odd key) in entitled key session.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id_2),
strlen(content_key_id_2), OEMCrypto_CipherMode_CTR));
// Content key one is still in the entitled key session as they are even and
// odd keys that can use the same entitlement key.
ASSERT_EQ(
OEMCrypto_SUCCESS,
OEMCrypto_SelectKey(key_session_id,
reinterpret_cast<const uint8_t*>(content_key_id_1),
strlen(content_key_id_1), OEMCrypto_CipherMode_CTR));
}
// 'cens' mode is no longer supported in v16
TEST_P(OEMCryptoLicenseTest, RejectCensAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
@@ -1819,9 +1846,6 @@ TEST_P(OEMCryptoLicenseTest, QueryKeyControl) {
session_.session_id(), license_messages_.response_data().keys[0].key_id,
license_messages_.response_data().keys[0].key_id_length,
reinterpret_cast<uint8_t*>(&block), &size);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
return;
}
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
const char* key_id = "no_key";
size = sizeof(block);
@@ -2029,6 +2053,60 @@ TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) {
INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP,
Range(1, 6));
// Used to test the different HDCP versions. This test is parameterized by the
// required HDCP version in the key control block.
class OEMCryptoSessionTestLoadCasKeysWithHDCP : public OEMCryptoSessionTests,
public WithParamInterface<int> {
protected:
void LoadCasKeysWithHDCP(OEMCrypto_HDCP_Capability version) {
OEMCryptoResult sts;
OEMCrypto_HDCP_Capability current, maximum;
sts = OEMCrypto_GetHDCPCapability(&current, &maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s));
LicenseRoundTrip license_messages(&s);
license_messages.set_control((version << wvoec::kControlHDCPVersionShift) |
wvoec::kControlObserveHDCP |
wvoec::kControlHDCPRequired);
license_messages.set_license_type(OEMCrypto_EntitlementLicense);
ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse());
uint32_t key_session_id;
sts = OEMCrypto_CreateEntitledKeySession(s.session_id(), &key_session_id);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message_1(&license_messages);
entitled_message_1.FillKeyArray();
entitled_message_1.SetEntitledKeySession(key_session_id);
if (version > current) {
ASSERT_NO_FATAL_FAILURE(
entitled_message_1.LoadCasKeys(OEMCrypto_ERROR_INSUFFICIENT_HDCP,
/*load_even=*/true, /*load_odd=*/true))
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
<< ", license HDCP = " << HDCPCapabilityAsString(version);
} else {
ASSERT_NO_FATAL_FAILURE(entitled_message_1.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true))
<< "Failed when current HDCP = " << HDCPCapabilityAsString(current)
<< ", maximum HDCP = " << HDCPCapabilityAsString(maximum)
<< ", license HDCP = " << HDCPCapabilityAsString(version);
}
}
};
TEST_P(OEMCryptoSessionTestLoadCasKeysWithHDCP, LoadCasECMKeysAPI16) {
// Test parameterized by HDCP version.
LoadCasKeysWithHDCP(static_cast<OEMCrypto_HDCP_Capability>(GetParam()));
}
INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP,
Range(1, 6));
//
// Load, Refresh Keys Test
//
@@ -2924,6 +3002,54 @@ TEST_P(OEMCryptoLicenseTest, KeyDuration) {
ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(0));
}
TEST_P(OEMCryptoLicenseTest, LoadCasKeysNoAnalogAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
license_messages_.set_control(wvoec::kControlDisableAnalogOutput);
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message(&license_messages_);
entitled_message.FillKeyArray();
entitled_message.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys(
OEMCrypto_ERROR_ANALOG_OUTPUT, /*load_even=*/true, /*load_odd=*/true));
}
// Test that key duration is honored.
TEST_P(OEMCryptoLicenseTest, LoadCasKeysKeyDurationAPI16) {
ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce());
license_messages_.set_license_type(OEMCrypto_EntitlementLicense);
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t key_session_id;
OEMCryptoResult sts = OEMCrypto_CreateEntitledKeySession(
session_.session_id(), &key_session_id);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
EntitledMessage entitled_message(&license_messages_);
entitled_message.FillKeyArray();
entitled_message.SetEntitledKeySession(key_session_id);
ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
wvutil::TestSleep::Sleep(kShortSleep); // Should still be valid key.
ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys(
OEMCrypto_SUCCESS, /*load_even=*/true, /*load_odd=*/true));
wvutil::TestSleep::Sleep(kLongSleep); // Should be expired key.
ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys(
OEMCrypto_ERROR_KEY_EXPIRED, /*load_even=*/true, /*load_odd=*/true));
}
INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoLicenseTest,
Range<uint32_t>(kCurrentAPI - 1, kCurrentAPI + 1));