Add certificate information to Usage Information

[ Merge of http://go/wvgerrit/120509 ]

Certificates and wrapped key material are stored in the usage information
file but apart from usage information records. Usage info records include
an identifier which indicates the associated certificate. Routines to
help with finding, inserting or removal of associated certificates have
been included. After a usage entry is deleted, a garbage collection
routine is run to remove any certificates not used by usage infomation
records.

Bug: 169740403
Test: WV unit/integration tests
Change-Id: I49075a7704a58c2488b73eb7c38b007958af566d
This commit is contained in:
Rahul Frias
2021-03-20 15:39:24 -07:00
parent 335720be1c
commit 1aa197ce4a
8 changed files with 475 additions and 201 deletions

View File

@@ -1490,16 +1490,17 @@ CdmResponseType CdmEngine::RemoveUsageInfo(
new CdmSession(file_system_, metrics_->AddSession()));
usage_session_->Init(usage_property_set_.get());
std::vector<DeviceFiles::CdmUsageData> usage_data;
CdmKeyMessage license_request;
CdmKeyResponse license_response;
CdmUsageEntry usage_entry;
uint32_t usage_entry_number;
std::string drm_certificate;
CryptoWrappedKey wrapped_private_key;
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
provider_session_token, &license_request,
&license_response, &usage_entry,
&usage_entry_number)) {
if (!handle.RetrieveUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id), provider_session_token,
&license_request, &license_response, &usage_entry,
&usage_entry_number, &drm_certificate, &wrapped_private_key)) {
// Try other security level
continue;
}
@@ -1575,7 +1576,8 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id,
&(usage_data.provider_session_token), &(usage_data.license_request),
&(usage_data.license), &(usage_data.usage_entry),
&(usage_data.usage_entry_number))) {
&(usage_data.usage_entry_number), &(usage_data.drm_certificate),
&(usage_data.wrapped_private_key))) {
LOGE("Unable to find usage information");
return LOAD_USAGE_INFO_MISSING;
}

View File

