[ Merge of http://go/wvgerrit/22565 ] When using the grace period, the CDM will need to override the values given to use by the TEE (through OEMCrypto). Normally the first (and last) decrypt times are stored securely by the TEE. To avoid extra complexity in OEMCrypto, we will simply ignore the values given to us by the TEE when using this feature. However, the TEE will still enforce the (hard) license duration. So only the rental/playback durations will be affected by malicious editing of files. b/34211676 Test: Reran unittests including newly added tests. All tests other than some oemcrypto, request_license_test passed. Those tests failed with or without this CL. Change-Id: I6d7b5bfb669fd8603b474b68c2f7175b0c30901d
824 lines
25 KiB
C++
824 lines
25 KiB
C++
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
|
|
#include "device_files.h"
|
|
|
|
#include <string.h>
|
|
#include <string>
|
|
|
|
#include "file_store.h"
|
|
#include "log.h"
|
|
#include "properties.h"
|
|
#include "string_conversions.h"
|
|
#include "wv_cdm_constants.h"
|
|
|
|
#if defined(__APPLE__)
|
|
#include <CommonCrypto/CommonDigest.h>
|
|
#define SHA256 CC_SHA256
|
|
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
|
#define MD5 CC_MD5
|
|
#define MD5_DIGEST_LENGTH CC_MD5_DIGEST_LENGTH
|
|
#else
|
|
#include <openssl/md5.h>
|
|
#include <openssl/sha.h>
|
|
#endif
|
|
|
|
// Protobuf generated classes.
|
|
using video_widevine_client::sdk::DeviceCertificate;
|
|
using video_widevine_client::sdk::HashedFile;
|
|
using video_widevine_client::sdk::HlsAttributes;
|
|
using video_widevine_client::sdk::HlsAttributes_Method_AES_128;
|
|
using video_widevine_client::sdk::HlsAttributes_Method_SAMPLE_AES;
|
|
using video_widevine_client::sdk::License;
|
|
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_ProviderSession;
|
|
|
|
namespace {
|
|
|
|
const char kCertificateFileName[] = "cert.bin";
|
|
const char kHlsAttributesFileNameExt[] = ".hal";
|
|
const char kUsageInfoFileNamePrefix[] = "usage";
|
|
const char kUsageInfoFileNameExt[] = ".bin";
|
|
const char kLicenseFileNameExt[] = ".lic";
|
|
const char kEmptyFileName[] = "";
|
|
const char kWildcard[] = "*";
|
|
|
|
bool Hash(const std::string& data, std::string* hash) {
|
|
if (!hash) return false;
|
|
hash->resize(SHA256_DIGEST_LENGTH);
|
|
|
|
const unsigned char* input =
|
|
reinterpret_cast<const unsigned char*>(data.data());
|
|
unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
|
|
SHA256(input, data.size(), output);
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace wvcdm {
|
|
|
|
// static
|
|
std::set<std::string> DeviceFiles::reserved_license_ids_;
|
|
|
|
DeviceFiles::DeviceFiles(FileSystem* file_system)
|
|
: file_system_(file_system),
|
|
security_level_(kSecurityLevelUninitialized),
|
|
initialized_(false) {}
|
|
|
|
DeviceFiles::~DeviceFiles() {}
|
|
|
|
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
|
if (!file_system_) {
|
|
LOGD("DeviceFiles::Init: Invalid FileSystem given.");
|
|
return false;
|
|
}
|
|
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level, &path)) {
|
|
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
|
|
return false;
|
|
}
|
|
security_level_ = security_level;
|
|
initialized_ = true;
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
|
const std::string& wrapped_private_key) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::StoreCertificate: not initialized");
|
|
return false;
|
|
}
|
|
|
|
// Fill in file information
|
|
video_widevine_client::sdk::File file;
|
|
|
|
file.set_type(video_widevine_client::sdk::File::DEVICE_CERTIFICATE);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
DeviceCertificate* device_certificate = file.mutable_device_certificate();
|
|
device_certificate->set_certificate(certificate);
|
|
device_certificate->set_wrapped_private_key(wrapped_private_key);
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
|
|
return StoreFileWithHash(GetCertificateFileName(), serialized_file);
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
|
std::string* wrapped_private_key) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
|
|
return false;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (!RetrieveHashedFile(GetCertificateFileName(), &file)) {
|
|
return false;
|
|
}
|
|
|
|
if (file.type() != video_widevine_client::sdk::File::DEVICE_CERTIFICATE) {
|
|
LOGW("DeviceFiles::RetrieveCertificate: Incorrect file type");
|
|
return false;
|
|
}
|
|
|
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
|
LOGW("DeviceFiles::RetrieveCertificate: Incorrect file version");
|
|
return false;
|
|
}
|
|
|
|
if (!file.has_device_certificate()) {
|
|
LOGW("DeviceFiles::RetrieveCertificate: Certificate not present");
|
|
return false;
|
|
}
|
|
|
|
DeviceCertificate device_certificate = file.device_certificate();
|
|
|
|
*certificate = device_certificate.certificate();
|
|
*wrapped_private_key = device_certificate.wrapped_private_key();
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::HasCertificate() {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::HasCertificate: not initialized");
|
|
return false;
|
|
}
|
|
|
|
return FileExists(GetCertificateFileName());
|
|
}
|
|
|
|
bool DeviceFiles::RemoveCertificate() {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::RemoveCertificate: not initialized");
|
|
return false;
|
|
}
|
|
|
|
return RemoveFile(GetCertificateFileName());
|
|
}
|
|
|
|
bool DeviceFiles::StoreLicense(
|
|
const std::string& key_set_id, const LicenseState state,
|
|
const CdmInitData& pssh_data, const CdmKeyMessage& license_request,
|
|
const CdmKeyResponse& license_message,
|
|
const CdmKeyMessage& license_renewal_request,
|
|
const CdmKeyResponse& license_renewal,
|
|
const std::string& release_server_url, int64_t playback_start_time,
|
|
int64_t last_playback_time, int64_t grace_period_end_time,
|
|
const CdmAppParameterMap& app_parameters) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::StoreLicense: not initialized");
|
|
return false;
|
|
}
|
|
|
|
// Fill in file information
|
|
video_widevine_client::sdk::File file;
|
|
|
|
file.set_type(video_widevine_client::sdk::File::LICENSE);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
License* license = file.mutable_license();
|
|
switch (state) {
|
|
case kLicenseStateActive:
|
|
license->set_state(License_LicenseState_ACTIVE);
|
|
break;
|
|
case kLicenseStateReleasing:
|
|
license->set_state(License_LicenseState_RELEASING);
|
|
break;
|
|
default:
|
|
LOGW("DeviceFiles::StoreLicense: Unknown license state: %u", state);
|
|
return false;
|
|
break;
|
|
}
|
|
license->set_pssh_data(pssh_data);
|
|
license->set_license_request(license_request);
|
|
license->set_license(license_message);
|
|
license->set_renewal_request(license_renewal_request);
|
|
license->set_renewal(license_renewal);
|
|
license->set_release_server_url(release_server_url);
|
|
license->set_playback_start_time(playback_start_time);
|
|
license->set_last_playback_time(last_playback_time);
|
|
license->set_grace_period_end_time(grace_period_end_time);
|
|
NameValue* app_params;
|
|
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
|
|
iter != app_parameters.end(); ++iter) {
|
|
app_params = license->add_app_parameters();
|
|
app_params->set_name(iter->first);
|
|
app_params->set_value(iter->second);
|
|
}
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
|
|
reserved_license_ids_.erase(key_set_id);
|
|
return StoreFileWithHash(key_set_id + kLicenseFileNameExt, serialized_file);
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveLicense(
|
|
const std::string& key_set_id, LicenseState* state, CdmInitData* pssh_data,
|
|
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
|
|
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
|
|
std::string* release_server_url, int64_t* playback_start_time,
|
|
int64_t* last_playback_time, int64_t* grace_period_end_time,
|
|
CdmAppParameterMap* app_parameters) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::RetrieveLicense: not initialized");
|
|
return false;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (!RetrieveHashedFile(key_set_id + kLicenseFileNameExt, &file)) {
|
|
return false;
|
|
}
|
|
|
|
if (file.type() != video_widevine_client::sdk::File::LICENSE) {
|
|
LOGW("DeviceFiles::RetrieveLicense: Incorrect file type");
|
|
return false;
|
|
}
|
|
|
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
|
LOGW("DeviceFiles::RetrieveLicense: Incorrect file version");
|
|
return false;
|
|
}
|
|
|
|
if (!file.has_license()) {
|
|
LOGW("DeviceFiles::RetrieveLicense: License not present");
|
|
return false;
|
|
}
|
|
|
|
License license = file.license();
|
|
|
|
switch (license.state()) {
|
|
case License_LicenseState_ACTIVE:
|
|
*state = kLicenseStateActive;
|
|
break;
|
|
case License_LicenseState_RELEASING:
|
|
*state = kLicenseStateReleasing;
|
|
break;
|
|
default:
|
|
LOGW("DeviceFiles::RetrieveLicense: Unrecognized license state: %u",
|
|
kLicenseStateUnknown);
|
|
*state = kLicenseStateUnknown;
|
|
break;
|
|
}
|
|
*pssh_data = license.pssh_data();
|
|
*license_request = license.license_request();
|
|
*license_message = license.license();
|
|
*license_renewal_request = license.renewal_request();
|
|
*license_renewal = license.renewal();
|
|
*release_server_url = license.release_server_url();
|
|
*playback_start_time = license.playback_start_time();
|
|
*last_playback_time = license.last_playback_time();
|
|
*grace_period_end_time = license.grace_period_end_time();
|
|
for (int i = 0; i < license.app_parameters_size(); ++i) {
|
|
(*app_parameters)[license.app_parameters(i).name()] =
|
|
license.app_parameters(i).value();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::DeleteLicense: not initialized");
|
|
return false;
|
|
}
|
|
return RemoveFile(key_set_id + kLicenseFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::DeleteAllLicenses() {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
|
|
return false;
|
|
}
|
|
return RemoveFile(std::string(kWildcard) + kLicenseFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::DeleteAllFiles() {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::DeleteAllFiles: not initialized");
|
|
return false;
|
|
}
|
|
|
|
// We pass an empty string to RemoveFile to delete the device files base
|
|
// directory itself.
|
|
// TODO[gmorgan]: verify RemoveFile("") should remove all files.
|
|
return RemoveFile(kEmptyFileName);
|
|
}
|
|
|
|
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::LicenseExists: not initialized");
|
|
return false;
|
|
}
|
|
return reserved_license_ids_.count(key_set_id) ||
|
|
FileExists(key_set_id + kLicenseFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::ReserveLicenseId: not initialized");
|
|
return false;
|
|
}
|
|
reserved_license_ids_.insert(key_set_id);
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::UnreserveLicenseId: not initialized");
|
|
return false;
|
|
}
|
|
reserved_license_ids_.erase(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& app_id,
|
|
const std::string& key_set_id) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
|
|
return false;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
if (!FileExists(file_name)) {
|
|
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
} else {
|
|
if (!RetrieveHashedFile(file_name, &file)) {
|
|
LOGW("DeviceFiles::StoreUsageInfo: Unable to parse file");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UsageInfo* usage_info = file.mutable_usage_info();
|
|
UsageInfo_ProviderSession* provider_session = usage_info->add_sessions();
|
|
|
|
provider_session->set_token(provider_session_token.data(),
|
|
provider_session_token.size());
|
|
provider_session->set_license_request(key_request.data(), key_request.size());
|
|
provider_session->set_license(key_response.data(), key_response.size());
|
|
provider_session->set_key_set_id(key_set_id.data(), key_set_id.size());
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
return StoreFileWithHash(file_name, serialized_file);
|
|
}
|
|
|
|
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
|
|
const std::string& provider_session_token) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
|
return false;
|
|
}
|
|
video_widevine_client::sdk::File file;
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
if (!RetrieveHashedFile(file_name, &file)) return false;
|
|
|
|
UsageInfo* usage_info = file.mutable_usage_info();
|
|
int index = 0;
|
|
bool found = false;
|
|
for (; index < usage_info->sessions_size(); ++index) {
|
|
if (usage_info->sessions(index).token() == provider_session_token) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
LOGW(
|
|
"DeviceFiles::DeleteUsageInfo: Unable to find provider session "
|
|
"token: %s",
|
|
b2a_hex(provider_session_token).c_str());
|
|
return false;
|
|
}
|
|
|
|
google::protobuf::RepeatedPtrField<UsageInfo_ProviderSession>* sessions =
|
|
usage_info->mutable_sessions();
|
|
if (index < usage_info->sessions_size() - 1) {
|
|
sessions->SwapElements(index, usage_info->sessions_size() - 1);
|
|
}
|
|
sessions->RemoveLast();
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
return StoreFileWithHash(file_name, serialized_file);
|
|
}
|
|
|
|
bool DeviceFiles::DeleteAllUsageInfoForApp(
|
|
const std::string& app_id,
|
|
std::vector<std::string>* provider_session_tokens) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
|
|
return false;
|
|
}
|
|
if (NULL == provider_session_tokens) {
|
|
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: pst destination not provided");
|
|
return false;
|
|
}
|
|
provider_session_tokens->clear();
|
|
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
if (!FileExists(file_name)) return true;
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (RetrieveHashedFile(file_name, &file)) {
|
|
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
|
|
provider_session_tokens->push_back(file.usage_info().sessions(i).token());
|
|
}
|
|
} else {
|
|
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file");
|
|
}
|
|
return RemoveFile(file_name);
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveUsageInfo(
|
|
const std::string& app_id,
|
|
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
|
return false;
|
|
}
|
|
|
|
if (NULL == usage_info) {
|
|
LOGW(
|
|
"DeviceFiles::RetrieveUsageInfo: license destination not "
|
|
"provided");
|
|
return false;
|
|
}
|
|
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
|
|
usage_info->resize(0);
|
|
return true;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (!RetrieveHashedFile(file_name, &file)) {
|
|
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse 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& app_id,
|
|
const std::string& provider_session_token,
|
|
CdmKeyMessage* license_request,
|
|
CdmKeyResponse* license_response) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
|
return false;
|
|
}
|
|
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
video_widevine_client::sdk::File file;
|
|
if (!RetrieveHashedFile(file_name, &file)) {
|
|
return false;
|
|
}
|
|
|
|
int index = 0;
|
|
for (; index < file.usage_info().sessions_size(); ++index) {
|
|
if (file.usage_info().sessions(index).token() == provider_session_token) {
|
|
*license_request = file.usage_info().sessions(index).license_request();
|
|
*license_response = file.usage_info().sessions(index).license();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveUsageInfoByKeySetId(
|
|
const std::string& app_id,
|
|
const std::string& key_set_id,
|
|
CdmKeyMessage* license_request,
|
|
CdmKeyResponse* license_response) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: not initialized");
|
|
return false;
|
|
}
|
|
|
|
std::string file_name = GetUsageInfoFileName(app_id);
|
|
video_widevine_client::sdk::File file;
|
|
if (!RetrieveHashedFile(file_name, &file)) {
|
|
return false;
|
|
}
|
|
|
|
int index = 0;
|
|
for (; index < file.usage_info().sessions_size(); ++index) {
|
|
if (file.usage_info().sessions(index).key_set_id() == key_set_id) {
|
|
*license_request = file.usage_info().sessions(index).license_request();
|
|
*license_response = file.usage_info().sessions(index).license();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DeviceFiles::StoreHlsAttributes(
|
|
const std::string& key_set_id, const CdmHlsMethod method,
|
|
const std::vector<uint8_t>& media_segment_iv) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::StoreHlsAttributes: not initialized");
|
|
return false;
|
|
}
|
|
|
|
// Fill in file information
|
|
video_widevine_client::sdk::File file;
|
|
|
|
file.set_type(video_widevine_client::sdk::File::HLS_ATTRIBUTES);
|
|
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
|
|
|
HlsAttributes* hls_attributes = file.mutable_hls_attributes();
|
|
switch (method) {
|
|
case kHlsMethodAes128:
|
|
hls_attributes->set_method(HlsAttributes_Method_AES_128);
|
|
break;
|
|
case kHlsMethodSampleAes:
|
|
hls_attributes->set_method(HlsAttributes_Method_SAMPLE_AES);
|
|
break;
|
|
case kHlsMethodNone:
|
|
default:
|
|
LOGW("DeviceFiles::StoreHlsAttributeInfo: Unknown HLS method: %u",
|
|
method);
|
|
return false;
|
|
break;
|
|
}
|
|
hls_attributes->set_media_segment_iv(&media_segment_iv[0],
|
|
media_segment_iv.size());
|
|
|
|
std::string serialized_file;
|
|
file.SerializeToString(&serialized_file);
|
|
|
|
return StoreFileWithHash(key_set_id + kHlsAttributesFileNameExt,
|
|
serialized_file);
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveHlsAttributes(
|
|
const std::string& key_set_id, CdmHlsMethod* method,
|
|
std::vector<uint8_t>* media_segment_iv) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::RetrieveHlsAttributes: not initialized");
|
|
return false;
|
|
}
|
|
|
|
video_widevine_client::sdk::File file;
|
|
if (!RetrieveHashedFile(key_set_id + kHlsAttributesFileNameExt, &file)) {
|
|
return false;
|
|
}
|
|
|
|
if (file.type() != video_widevine_client::sdk::File::HLS_ATTRIBUTES) {
|
|
LOGW("DeviceFiles::RetrieveHlsAttributes: Incorrect file type: %u",
|
|
file.type());
|
|
return false;
|
|
}
|
|
|
|
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
|
LOGW("DeviceFiles::RetrieveHlsAttributes: Incorrect file version: %u",
|
|
file.version());
|
|
return false;
|
|
}
|
|
|
|
if (!file.has_hls_attributes()) {
|
|
LOGW("DeviceFiles::RetrieveHlsAttributes: HLS attributes not present");
|
|
return false;
|
|
}
|
|
|
|
HlsAttributes attributes = file.hls_attributes();
|
|
|
|
switch (attributes.method()) {
|
|
case HlsAttributes_Method_AES_128:
|
|
*method = kHlsMethodAes128;
|
|
break;
|
|
case HlsAttributes_Method_SAMPLE_AES:
|
|
*method = kHlsMethodSampleAes;
|
|
break;
|
|
default:
|
|
LOGW("DeviceFiles::RetrieveHlsAttributes: Unrecognized HLS method: %u",
|
|
attributes.method());
|
|
*method = kHlsMethodNone;
|
|
break;
|
|
}
|
|
media_segment_iv->assign(attributes.media_segment_iv().begin(),
|
|
attributes.media_segment_iv().end());
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) {
|
|
if (!initialized_) {
|
|
LOGW("DeviceFiles::DeleteHlsAttributes: not initialized");
|
|
return false;
|
|
}
|
|
return RemoveFile(key_set_id + kHlsAttributesFileNameExt);
|
|
}
|
|
|
|
bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
|
const std::string& serialized_file) {
|
|
// calculate SHA hash
|
|
std::string hash;
|
|
if (!Hash(serialized_file, &hash)) {
|
|
LOGW("DeviceFiles::StoreFileWithHash: Hash computation failed");
|
|
return false;
|
|
}
|
|
|
|
// Fill in hashed file data
|
|
HashedFile hash_file;
|
|
hash_file.set_file(serialized_file);
|
|
hash_file.set_hash(hash);
|
|
|
|
std::string serialized_hash_file;
|
|
hash_file.SerializeToString(&serialized_hash_file);
|
|
|
|
return StoreFileRaw(name, serialized_hash_file);
|
|
}
|
|
|
|
bool DeviceFiles::StoreFileRaw(const std::string& name,
|
|
const std::string& serialized_file) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
|
|
return false;
|
|
}
|
|
|
|
path += name;
|
|
|
|
File* file =
|
|
file_system_->Open(path, FileSystem::kCreate | FileSystem::kTruncate);
|
|
if (!file) {
|
|
LOGW("DeviceFiles::StoreFileRaw: File open failed: %s", path.c_str());
|
|
return false;
|
|
}
|
|
|
|
ssize_t bytes = file->Write(serialized_file.data(), serialized_file.size());
|
|
file->Close();
|
|
|
|
if (bytes != static_cast<ssize_t>(serialized_file.size())) {
|
|
LOGW(
|
|
"DeviceFiles::StoreFileRaw: write failed: (actual: %d, "
|
|
"expected: %d)",
|
|
bytes, serialized_file.size());
|
|
return false;
|
|
}
|
|
|
|
LOGV("DeviceFiles::StoreFileRaw: success: %s (%db)", path.c_str(),
|
|
serialized_file.size());
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::RetrieveHashedFile(
|
|
const std::string& name,
|
|
video_widevine_client::sdk::File* deserialized_file) {
|
|
std::string serialized_file;
|
|
|
|
if (!deserialized_file) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file parameter");
|
|
return false;
|
|
}
|
|
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGW("DeviceFiles::StoreFileWithHash: Unable to get base path");
|
|
return false;
|
|
}
|
|
|
|
path += name;
|
|
|
|
if (!file_system_->Exists(path)) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: %s does not exist", path.c_str());
|
|
return false;
|
|
}
|
|
|
|
ssize_t bytes = file_system_->FileSize(path);
|
|
if (bytes <= 0) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s",
|
|
path.c_str());
|
|
// Remove the corrupted file so the caller will not get the same error
|
|
// when trying to access the file repeatedly, causing the system to stall.
|
|
file_system_->Remove(path);
|
|
return false;
|
|
}
|
|
|
|
File* file = file_system_->Open(path, FileSystem::kReadOnly);
|
|
if (!file) {
|
|
return false;
|
|
}
|
|
|
|
std::string serialized_hash_file;
|
|
serialized_hash_file.resize(bytes);
|
|
bytes = file->Read(&serialized_hash_file[0], serialized_hash_file.size());
|
|
file->Close();
|
|
|
|
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: read failed");
|
|
return false;
|
|
}
|
|
|
|
LOGV("DeviceFiles::RetrieveHashedFile: success: %s (%db)", path.c_str(),
|
|
serialized_hash_file.size());
|
|
|
|
HashedFile hash_file;
|
|
if (!hash_file.ParseFromString(serialized_hash_file)) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse hash file");
|
|
return false;
|
|
}
|
|
|
|
std::string hash;
|
|
if (!Hash(hash_file.file(), &hash)) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: Hash computation failed");
|
|
return false;
|
|
}
|
|
|
|
if (hash != hash_file.hash()) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
|
|
// Remove the corrupted file so the caller will not get the same error
|
|
// when trying to access the file repeatedly, causing the system to stall.
|
|
file_system_->Remove(path);
|
|
return false;
|
|
}
|
|
|
|
if (!deserialized_file->ParseFromString(hash_file.file())) {
|
|
LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse file");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DeviceFiles::FileExists(const std::string& name) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGW("DeviceFiles::FileExists: Unable to get base path");
|
|
return false;
|
|
}
|
|
path += name;
|
|
|
|
return file_system_->Exists(path);
|
|
}
|
|
|
|
bool DeviceFiles::RemoveFile(const std::string& name) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
|
|
return false;
|
|
}
|
|
path += name;
|
|
|
|
return file_system_->Remove(path);
|
|
}
|
|
|
|
ssize_t DeviceFiles::GetFileSize(const std::string& name) {
|
|
std::string path;
|
|
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
|
LOGW("DeviceFiles::GetFileSize: Unable to get base path");
|
|
return -1;
|
|
}
|
|
path += name;
|
|
|
|
return file_system_->FileSize(path);
|
|
}
|
|
|
|
std::string DeviceFiles::GetCertificateFileName() {
|
|
return kCertificateFileName;
|
|
}
|
|
|
|
std::string DeviceFiles::GetHlsAttributesFileNameExtension() {
|
|
return kHlsAttributesFileNameExt;
|
|
}
|
|
|
|
std::string DeviceFiles::GetLicenseFileNameExtension() {
|
|
return kLicenseFileNameExt;
|
|
}
|
|
|
|
std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
|
|
std::string hash;
|
|
if (app_id != "") {
|
|
hash = GetFileNameSafeHash(app_id);
|
|
}
|
|
return kUsageInfoFileNamePrefix + hash + kUsageInfoFileNameExt;
|
|
}
|
|
|
|
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
|
|
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
|
|
const unsigned char* input_ptr =
|
|
reinterpret_cast<const unsigned char*>(input.data());
|
|
MD5(input_ptr, input.size(), &hash[0]);
|
|
return wvcdm::Base64SafeEncode(hash);
|
|
}
|
|
|
|
} // namespace wvcdm
|