@@ -1018,10 +1018,12 @@ CdmResponseType CdmSession::StoreLicense() {
std::string app_id;
GetApplicationId(&app_id);
std::string drm_certificate;
CryptoWrappedKey wrapped_private_key;
if (!file_handle_->StoreUsageInfo(
provider_session_token, key_request_, key_response_,
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id_, usage_entry_,
usage_entry_number_)) {
usage_entry_number_, drm_certificate, wrapped_private_key)) {
LOGE("Unable to store usage info");
// Usage info file is corrupt. Delete current usage entry and file.
if (usage_support_type_ == kUsageEntrySupport) {

View File

@@ -32,6 +32,7 @@ using video_widevine_client::sdk::License_LicenseState_ACTIVE;
using video_widevine_client::sdk::License_LicenseState_RELEASING;
using video_widevine_client::sdk::NameValue;
using video_widevine_client::sdk::UsageInfo;
using video_widevine_client::sdk::UsageInfo_DrmUsageCertificate;
using video_widevine_client::sdk::UsageInfo_ProviderSession;
using video_widevine_client::sdk::UsageTableInfo;
using video_widevine_client::sdk::UsageTableInfo_UsageEntryInfo;
@@ -134,7 +135,7 @@ bool ExtractFromDeviceCertificate(const DeviceCertificate& device_certificate,
if (!has_certificate && !has_key) return true;
// Flag if not a default certificate
if (!(has_certificate && has_key)) {
if (!has_certificate || !has_key) {
LOGE(
"Device certificate proto belongs to neither a default or legacy cert. "
"has_certificate: %s, has_key: %s",
@@ -142,6 +143,16 @@ bool ExtractFromDeviceCertificate(const DeviceCertificate& device_certificate,
return false;
}
if (device_certificate.certificate().empty() ||
device_certificate.wrapped_private_key().empty()) {
LOGE(
"Device certificate proto belongs does not have a valid certificate or "
"wrapped key. certificate size: %zu, wrapped key size: %zu",
device_certificate.certificate().size(),
device_certificate.wrapped_private_key().size());
return false;
}
*certificate = device_certificate.certificate();
private_key->Clear();
private_key->set_key(device_certificate.wrapped_private_key());
@@ -169,6 +180,112 @@ bool ExtractFromDeviceCertificate(const DeviceCertificate& device_certificate,
return true;
}
bool FindOrInsertUsageCertificate(
const std::string& drm_certificate,
const wvcdm::CryptoWrappedKey& wrapped_private_key, UsageInfo* usage_info,
uint32_t* drm_certificate_id) {
RETURN_FALSE_IF_NULL(usage_info);
RETURN_FALSE_IF_NULL(drm_certificate_id);
// Scan |drm_certificate_cache| for |drm_certificate|. If present,
// return the id
std::set<uint32_t> ids;
for (const UsageInfo_DrmUsageCertificate& drm_device_cert :
usage_info->drm_certificate_cache()) {
if (drm_device_cert.drm_certificate().certificate() == drm_certificate) {
*drm_certificate_id = drm_device_cert.drm_certificate_id();
return true;
}
ids.insert(drm_device_cert.drm_certificate_id());
}
uint32_t last_id = 0;
// |drm_certificate| is not in the cache. Find the first non-contiguous
// id number to insert
for (uint32_t id : ids) {
if (id > last_id + 1) {
break;
}
last_id = id;
}
if (ids.empty())
*drm_certificate_id = 0;
else
*drm_certificate_id = last_id + 1;
// Now insert into |drm_certificate_cache|
UsageInfo_DrmUsageCertificate* drm_usage_certificate =
usage_info->add_drm_certificate_cache();
drm_usage_certificate->set_drm_certificate_id(*drm_certificate_id);
return SetDeviceCertificate(drm_certificate, wrapped_private_key,
drm_usage_certificate->mutable_drm_certificate());
}
bool FindUsageCertificate(
uint32_t drm_certificate_id,
const google::protobuf::RepeatedPtrField<UsageInfo_DrmUsageCertificate>&
drm_certificate_cache,
std::string* drm_certificate,
wvcdm::CryptoWrappedKey* wrapped_private_key) {
for (const UsageInfo_DrmUsageCertificate& drm_usage_cert :
drm_certificate_cache) {
if (drm_usage_cert.drm_certificate_id() == drm_certificate_id) {
return ExtractFromDeviceCertificate(drm_usage_cert.drm_certificate(),
drm_certificate, wrapped_private_key);
}
}
LOGE("Unable to find any certificate in usage cache for entry: %d",
drm_certificate_id);
return false;
}
bool UsageCertificateCacheCleanUp(UsageInfo* usage_info) {
const google::protobuf::RepeatedPtrField<UsageInfo_ProviderSession>&
provider_sessions = usage_info->sessions();
google::protobuf::RepeatedPtrField<UsageInfo_DrmUsageCertificate>*
drm_certificate_cache = usage_info->mutable_drm_certificate_cache();
// Find all the DRM certificate ids in |drm_certificate_cache|
std::set<uint32_t> ids;
for (const UsageInfo_DrmUsageCertificate& drm_usage_cert :
*drm_certificate_cache) {
ids.insert(drm_usage_cert.drm_certificate_id());
}
// Next find all the DRM certificate ids in |provider_sessions|
std::set<uint32_t> session_ids;
for (const UsageInfo_ProviderSession& session : provider_sessions) {
session_ids.insert(session.drm_certificate_id());
}
// Now find all the entry numbers for DRM certificates in
// |drm_device_certificates| but not in |provider_sessions|. These need to
// be removed.
std::set<uint32_t> ids_to_erase;
std::set_difference(ids.begin(), ids.end(), session_ids.begin(),
session_ids.end(),
std::inserter(ids_to_erase, ids_to_erase.begin()));
const auto is_deletable =
[&ids_to_erase](
const UsageInfo_DrmUsageCertificate& usage_certificate) -> bool {
return std::find(ids_to_erase.cbegin(), ids_to_erase.cend(),
usage_certificate.drm_certificate_id()) !=
ids_to_erase.cend();
};
drm_certificate_cache->erase(
std::remove_if(drm_certificate_cache->begin(),
drm_certificate_cache->end(), is_deletable),
drm_certificate_cache->end());
return true;
}
} // namespace
namespace wvcdm {
@@ -522,7 +639,7 @@ bool DeviceFiles::StoreLicense(const CdmLicenseData& license_data,
}
license->set_usage_entry(license_data.usage_entry);
license->set_usage_entry_number(license_data.usage_entry_number);
if (license_data.drm_certificate.size() > 0) {
if (!license_data.drm_certificate.empty()) {
DeviceCertificate* device_certificate = license->mutable_drm_certificate();
if (!SetDeviceCertificate(license_data.drm_certificate,
license_data.wrapped_private_key,
@@ -685,13 +802,12 @@ bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) {
return true;
}
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const std::string& usage_info_file_name,
const std::string& key_set_id,
const std::string& usage_entry,
uint32_t usage_entry_number) {
bool DeviceFiles::StoreUsageInfo(
const std::string& provider_session_token, const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response, const std::string& usage_info_file_name,
const std::string& key_set_id, const std::string& usage_entry,
uint32_t usage_entry_number, const std::string& drm_certificate,
const CryptoWrappedKey& wrapped_private_key) {
RETURN_FALSE_IF_UNINITIALIZED();
video_widevine_client::sdk::File file;
if (!FileExists(usage_info_file_name)) {
@@ -715,6 +831,16 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
provider_session->set_usage_entry(usage_entry);
provider_session->set_usage_entry_number(usage_entry_number);
if (drm_certificate.size() > 0) {
uint32_t drm_certificate_id;
if (!FindOrInsertUsageCertificate(drm_certificate, wrapped_private_key,
usage_info, &drm_certificate_id)) {
LOGE("Unable to insert a certificate in the usage certificate cache");
return false;
}
provider_session->set_drm_certificate_id(drm_certificate_id);
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file) == kNoError;
@@ -822,6 +948,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name,
sessions->SwapElements(index, usage_info->sessions_size() - 1);
}
sessions->RemoveLast();
UsageCertificateCacheCleanUp(usage_info);
std::string serialized_file;
file.SerializeToString(&serialized_file);
@@ -884,6 +1011,7 @@ bool DeviceFiles::DeleteMultipleUsageInfoByKeySetIds(
}
if (sessions->size() > 0) {
UsageCertificateCacheCleanUp(file.mutable_usage_info());
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file) == kNoError;
@@ -898,41 +1026,21 @@ bool DeviceFiles::DeleteAllUsageInfo() {
kUsageInfoFileNameExt);
}
bool DeviceFiles::RetrieveUsageInfo(
const std::string& usage_info_file_name,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
RETURN_FALSE_IF_UNINITIALIZED();
RETURN_FALSE_IF_NULL(usage_info);
if (!FileExists(usage_info_file_name) ||
GetFileSize(usage_info_file_name) == 0) {
usage_info->resize(0);
return true;
}
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
LOGE("Unable to retrieve usage info file");
return false;
}
usage_info->resize(file.usage_info().sessions_size());
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
(*usage_info)[i] =
std::make_pair(file.usage_info().sessions(i).license_request(),
file.usage_info().sessions(i).license());
}
return true;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license,
std::string* usage_entry,
uint32_t* usage_entry_number) {
uint32_t* usage_entry_number,
std::string* drm_certificate,
CryptoWrappedKey* wrapped_private_key) {
RETURN_FALSE_IF_UNINITIALIZED();
RETURN_FALSE_IF_NULL(license_request);
RETURN_FALSE_IF_NULL(license);
RETURN_FALSE_IF_NULL(usage_entry);
RETURN_FALSE_IF_NULL(usage_entry_number);
RETURN_FALSE_IF_NULL(drm_certificate);
RETURN_FALSE_IF_NULL(wrapped_private_key);
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
@@ -948,6 +1056,20 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
if (!file.usage_info().sessions(index).has_drm_certificate_id()) {
drm_certificate->clear();
wrapped_private_key->Clear();
return true;
}
if (!FindUsageCertificate(
file.usage_info().sessions(index).drm_certificate_id(),
file.usage_info().drm_certificate_cache(), drm_certificate,
wrapped_private_key)) {
LOGE("Unable to find DRM certificate information from usage cache");
return false;
}
return true;
}
}
@@ -959,8 +1081,15 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId(
const std::string& usage_info_file_name, const std::string& key_set_id,
std::string* provider_session_token, CdmKeyMessage* license_request,
CdmKeyResponse* license, std::string* usage_entry,
uint32_t* usage_entry_number) {
uint32_t* usage_entry_number, std::string* drm_certificate,
CryptoWrappedKey* wrapped_private_key) {
RETURN_FALSE_IF_UNINITIALIZED();
RETURN_FALSE_IF_NULL(license_request);
RETURN_FALSE_IF_NULL(license);
RETURN_FALSE_IF_NULL(usage_entry);
RETURN_FALSE_IF_NULL(usage_entry_number);
RETURN_FALSE_IF_NULL(drm_certificate);
RETURN_FALSE_IF_NULL(wrapped_private_key);
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(usage_info_file_name, &file) != kNoError) {
@@ -977,6 +1106,20 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId(
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
if (!file.usage_info().sessions(index).has_drm_certificate_id()) {
drm_certificate->clear();
wrapped_private_key->Clear();
return true;
}
if (!FindUsageCertificate(
file.usage_info().sessions(index).drm_certificate_id(),
file.usage_info().drm_certificate_cache(), drm_certificate,
wrapped_private_key)) {
LOGE("Unable to find DRM certificate information from usage cache");
return false;
}
return true;
}
}
@@ -993,6 +1136,7 @@ bool DeviceFiles::StoreUsageInfo(const std::string& usage_info_file_name,
file.set_version(video_widevine_client::sdk::File::VERSION_1);
UsageInfo* usage_info = file.mutable_usage_info();
for (size_t i = 0; i < usage_data.size(); ++i) {
UsageInfo_ProviderSession* provider_session = usage_info->add_sessions();
@@ -1006,6 +1150,17 @@ bool DeviceFiles::StoreUsageInfo(const std::string& usage_info_file_name,
usage_data[i].key_set_id.size());
provider_session->set_usage_entry(usage_data[i].usage_entry);
provider_session->set_usage_entry_number(usage_data[i].usage_entry_number);
if (usage_data[i].drm_certificate.size() > 0) {
uint32_t drm_certificate_id;
if (!FindOrInsertUsageCertificate(usage_data[i].drm_certificate,
usage_data[i].wrapped_private_key,
usage_info, &drm_certificate_id)) {
LOGE("Unable to insert a certificate in the usage certificate cache");
return false;
}
provider_session->set_drm_certificate_id(drm_certificate_id);
}
}
std::string serialized_file;
@@ -1041,6 +1196,17 @@ bool DeviceFiles::UpdateUsageInfo(const std::string& usage_info_file_name,
provider_session->set_usage_entry(usage_data.usage_entry);
provider_session->set_usage_entry_number(usage_data.usage_entry_number);
if (usage_data.drm_certificate.size() > 0) {
uint32_t drm_certificate_id;
if (!FindOrInsertUsageCertificate(usage_data.drm_certificate,
usage_data.wrapped_private_key,
usage_info, &drm_certificate_id)) {
LOGE("Unable to find a certificate in to update the usage info");
return false;
}
provider_session->set_drm_certificate_id(drm_certificate_id);
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file) ==
@@ -1079,6 +1245,19 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
(*usage_data)[i].usage_entry = file.usage_info().sessions(i).usage_entry();
(*usage_data)[i].usage_entry_number =
file.usage_info().sessions(i).usage_entry_number();
if (!file.usage_info().sessions(i).has_drm_certificate_id()) {
(*usage_data)[i].drm_certificate.clear();
(*usage_data)[i].wrapped_private_key.Clear();
} else {
if (!FindUsageCertificate(
file.usage_info().sessions(i).drm_certificate_id(),
file.usage_info().drm_certificate_cache(),
&(*usage_data)[i].drm_certificate,
&(*usage_data)[i].wrapped_private_key)) {
LOGW("Unable to find DRM certificate information from usage cache");
}
}
}
return true;
@@ -1108,6 +1287,20 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
usage_data->usage_entry = file.usage_info().sessions(index).usage_entry();
usage_data->usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
if (!file.usage_info().sessions(index).has_drm_certificate_id()) {
usage_data->drm_certificate.clear();
usage_data->wrapped_private_key.Clear();
return true;
}
if (!FindUsageCertificate(
file.usage_info().sessions(index).drm_certificate_id(),
file.usage_info().drm_certificate_cache(),
&usage_data->drm_certificate, &usage_data->wrapped_private_key)) {
LOGE("Unable to find DRM certificate information from usage cache");
return false;
}
return true;
}
}

View File

@@ -80,17 +80,17 @@ message UsageInfo {
optional int64 usage_entry_number = 6;
// If not present, use the legacy DRM certificate rather than
// one in DrmDeviceCertificate
optional int32 drm_certificate_entry_number = 7;
optional uint32 drm_certificate_id = 7;
}
// A cache of DeviceCertificates associated with usage entries
message DrmDeviceCertificate {
optional int32 drm_certificate_entry_number = 1;
message DrmUsageCertificate {
optional uint32 drm_certificate_id = 1;
optional DeviceCertificate drm_certificate = 2;
}
repeated ProviderSession sessions = 1;
repeated DrmDeviceCertificate drm_device_certificates = 2;
repeated DrmUsageCertificate drm_certificate_cache = 2;
}
message HlsAttributes {

View File

@@ -118,10 +118,12 @@ bool RetrieveUsageInfoLicense(DeviceFiles* device_files,
CdmUsageEntry usage_entry;
std::string provider_session_token;
CdmKeyMessage license_request;
std::string drm_certificate;
CryptoWrappedKey wrapped_private_key;
if (!device_files->RetrieveUsageInfoByKeySetId(
usage_info_file_name, key_set_id, &provider_session_token,
&license_request, license_message, &usage_entry,
usage_entry_number)) {
&license_request, license_message, &usage_entry, usage_entry_number,
&drm_certificate, &wrapped_private_key)) {
LOGW(
"Failed to retrieve usage information: "
"key_set_id = %s, usage_info_file_name = %s",
@@ -598,12 +600,14 @@ CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number,
std::string provider_session_token;
CdmKeyMessage license_request;
CdmKeyResponse license;
std::string drm_certificate;
CryptoWrappedKey wrapped_private_key;
if (!device_files->RetrieveUsageInfoByKeySetId(
usage_entry_info_[usage_entry_number].usage_info_file_name,
usage_entry_info_[usage_entry_number].key_set_id,
&provider_session_token, &license_request, &license, usage_entry,
&entry_number)) {
&entry_number, &drm_certificate, &wrapped_private_key)) {
LOGE("Failed to retrieve usage information");
return USAGE_GET_ENTRY_RETRIEVE_USAGE_INFO_FAILED;
}
@@ -667,11 +671,13 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
uint32_t entry_number;
std::string provider_session_token, init_data, key_request, key_response,
key_renewal_request;
std::string drm_certificate;
CryptoWrappedKey wrapped_private_key;
if (!device_files->RetrieveUsageInfoByKeySetId(
usage_entry_info_[usage_entry_number].usage_info_file_name,
usage_entry_info_[usage_entry_number].key_set_id,
&provider_session_token, &key_request, &key_response, &entry,
&entry_number)) {
&entry_number, &drm_certificate, &wrapped_private_key)) {
LOGE("Failed to retrieve usage information");
return USAGE_STORE_ENTRY_RETRIEVE_USAGE_INFO_FAILED;
}
@@ -682,7 +688,7 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
provider_session_token, key_request, key_response,
usage_entry_info_[usage_entry_number].usage_info_file_name,
usage_entry_info_[usage_entry_number].key_set_id, usage_entry,
usage_entry_number)) {
usage_entry_number, drm_certificate, wrapped_private_key)) {
LOGE("Failed to store usage information");
return USAGE_STORE_USAGE_INFO_FAILED;
}