Merges to android Pi release (part 3)

These are a set of CLs merged from the wv cdm repo to the android repo.

* Add CDM status return for decrypt blocked by HDCP.

  Author: Gene Morgan <gmorgan@google.com>

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

  New status code is kKeyUsageBlockedByPolicy. It is returned by the decrypt()
  call instead of kDecryptError or kNoKey.

  Also shuffled the CDM status returns to define the EME-aligned codes
  first, and added comments to highlight the differences in handling.

  BUG: 37540672

* Change division and mod ops to relocatables

  Author: Srujan Gaddam <srujzs@google.com>

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

  This is similar to I2dad1028acf295288cd10817a2bcff2513c053c9.
  We should be using the relocatable functions instead of the
  native division and mod operations.

* Cleanup Encrypted ClientID in provisioning request

  Author: Gene Morgan <gmorgan@google.com>

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

  b/36897239

  Staging server does not support it (or the client is not constructing
  it properly).  Leave it disabled pending investigation.

* Certificate Provisioning fixes.

  Author: Gene Morgan <gmorgan@google.com>

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

  Partial fix for BUG: 37482676
  Partial fix for BUG: 37481392

  Update service certificates, get rid of DEV/QA root certificate.
  Provisioning request and response are base64 (web-safe) encoded.
  Response is optionally JSON-wrapped.

  Change ConfigTestEnv; clearer comments and a closer match to reality.

BUG: 71650075
Test: Not currently passing. Will be addressed in a subsequent
      commit in the chain.

Change-Id: I79d3c4bf1124e5e0d3e4d40baead65a8266ea874
This commit is contained in:
Rahul Frias
2018-01-09 15:43:41 -08:00
parent 387147dffe
commit 11068accd2
30 changed files with 902 additions and 920 deletions

View File

@@ -29,17 +29,13 @@ class DeviceFiles {
kLicenseStateUnknown,
} LicenseState;
typedef enum {
kStorageLicense, // persistent license
kStorageUsageInfo, // secure stop
} UsageEntryStorage;
struct UsageEntryInfo {
UsageEntryStorage storage_type;
std::string key_set_id; // used when storage_type is kStorageLicense
std::string
provider_session_token; // used when storage_type is kStorageUsageInfo
std::string app_id; // used when storage_type is kStorageUsageInfo
struct CdmUsageData {
std::string provider_session_token;
CdmKeyMessage license_request;
CdmKeyResponse license;
std::string key_set_id;
CdmUsageEntry usage_entry;
uint32_t usage_entry_number;
};
DeviceFiles(FileSystem*);
@@ -71,7 +67,8 @@ class DeviceFiles {
int64_t last_playback_time,
int64_t grace_period_end_time,
const CdmAppParameterMap& app_parameters,
const CdmUsageEntry& usage_entry);
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number);
virtual bool RetrieveLicense(
const std::string& key_set_id, LicenseState* state,
CdmInitData* pssh_data, CdmKeyMessage* key_request,
@@ -79,7 +76,7 @@ class DeviceFiles {
CdmKeyResponse* key_renewal_response, std::string* release_server_url,
int64_t* playback_start_time, int64_t* last_playback_time,
int64_t* grace_period_end_time, CdmAppParameterMap* app_parameters,
CdmUsageEntry* usage_entry);
CdmUsageEntry* usage_entry, uint32_t* usage_entry_number);
virtual bool DeleteLicense(const std::string& key_set_id);
virtual bool ListLicenses(std::vector<std::string>* key_set_ids);
virtual bool DeleteAllFiles();
@@ -88,13 +85,25 @@ class DeviceFiles {
virtual bool ReserveLicenseId(const std::string& key_set_id);
virtual bool UnreserveLicenseId(const std::string& key_set_id);
// Store a usage record to the set of usage information on the file system.
// Use this method to create a |usage_info_file_name| from an |app_id|
static std::string GetUsageInfoFileName(const std::string& app_id);
// The UsageInfo methods have been revised to use |usage_info_file_name|
// rather than |app_id| as a parameter. Use the helper method above to
// translate.
// OEMCrypto API 13 introduced big usage tables which required
// migration from usage tables stored by the TEE to usage table
// header+usage entries stored in unsecured persistent storage. The upgrade
// required creation of reverse lookup tables (CdmUsageEntryInfo).
// |app_id| however was hashed and unextractable, and necessitated the
// switch to |usage_info_file_name|
virtual bool StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const std::string& app_id,
const std::string& usage_info_file_name,
const std::string& key_set_id,
const CdmUsageEntry& usage_entry);
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number);
// Extract KSIDs from usage information on the file system.
virtual bool ListUsageRecords(const std::string& app_id,
@@ -105,37 +114,49 @@ class DeviceFiles {
const std::string& key_set_id,
std::string* provider_session_token);
// Delete the usage record for the given PST.
virtual bool DeleteUsageInfo(const std::string& app_id,
virtual bool DeleteUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token);
// Delete usage information from the file system. Puts a list of all the
// psts that were deleted from the file into |provider_session_tokens|.
virtual bool DeleteAllUsageInfoForApp(
const std::string& app_id,
const std::string& usage_info_file_name,
std::vector<std::string>* provider_session_tokens);
// Retrieve one usage info from the file. Subsequent calls will retrieve
// subsequent entries in the table for this app_id.
virtual bool RetrieveUsageInfo(
const std::string& app_id,
const std::string& usage_info_file_name,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
// Retrieve the usage info entry specified by |provider_session_token|.
// Returns false if the entry could not be found.
virtual bool RetrieveUsageInfo(const std::string& app_id,
virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry);
CdmUsageEntry* usage_entry,
uint32_t* usage_entry_number);
// Retrieve the usage info entry specified by |key_set_id|.
// Returns false if the entry could not be found.
virtual bool RetrieveUsageInfoByKeySetId(const std::string& app_id,
const std::string& key_set_id,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry);
virtual bool RetrieveUsageInfoByKeySetId(
const std::string& usage_info_file_name,
const std::string& key_set_id,
std::string* provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry,
uint32_t* usage_entry_number);
// These APIs support upgrading from usage tables to usage tabler header +
// entries introduced in OEMCrypto V13.
virtual bool ListUsageInfoFiles(std::vector<std::string>* usage_file_names);
virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name,
std::vector<CdmUsageData>* usage_data);
// This method overwrites rather than appends data to the usage file
virtual bool StoreUsageInfo(const std::string& usage_info_file_name,
const std::vector<CdmUsageData>& usage_data);
virtual bool StoreHlsAttributes(const std::string& key_set_id,
const CdmHlsMethod method,
@@ -147,11 +168,11 @@ class DeviceFiles {
virtual bool StoreUsageTableInfo(
const CdmUsageTableHeader& usage_table_header,
const std::vector<UsageEntryInfo>& usage_entry_info);
const std::vector<CdmUsageEntryInfo>& usage_entry_info);
virtual bool RetrieveUsageTableInfo(
CdmUsageTableHeader* usage_table_header,
std::vector<UsageEntryInfo>* usage_entry_info);
std::vector<CdmUsageEntryInfo>* usage_entry_info);
private:
// Extract serial number and system ID from DRM Device certificate
@@ -175,7 +196,6 @@ class DeviceFiles {
static std::string GetCertificateFileName();
static std::string GetHlsAttributesFileNameExtension();
static std::string GetLicenseFileNameExtension();
static std::string GetUsageInfoFileName(const std::string& app_id);
static std::string GetUsageTableFileName();
static std::string GetFileNameSafeHash(const std::string& input);

View File

@@ -37,7 +37,11 @@ class LicenseKeys {
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Populates the CdmKeyStatusMap with the current content keys.
// Returns current CdmKeyStatus for the given key.
// Returns kKeyStatusKeyUnknown if key_id not found.
virtual CdmKeyStatus GetKeyStatus(const KeyId& key_id);
// Populates a CdmKeyStatusMap with the current content keys.
virtual void ExtractKeyStatuses(CdmKeyStatusMap* content_keys);
// Determines whether the specified key can be used under the current

View File

@@ -34,6 +34,11 @@ class PolicyEngine {
// status is not calculated to avoid overhead in the decryption path.
virtual bool CanDecryptContent(const KeyId& key_id);
// Returns the current CdmKeyStatus for the given key, or
// kKeyStatusKeyUnknown if the key is not found. This is useful for finding
// out why a key is not usable.
virtual CdmKeyStatus GetKeyStatus(const KeyId& key_id);
// OnTimerEvent is called when a timer fires. It notifies the Policy Engine
// that the timer has fired and dispatches the relevant events through
// |event_listener_|.

View File

@@ -317,6 +317,7 @@ enum CdmResponseType {
};
enum CdmKeyStatus {
kKeyStatusKeyUnknown,
kKeyStatusUsable,
kKeyStatusExpired,
kKeyStatusOutputNotAllowed,
@@ -398,6 +399,18 @@ enum CdmUsageSupportType {
kUnknownUsageSupport,
};
enum CdmUsageEntryStorageType {
kStorageLicense,
kStorageUsageInfo,
kStorageUnknown,
};
struct CdmUsageEntryInfo {
CdmUsageEntryStorageType storage_type;
CdmKeySetId key_set_id;
std::string usage_info_file_name;
};
class CdmKeyAllowedUsage {
public:
CdmKeyAllowedUsage() {

View File

@@ -99,11 +99,6 @@ bool CdmEngine::GetServiceCertificateRequest(CdmKeyMessage* request) {
SignedMessage message;
message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
message.SerializeToString(request);
// Convert to base64.
std::vector<uint8_t> request_vector(request->begin(), request->end());
std::string request_b64 = Base64SafeEncodeNoPad(request_vector);
request->swap(request_b64);
return true;
}
@@ -118,16 +113,8 @@ CdmResponseType CdmEngine::ParseServiceCertificateResponse(
return INVALID_PARAMETERS_ENG_24;
}
// The response is base64 encoded - decode it before parsing the string.
std::string padded_response(response);
while (padded_response.size() % 4 != 0) {
padded_response = padded_response + "=";
}
std::vector<uint8_t> raw_message = Base64SafeDecode(padded_response);
std::string raw_string(raw_message.begin(), raw_message.end());
SignedMessage signed_response;
if (!signed_response.ParseFromString(raw_string)) {
if (!signed_response.ParseFromString(response)) {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: cannot parse response");
return PARSE_RESPONSE_ERROR_1;
@@ -157,7 +144,8 @@ CdmResponseType CdmEngine::ParseServiceCertificateResponse(
return PARSE_RESPONSE_ERROR_3;
} else {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: response is wrong type");
"CdmEngine::ParseServiceCertificateResponse: response (%d) is "
"wrong type", signed_response.type());
return PARSE_RESPONSE_ERROR_4;
}
return NO_ERROR;
@@ -1079,8 +1067,11 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
CdmKeyMessage license_request;
CdmKeyResponse license_response;
std::string usage_entry;
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response, &usage_entry)) {
uint32_t usage_entry_number = 0;
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
ssid, &license_request,
&license_response, &usage_entry,
&usage_entry_number)) {
usage_property_set_->set_security_level(kLevel3);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(file_system_));
@@ -1093,8 +1084,10 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
LOGE("CdmEngine::GetUsageInfo: device file init error");
return GET_USAGE_INFO_ERROR_2;
}
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response, &usage_entry)) {
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
ssid, &license_request,
&license_response, &usage_entry,
&usage_entry_number)) {
// No entry found for that ssid.
return USAGE_INFO_NOT_FOUND;
}
@@ -1120,6 +1113,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
usage_info->clear();
return status;
}
return KEY_MESSAGE;
}
@@ -1135,7 +1129,9 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
do {
status = GetUsageInfo(app_id, security_level, usage_info);
if (KEY_MESSAGE == status && !usage_info->empty()) return status;
if (KEY_MESSAGE == status && !usage_info->empty()) {
return status;
}
} while (KEY_CANCELED == status);
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
@@ -1172,7 +1168,8 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
}
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
if (!handle.RetrieveUsageInfo(app_id, &license_info)) {
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
&license_info)) {
LOGE("CdmEngine::GetUsageInfo: unable to read usage information");
return GET_USAGE_INFO_ERROR_4;
}
@@ -1262,7 +1259,9 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
DeviceFiles handle(file_system_);
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
if (!handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&provider_session_tokens)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure"
"stops", j);
status = RELEASE_ALL_USAGE_INFO_ERROR_1;
@@ -1338,11 +1337,15 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
std::string app_id;
session->GetApplicationId(&app_id);
std::string provider_session_token;
CdmKeyMessage key_message;
CdmKeyResponse key_response;
std::string usage_entry;
if (!handle.RetrieveUsageInfoByKeySetId(app_id, key_set_id, &key_message,
&key_response, &usage_entry)) {
uint32_t usage_entry_number = 0;
if (!handle.RetrieveUsageInfoByKeySetId(
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id,
&provider_session_token, &key_message, &key_response,
&usage_entry, &usage_entry_number)) {
LOGE("CdmEngine::LoadUsageSession: unable to find usage information");
return LOAD_USAGE_INFO_MISSING;
}

View File

@@ -189,13 +189,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
int64_t last_playback_time;
int64_t grace_period_end_time;
std::string usage_entry;
uint32_t usage_entry_number = 0;
if (!file_handle_->RetrieveLicense(
key_set_id, &license_state, &offline_init_data_, &key_request_,
&key_response_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &grace_period_end_time,
&app_parameters_, &usage_entry)) {
&app_parameters_, &usage_entry, &usage_entry_number)) {
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
key_set_id.c_str());
return GET_LICENSE_ERROR;
@@ -345,6 +346,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
license_received_ = true;
key_response_ = key_response;
LOGV("AddKey: provider_session_token (size=%d) =%s",
license_parser_->provider_session_token().size(),
license_parser_->provider_session_token().c_str());
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
sts = StoreLicense();
if (sts != NO_ERROR) return sts;
@@ -598,9 +603,12 @@ CdmResponseType CdmSession::StoreLicense() {
std::string app_id;
GetApplicationId(&app_id);
std::string usage_entry;
uint32_t usage_entry_number = 0;
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
key_response_, app_id, key_set_id_,
usage_entry)) {
key_response_,
DeviceFiles::GetUsageInfoFileName(app_id),
key_set_id_, usage_entry,
usage_entry_number)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return STORE_USAGE_INFO_ERROR;
}
@@ -609,12 +617,14 @@ CdmResponseType CdmSession::StoreLicense() {
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
std::string usage_entry;
uint32_t usage_entry_number = 0;
return file_handle_->StoreLicense(
key_set_id_, state, offline_init_data_, key_request_, key_response_,
offline_key_renewal_request_, offline_key_renewal_response_,
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
policy_engine_->GetLastPlaybackTime(),
policy_engine_->GetGracePeriodEndTime(), app_parameters_, usage_entry);
policy_engine_->GetGracePeriodEndTime(), app_parameters_, usage_entry,
usage_entry_number);
}
CdmResponseType CdmSession::ReleaseCrypto() {
@@ -632,7 +642,8 @@ bool CdmSession::DeleteLicense() {
std::string app_id;
GetApplicationId(&app_id);
return file_handle_->DeleteUsageInfo(
app_id, license_parser_->provider_session_token());
DeviceFiles::GetUsageInfoFileName(app_id),
license_parser_->provider_session_token());
}
}

View File

@@ -66,7 +66,7 @@ void ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
result->assign(decoded_message.begin(), decoded_message.end());
}
}
} // namespace
namespace wvcdm {
// Protobuf generated classes.
@@ -190,7 +190,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
client_id->set_type(token_type);
#if 0 // TODO(gmorgan) Encrypt ClientIdentification. Pending Design.
if (service_certificate_->HasCertificate()) {
if (service_certificate_->has_certificate()) {
EncryptedClientIdentification* encrypted_client_id =
provisioning_request.mutable_encrypted_client_id();
CdmResponseType status;
@@ -279,9 +279,9 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
FileSystem* file_system, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
std::string raw_string;
// The response is base64 encoded in a JSON wrapper.
// Extract it and decode it. If errors, return an empty string.
std::string raw_string;
ExtractAndDecodeSignedMessage(response, &raw_string);
if (raw_string.empty()) {

View File

@@ -41,6 +41,8 @@ using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE;
using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO;
using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::DrmDeviceCertificate;
@@ -218,7 +220,8 @@ bool DeviceFiles::StoreLicense(
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,
const CdmUsageEntry& usage_entry) {
const CdmUsageEntry& usage_entry,
const uint32_t usage_entry_number) {
if (!initialized_) {
LOGW("DeviceFiles::StoreLicense: not initialized");
return false;
@@ -260,6 +263,7 @@ bool DeviceFiles::StoreLicense(
app_params->set_value(iter->second);
}
license->set_usage_entry(usage_entry);
license->set_usage_entry_number(usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
@@ -274,7 +278,8 @@ bool DeviceFiles::RetrieveLicense(
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, CdmUsageEntry* usage_entry) {
CdmAppParameterMap* app_parameters, CdmUsageEntry* usage_entry,
uint32_t* usage_entry_number) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveLicense: not initialized");
return false;
@@ -329,6 +334,7 @@ bool DeviceFiles::RetrieveLicense(
license.app_parameters(i).value();
}
*usage_entry = license.usage_entry();
*usage_entry_number = license.usage_entry_number();
return true;
}
@@ -422,21 +428,21 @@ bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) {
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& usage_info_file_name,
const std::string& key_set_id,
const CdmUsageEntry& usage_entry) {
const std::string& usage_entry,
uint32_t usage_entry_number) {
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)) {
if (!FileExists(usage_info_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)) {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
LOGW("DeviceFiles::StoreUsageInfo: Unable to parse file");
return false;
}
@@ -451,10 +457,11 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
provider_session->set_license(key_response.data(), key_response.size());
provider_session->set_key_set_id(key_set_id.data(), key_set_id.size());
provider_session->set_usage_entry(usage_entry);
provider_session->set_usage_entry_number(usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(file_name, serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
bool DeviceFiles::ListUsageRecords(const std::string& app_id,
@@ -528,15 +535,14 @@ bool DeviceFiles::GetProviderSessionToken(const std::string& app_id,
return false;
}
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name,
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;
if (!RetrieveHashedFile(usage_info_file_name, &file)) return false;
UsageInfo* usage_info = file.mutable_usage_info();
int index = 0;
@@ -565,11 +571,11 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(file_name, serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
bool DeviceFiles::DeleteAllUsageInfoForApp(
const std::string& app_id,
const std::string& usage_info_file_name,
std::vector<std::string>* provider_session_tokens) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
@@ -581,22 +587,21 @@ bool DeviceFiles::DeleteAllUsageInfoForApp(
}
provider_session_tokens->clear();
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name)) return true;
if (!FileExists(usage_info_file_name)) return true;
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(file_name, &file)) {
if (RetrieveHashedFile(usage_info_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);
return RemoveFile(usage_info_file_name);
}
bool DeviceFiles::RetrieveUsageInfo(
const std::string& app_id,
const std::string& usage_info_file_name,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
@@ -610,14 +615,14 @@ bool DeviceFiles::RetrieveUsageInfo(
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
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(file_name, &file)) {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
return false;
}
@@ -632,19 +637,19 @@ bool DeviceFiles::RetrieveUsageInfo(
return true;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry) {
std::string* usage_entry,
uint32_t* usage_entry_number) {
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)) {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
@@ -654,6 +659,8 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
@@ -662,28 +669,32 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
}
bool DeviceFiles::RetrieveUsageInfoByKeySetId(
const std::string& app_id,
const std::string& usage_info_file_name,
const std::string& key_set_id,
std::string* provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry) {
std::string* usage_entry,
uint32_t* usage_entry_number) {
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)) {
if (!RetrieveHashedFile(usage_info_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) {
*provider_session_token = file.usage_info().sessions(index).token();
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
@@ -691,6 +702,97 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId(
return false;
}
bool DeviceFiles::StoreUsageInfo(const std::string& usage_info_file_name,
const std::vector<CdmUsageData>& usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
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();
provider_session->set_token(usage_data[i].provider_session_token.data(),
usage_data[i].provider_session_token.size());
provider_session->set_license_request(usage_data[i].license_request.data(),
usage_data[i].license_request.size());
provider_session->set_license(usage_data[i].license.data(),
usage_data[i].license.size());
provider_session->set_key_set_id(usage_data[i].key_set_id.data(),
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);
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
std::vector<CdmUsageData>* usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
usage_data->resize(file.usage_info().sessions_size());
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
(*usage_data)[i].provider_session_token =
file.usage_info().sessions(i).token();
(*usage_data)[i].license_request =
file.usage_info().sessions(i).license_request();
(*usage_data)[i].license = file.usage_info().sessions(i).license();
(*usage_data)[i].key_set_id = file.usage_info().sessions(i).key_set_id();
(*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();
}
return false;
}
bool DeviceFiles::ListUsageInfoFiles(
std::vector<std::string>* usage_info_file_names) {
if (!initialized_) {
LOGW("DeviceFiles::ListUsageInfoFiles: not initialized");
return false;
}
// Get list of filenames
std::vector<std::string> filenames;
if (!ListFiles(&filenames)) {
return false;
}
// Scan list of all filenames and return only usage info filenames
usage_info_file_names->clear();
for (size_t i = 0; i < filenames.size(); i++) {
std::string* name = &filenames[i];
std::size_t pos_prefix = name->find(kUsageInfoFileNamePrefix);
std::size_t pos_suffix = name->find(kUsageInfoFileNameExt);
if (pos_prefix == std::string::npos ||
pos_suffix == std::string::npos) {
// Skip this file - extension does not match
continue;
}
usage_info_file_names->push_back(*name);
}
return true;
}
bool DeviceFiles::StoreHlsAttributes(
const std::string& key_set_id, const CdmHlsMethod method,
const std::vector<uint8_t>& media_segment_iv) {
@@ -790,7 +892,7 @@ bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) {
bool DeviceFiles::StoreUsageTableInfo(
const CdmUsageTableHeader& usage_table_header,
const std::vector<DeviceFiles::UsageEntryInfo>& usage_entry_info) {
const std::vector<CdmUsageEntryInfo>& usage_entry_info) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageTableHeader: not initialized");
return false;
@@ -807,23 +909,23 @@ bool DeviceFiles::StoreUsageTableInfo(
for (size_t i = 0; i < usage_entry_info.size(); ++i) {
UsageTableInfo_UsageEntryInfo* info =
usage_table_info->add_usage_entry_info();
info->set_key_set_id(usage_entry_info[i].key_set_id);
switch (usage_entry_info[i].storage_type) {
case kStorageLicense:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE);
info->set_key_set_id(usage_entry_info[i].key_set_id);
break;
case kStorageUsageInfo:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO);
info->set_provider_session_token(
usage_entry_info[i].provider_session_token);
info->set_app_id(usage_entry_info[i].app_id);
info->set_usage_info_file_name(
usage_entry_info[i].usage_info_file_name);
break;
case kStorageUnknown:
default:
LOGW("DeviceFiles::StoreUsageTableHeader: unknown storage type: %d",
usage_entry_info[i].storage_type);
return false;
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN);
break;
}
}
@@ -835,7 +937,7 @@ bool DeviceFiles::StoreUsageTableInfo(
bool DeviceFiles::RetrieveUsageTableInfo(
CdmUsageTableHeader* usage_table_header,
std::vector<DeviceFiles::UsageEntryInfo>* usage_entry_info) {
std::vector<CdmUsageEntryInfo>* usage_entry_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageTableInfo: not initialized");
return false;
@@ -879,21 +981,20 @@ bool DeviceFiles::RetrieveUsageTableInfo(
for (int i = 0; i < usage_table_info.usage_entry_info_size(); ++i) {
const UsageTableInfo_UsageEntryInfo& info =
usage_table_info.usage_entry_info(i);
(*usage_entry_info)[i].key_set_id = info.key_set_id();
switch (info.storage()) {
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE:
(*usage_entry_info)[i].storage_type = kStorageLicense;
(*usage_entry_info)[i].key_set_id = info.key_set_id();
break;
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO:
(*usage_entry_info)[i].storage_type = kStorageUsageInfo;
(*usage_entry_info)[i].provider_session_token =
info.provider_session_token();
(*usage_entry_info)[i].app_id = info.app_id();
(*usage_entry_info)[i].usage_info_file_name =
info.usage_info_file_name();
break;
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN:
default:
LOGW("DeviceFiles::RetrieveUsageTableInfo: Unknown storage type: %d",
info.storage());
return false;
(*usage_entry_info)[i].storage_type = kStorageUnknown;
break;
}
}

View File

@@ -44,6 +44,7 @@ message License {
// ignored if there is no grace period.
optional int64 grace_period_end_time = 11 [default = 0];
optional bytes usage_entry = 12;
optional int64 usage_entry_number = 13;
}
message UsageInfo {
@@ -53,6 +54,7 @@ message UsageInfo {
optional bytes license = 3;
optional bytes key_set_id = 4;
optional bytes usage_entry = 5;
optional int64 usage_entry_number = 6;
}
repeated ProviderSession sessions = 1;
@@ -72,12 +74,12 @@ message UsageTableInfo {
enum UsageEntryStorage {
LICENSE = 1;
USAGE_INFO = 2;
UNKNOWN = 3;
}
optional UsageEntryStorage storage = 1;
optional bytes key_set_id = 2; // used if storage is LICENSE
optional bytes provider_session_token = 3; // used if storage is USAGE_INFO
optional bytes app_id = 4; // used if storage is USAGE_INFO
optional bytes key_set_id = 2;
optional bytes usage_info_file_name = 3; // hash of the app_id
}
optional bytes usage_table_header = 1;

View File

@@ -205,8 +205,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return INVALID_PARAMETERS_LIC_7;
}
// If privacy mode and no service certificate, initiate a
// service certificate request.
// If privacy mode, must have service certificate
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->has_certificate()) {
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
@@ -479,8 +478,13 @@ CdmResponseType CdmLicense::HandleKeyResponse(
license.policy().can_persist())
is_offline_ = true;
if (license.id().has_provider_session_token())
LOGV("Get Provider_session_token:");
if (license.id().has_provider_session_token()) {
provider_session_token_ = license.id().provider_session_token();
LOGV("Provider_session_token=%s", provider_session_token_.c_str());
} else {
LOGV("NO Provider_session_token");
}
if (license.policy().has_renewal_server_url()) {
server_url_ = license.policy().renewal_server_url();
@@ -797,6 +801,7 @@ bool CdmLicense::GetClientTokenType(
case kClientTokenOemCert:
default:
// shouldn't happen
LOGE("GetClientTokenType: BAD TOKEN TYPE");
return false;
}
}
@@ -980,7 +985,7 @@ CdmResponseType CdmLicense::PrepareContentId(
if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
LOGE("CdmLicense::PrepareContentId: ISO-CENC init data not available");
return CENC_INIT_DATA_UNAVAILABLE;
}
@@ -994,7 +999,7 @@ CdmResponseType CdmLicense::PrepareContentId(
if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
LOGE("CdmLicense::PrepareContentId: WebM init data not available");
return WEBM_INIT_DATA_UNAVAILABLE;
}
@@ -1002,7 +1007,7 @@ CdmResponseType CdmLicense::PrepareContentId(
return PREPARE_WEBM_CONTENT_ID_FAILED;
}
} else {
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
LOGE("CdmLicense::PrepareContentId: no support for init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA_FORMAT;
}

View File

@@ -103,7 +103,15 @@ bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status,
return keys_changed;
}
CdmKeyStatus LicenseKeys::GetKeyStatus(const KeyId& key_id) {
if (keys_.count(key_id) == 0) {
return kKeyStatusKeyUnknown;
}
return keys_[key_id]->GetKeyStatus();
}
void LicenseKeys::ExtractKeyStatuses(CdmKeyStatusMap* content_keys) {
content_keys->clear();
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
if (it->second->IsContentKey()) {
const KeyId key_id = it->first;

View File

@@ -47,6 +47,10 @@ bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
}
}
CdmKeyStatus PolicyEngine::GetKeyStatus(const KeyId& key_id) {
return license_keys_->GetKeyStatus(key_id);
}
void PolicyEngine::InitDevice(CryptoSession* crypto_session) {
current_resolution_ = HDCP_UNSPECIFIED_VIDEO_RESOLUTION;
next_device_check_ = 0;
@@ -101,7 +105,9 @@ void PolicyEngine::OnTimerEvent() {
// Test to determine if renewal should be attempted.
switch (license_state_) {
case kLicenseStateCanPlay: {
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
if (HasRenewalDelayExpired(current_time)) {
renewal_needed = true;
}
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break;
@@ -113,7 +119,9 @@ void PolicyEngine::OnTimerEvent() {
}
case kLicenseStateWaitingLicenseUpdate: {
if (HasRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
if (HasRenewalRetryIntervalExpired(current_time)) {
renewal_needed = true;
}
break;
}
@@ -429,6 +437,10 @@ bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
next_renewal_time_ <= current_time;
}
// Apply a key status to the current keys.
// If this represents a new key status, perform a notification callback.
// NOTE: if the new status is kKeyStatusUsable, the HDCP check may result in an
// override to kKeyStatusOutputNotAllowed.
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
bool keys_changed;
bool has_new_usable_key = false;

View File

@@ -11,110 +11,6 @@
namespace {
// Service certificate for Google/Widevine Provisioning and License servers.
static const unsigned char kRootCertForDev[] = {
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00,
0x18, 0xc3, 0x94, 0x88, 0x8b, 0x05, 0x22, 0x8e,
0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
0x81, 0x00, 0xc0, 0x00, 0x36, 0x6f, 0x8e, 0xe9,
0xcf, 0x86, 0xdb, 0xcd, 0xdd, 0x4e, 0xfd, 0xcd,
0x45, 0xbf, 0x6d, 0x96, 0x05, 0x00, 0xb8, 0x72,
0xff, 0x9c, 0xb4, 0x39, 0xa8, 0xd8, 0xc0, 0x09,
0x73, 0xc0, 0x24, 0x6a, 0x39, 0x4d, 0x36, 0x3f,
0x9a, 0xe4, 0xb8, 0x76, 0xdc, 0x34, 0xe3, 0xee,
0x5f, 0xdd, 0x13, 0x20, 0x08, 0xdc, 0x4e, 0x6f,
0x4e, 0x9f, 0xc0, 0x36, 0xf9, 0xce, 0xc6, 0xb7,
0xdb, 0xe0, 0x51, 0x2d, 0x30, 0x0b, 0xae, 0x0a,
0x20, 0xd2, 0x29, 0x3c, 0x2c, 0x1d, 0x87, 0x65,
0xeb, 0x5f, 0x93, 0xd7, 0x3f, 0x12, 0x08, 0x50,
0x0e, 0x55, 0xf3, 0xf1, 0x19, 0xee, 0x18, 0x21,
0x6e, 0xea, 0xb6, 0x0a, 0x4a, 0x0b, 0x9c, 0x72,
0x37, 0xeb, 0x0b, 0x68, 0xfc, 0x52, 0x46, 0x62,
0xd0, 0xa2, 0x99, 0x66, 0xe2, 0x2b, 0x74, 0xdd,
0x5c, 0xaf, 0x9a, 0x03, 0xc4, 0x5d, 0x93, 0xfb,
0xcd, 0x45, 0x9a, 0xee, 0xfb, 0x7b, 0x18, 0x94,
0xc1, 0x8c, 0x82, 0x34, 0x7f, 0x02, 0x12, 0x21,
0xfc, 0x40, 0xc1, 0x50, 0xc9, 0xf4, 0x7c, 0xd5,
0x96, 0xbe, 0x55, 0x7f, 0x3c, 0x1d, 0x70, 0x34,
0xb4, 0xa2, 0x03, 0xc4, 0x3f, 0x89, 0x60, 0xe4,
0x24, 0x09, 0x1a, 0x74, 0xc4, 0xb6, 0x39, 0xf0,
0x34, 0x60, 0x8e, 0xa7, 0x5f, 0x02, 0x7f, 0xb9,
0x2a, 0xc5, 0xaa, 0xb2, 0x4c, 0x34, 0xd3, 0x5a,
0x5d, 0xfa, 0x07, 0xf2, 0xb9, 0xb3, 0xc1, 0xba,
0xab, 0xbe, 0x89, 0x99, 0xe3, 0x6d, 0x9b, 0xa9,
0xd3, 0xaf, 0x2a, 0x08, 0x76, 0xf3, 0x0e, 0xc9,
0xe0, 0xb3, 0xbf, 0x51, 0x0c, 0xc5, 0xf4, 0xf3,
0x15, 0x7b, 0x08, 0x11, 0x8f, 0x61, 0x1f, 0x61,
0x64, 0xdb, 0x15, 0x84, 0x5b, 0x8a, 0xd1, 0x28,
0x40, 0xde, 0xc5, 0x32, 0xb5, 0xad, 0xad, 0x65,
0x4c, 0xf5, 0xf7, 0xd1, 0x90, 0x14, 0x5d, 0xc2,
0x85, 0x98, 0xcc, 0xe9, 0xe6, 0x95, 0x42, 0xe1,
0x3e, 0xfc, 0x7f, 0xc4, 0x49, 0xed, 0x9c, 0xe4,
0x49, 0x3f, 0x03, 0x1b, 0x0d, 0xa0, 0xfb, 0xf5,
0x38, 0x49, 0xd2, 0xdf, 0xa3, 0x88, 0xb2, 0x76,
0x93, 0x08, 0x20, 0x18, 0xfe, 0xdc, 0x72, 0x6c,
0x6e, 0xbf, 0x61, 0x37, 0x03, 0xdb, 0xe5, 0x72,
0x68, 0xe0, 0x99, 0x2f, 0xb9, 0xe0, 0x2e, 0xbb,
0x9f, 0x96, 0x36, 0x61, 0xaa, 0x2d, 0xa4, 0x93,
0xe8, 0x50, 0x58, 0xe6, 0x61, 0xe1, 0x14, 0xcf,
0xac, 0x86, 0x98, 0x7f, 0x3c, 0x67, 0x16, 0xce,
0xb8, 0x70, 0x90, 0x3a, 0x5a, 0xd4, 0xe1, 0xe2,
0x35, 0x98, 0xbf, 0x93, 0x41, 0x11, 0xb2, 0x44,
0xb2, 0x64, 0xc2, 0xe7, 0x09, 0x45, 0xb7, 0x6f,
0xb0, 0xbd, 0x6e, 0xe8, 0x67, 0xfa, 0x8d, 0xd4,
0xfa, 0x4b, 0xef, 0xa8, 0x9d, 0x8a, 0x0a, 0xd9,
0x14, 0x77, 0x09, 0x11, 0x9e, 0xc3, 0x50, 0x14,
0x6c, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12,
0x80, 0x03, 0x17, 0x01, 0x60, 0x24, 0xe1, 0xfd,
0x75, 0x60, 0x17, 0x5c, 0x5e, 0x6f, 0x9f, 0x7f,
0xdf, 0xee, 0xf0, 0xf7, 0x7d, 0xb2, 0x50, 0x65,
0x36, 0x26, 0x14, 0x19, 0x01, 0x5e, 0x98, 0x94,
0x65, 0x97, 0x83, 0xaa, 0x4a, 0x2b, 0x98, 0x2e,
0x02, 0xf3, 0xb2, 0xc9, 0xb2, 0xed, 0xd3, 0x1b,
0x20, 0x27, 0x9e, 0xe1, 0x25, 0xc7, 0x86, 0xf0,
0x66, 0x68, 0x5d, 0xd2, 0x3d, 0xa7, 0xbb, 0xbc,
0x22, 0xfc, 0x29, 0xfa, 0x17, 0x16, 0xf4, 0xa2,
0x00, 0x10, 0x87, 0xb4, 0x5d, 0x51, 0x45, 0x6b,
0xc8, 0xf4, 0x6b, 0xcc, 0x92, 0x91, 0xe7, 0xa7,
0x93, 0xbc, 0xc7, 0x2e, 0xdc, 0xac, 0x82, 0x2b,
0x85, 0x56, 0x7b, 0xae, 0xf2, 0xd8, 0xda, 0xa6,
0xd7, 0xfa, 0x6d, 0x70, 0x2a, 0x2e, 0xcf, 0x69,
0xef, 0x57, 0x91, 0xa7, 0xaa, 0x40, 0x15, 0x4a,
0x49, 0x1b, 0xbc, 0x36, 0xbb, 0x1c, 0x94, 0x33,
0x36, 0x61, 0x22, 0x9d, 0x22, 0x66, 0xf0, 0x88,
0x5e, 0x7c, 0x3c, 0xa5, 0xff, 0x81, 0xcf, 0x1a,
0x44, 0xa1, 0x2b, 0xdf, 0xc9, 0x3d, 0xd5, 0xc7,
0xc7, 0x3a, 0x75, 0xac, 0x29, 0xfa, 0xfd, 0x5b,
0xda, 0xf5, 0x8f, 0xd9, 0xdf, 0x08, 0xa4, 0x8d,
0x19, 0x4a, 0xa4, 0x79, 0x6e, 0x47, 0xf6, 0x07,
0xe0, 0xbd, 0xbf, 0x30, 0x3a, 0xf9, 0xf5, 0xc0,
0x90, 0x6d, 0x70, 0x27, 0x44, 0xa8, 0x5e, 0x70,
0xcd, 0x43, 0x3e, 0xaf, 0xf0, 0xd7, 0x20, 0xd3,
0x5e, 0x97, 0x2d, 0x32, 0x1a, 0x3d, 0x2d, 0x0f,
0x0f, 0xcf, 0xac, 0x4e, 0x88, 0x75, 0x98, 0x6c,
0xfa, 0xe8, 0x42, 0x58, 0x99, 0xaa, 0x45, 0x0c,
0x41, 0x0c, 0x6e, 0x27, 0x58, 0x57, 0xd2, 0x5b,
0x82, 0x3d, 0x75, 0x2f, 0x9e, 0xf3, 0xe4, 0x00,
0xcf, 0x91, 0x48, 0x25, 0xca, 0x98, 0xf2, 0x91,
0x6b, 0x41, 0xa5, 0xe8, 0xcd, 0x64, 0xa7, 0x2e,
0x78, 0xc7, 0x76, 0x82, 0x3f, 0xf8, 0x57, 0x8a,
0x9d, 0x78, 0x25, 0xad, 0xf3, 0x1a, 0x8b, 0xfc,
0x83, 0x9a, 0x98, 0x87, 0xe4, 0x55, 0x3e, 0x1c,
0xa7, 0x80, 0x8f, 0xd6, 0x76, 0xab, 0x03, 0xc7,
0x05, 0x66, 0xc3, 0xa0, 0x4c, 0x33, 0x1f, 0x39,
0x74, 0x1b, 0x2a, 0xbf, 0xe6, 0xb0, 0x9f, 0x6b,
0xc1, 0xd6, 0xd3, 0xf4, 0x46, 0x9b, 0xf3, 0xab,
0xca, 0x2e, 0x88, 0x3d, 0x84, 0x5f, 0xc9, 0x9b,
0x47, 0xbb, 0x57, 0x64, 0x08, 0x0e, 0x18, 0x74,
0x83, 0x44, 0xd4, 0xc3, 0x18, 0x97, 0xcf, 0x89,
0x6a, 0x49, 0x51, 0xc6, 0xff, 0x8d, 0x39, 0xc5,
0x23, 0xf9, 0xd5, 0x01, 0xd7, 0x2f, 0xa9, 0xa5,
0x5d, 0xa9, 0xf3, 0xc9, 0xfd, 0xc4, 0x52, 0x19,
0x7d, 0xf6, 0xa4, 0x2c, 0x0c, 0xa0, 0x07, 0xdf,
0x7b, 0x44, 0xd7, 0xe5, 0xbf, 0x57, 0x87, 0xc9,
0x8c, 0xfe, 0x30, 0xb2, 0x89, 0x5d, 0x00, 0x03,
0x3b, 0xe5
};
static const unsigned char kRootCertForProd[] = {
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00,
0x18, 0xdd, 0x94, 0x88, 0x8b, 0x05, 0x22, 0x8e,
@@ -231,14 +127,8 @@ using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::SignedMessage;
CdmResponseType ServiceCertificate::Init(const std::string& certificate) {
// TODO(tinskip): Get rid of conditional compilation.
#if defined(QA_TEST_SERVER)
std::string root_cert_str(reinterpret_cast<const char*>(&kRootCertForDev[0]),
sizeof(kRootCertForDev));
#else
std::string root_cert_str(reinterpret_cast<const char*>(&kRootCertForProd[0]),
sizeof(kRootCertForProd));
#endif // !defined(QA_TEST_SERVER);
// Load root cert public key. Don't bother verifying it.
SignedDrmDeviceCertificate signed_root_cert;

View File

@@ -229,7 +229,7 @@ std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
}
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
// as Base64WebSafeDecode.
// as Base64WebSafeDecode. Add padding if needed.
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
if (b64_input.empty()) {
return std::vector<uint8_t>();

View File

@@ -38,24 +38,26 @@ KeyId g_key_id_unwrapped;
CdmKeySystem g_key_system;
std::string g_license_server;
std::string g_provisioning_server;
std::string g_service_certificate;
std::string g_provisioning_service_certificate;
std::string g_license_service_certificate;
KeyId g_wrong_key_id;
const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm";
static void CommonSetup(bool use_qa) {
static void CommonSetup(ServerConfigurationId which) {
// NOTE: Select QA/Test server config vs. UAT server config
ConfigTestEnv config((use_qa) ? kContentProtectionTestQAServer :
kContentProtectionUatServer);
// NOTE: Select configuration
ConfigTestEnv config(which);
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id());
g_license_server.assign(config.license_server());
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
g_service_certificate.assign(config.service_certificate());
g_provisioning_service_certificate.assign(
config.provisioning_service_certificate());
g_license_service_certificate.assign(config.license_service_certificate());
g_provisioning_server.assign(config.provisioning_server());
// Extract the key ID from the PSSH box.
@@ -63,6 +65,45 @@ static void CommonSetup(bool use_qa) {
g_key_id_unwrapped = extractor.data();
}
/*
* Locate the portion of the server's response message that is between
* the strings jason_start_substr and json_end_substr. Returns the string
* through *result. If the start substring match fails, assume the entire
* string represents a serialized protobuf mesaage and return true with
* the entire string. If the end_substring match fails, return false with
* an empty *result.
*/
bool ExtractSignedMessage(const std::string& response,
const std::string& json_start_substr,
const std::string& json_end_substr,
std::string* result) {
std::string response_string;
size_t start = response.find(json_start_substr);
if (start == response.npos) {
// Assume serialized protobuf message.
result->assign(response);
} else {
// Assume JSON-wrapped protobuf.
size_t end = response.find(json_end_substr,
start + json_start_substr.length());
if (end == response.npos) {
LOGE("ExtractSignedMessage cannot locate end substring");
result->clear();
return false;
}
size_t result_string_size = end - start - json_start_substr.length();
result->assign(response, start + json_start_substr.length(),
result_string_size);
}
if (result->empty()) {
LOGE("ExtractSignedMessage: Response message is empty");
return false;
}
return true;
}
} // namespace
class WvCdmEnginePreProvTest : public testing::Test {
@@ -72,15 +113,6 @@ class WvCdmEnginePreProvTest : public testing::Test {
virtual ~WvCdmEnginePreProvTest() {}
static void SetUpTestCase() {
// NOTE: Select QA/Test server config vs. UAT server config
#if defined(QA_TEST_SERVER)
CommonSetup(true);
#else
CommonSetup(false);
#endif // !defined(QA_TEST_SERVER)
}
virtual void SetUp() {
CdmResponseType status =
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
@@ -97,24 +129,11 @@ class WvCdmEnginePreProvTest : public testing::Test {
protected:
bool IsBase64Encoded(const std::string& message) {
for (size_t i = 0; i < message.size(); ++i) {
uint8_t ch = message[i];
if (ch >= 'a' && ch <= 'z') continue;
if (ch >= 'A' && ch <= 'Z') continue;
if (ch >= '0' && ch <= '9') continue;
if (ch == '-' || ch == '_' || ch == '=' || ch == '.' || ch == '/') {
continue;
}
return false;
}
return true;
}
// Trade request for response via the license server.
bool LicenseServerRequestResponse(const std::string& request,
std::string* response) {
LOGV("server url: %s", g_license_server.c_str());
LOGV("LicenseServerRequestResponse: server url: %s",
g_license_server.c_str());
UrlRequest url_request(g_license_server + g_client_auth);
url_request.PostRequest(request);
@@ -123,29 +142,67 @@ class WvCdmEnginePreProvTest : public testing::Test {
return false;
}
LOGV("http_resp:\n%s\n", http_response.c_str());
LOGV("http_response:\n%s\n", http_response.c_str());
// Separate message from HTTP headers.
LicenseRequest license_request;
std::string response_message;
license_request.GetDrmMessage(http_response, response_message);
license_request.GetDrmMessage(http_response, *response);
LOGV("resp: size=%u, string:\n%s\n", response_message.size(),
Base64SafeEncode(
std::vector<uint8_t>(response_message.begin(),
response_message.end())).c_str());
// Response should be base64 encoded. If it is not,
// fix it now.
if (!IsBase64Encoded(response_message)) {
std::vector<uint8_t> response_vector(response_message.begin(),
response_message.end());
response_message = Base64SafeEncode(response_vector);
}
response->swap(response_message);
LOGV("response: size=%u, string:\n%s\n", response->size(),
Base64SafeEncode(std::vector<uint8_t>(response->begin(),
response->end())).c_str());
return true;
}
virtual void Provision() {
LOGV("WvCdmEnginePreProvTest::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.SetServiceCertificate(
g_provisioning_service_certificate));
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok);
LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n",
http_message.c_str());
// extract provisioning response from received message
// Extracts signed response from JSON string, result is serialized protobuf.
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string protobuf_response;
EXPECT_TRUE (ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
&protobuf_response)) <<
"Failed to extract signed serialized response from JSON response";
LOGV("WvCdmEnginePreProvTest::Provision: extracted response "
"message: \n%s\n", protobuf_response.c_str());
ASSERT_EQ(NO_ERROR,
cdm_engine_.HandleProvisioningResponse(protobuf_response,
&cert, &wrapped_key));
ASSERT_EQ(NO_ERROR,
cdm_engine_.SetServiceCertificate(g_license_service_certificate));
}
FileSystem file_system_;
CdmEngine cdm_engine_;
bool session_opened_;
@@ -153,10 +210,75 @@ class WvCdmEnginePreProvTest : public testing::Test {
std::string session_id_;
};
class WvCdmEnginePreProvTestStaging : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStaging() {}
virtual ~WvCdmEnginePreProvTestStaging() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingLicense);
}
};
class WvCdmEnginePreProvTestProd : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestProd() {}
virtual ~WvCdmEnginePreProvTestProd() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionProdLicense);
}
};
class WvCdmEnginePreProvTestUat : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestUat() {}
virtual ~WvCdmEnginePreProvTestUat() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionUatLicense);
}
};
class WvCdmEnginePreProvTestStagingProv30 : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStagingProv30() {}
virtual ~WvCdmEnginePreProvTestStagingProv30() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingPlusProv30);
}
};
class WvCdmEnginePreProvTestUatProv30 : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestUatProv30() {}
virtual ~WvCdmEnginePreProvTestUatProv30() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionUatPlusProv30);
}
};
class WvCdmEngineTest : public WvCdmEnginePreProvTest {
public:
WvCdmEngineTest() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingLicense);
}
virtual void SetUp() {
CdmResponseType status =
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
@@ -170,43 +292,6 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
}
protected:
void Provision() {
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.SetServiceCertificate(
g_service_certificate));
CdmResponseType result = NO_ERROR;
for (int i = 0; i < 2; i++) { // Retry once if there is a nonce problem.
result = cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &prov_request, &provisioning_server_url);
if (result == CERT_PROVISIONING_NONCE_GENERATION_ERROR) {
LOGW("Woops. Nonce problem. Try again?");
sleep(1);
} else {
break;
}
}
ASSERT_EQ(NO_ERROR, result);
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
EXPECT_TRUE(ok);
// One of many reasons a device might fail to provision is that the server
// rejects its keybox. In that case, we usually see an error of
// CERT_PROVISIONING_RESPONSE_ERROR_1. The error response may help
// somebody look through the server logs for more clues.
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(message, &cert,
&wrapped_key))
<< "Error response: " << message;
}
void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string) {
@@ -269,6 +354,9 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
const std::string& client_auth,
bool expect_success) {
// Use secure connection and chunk transfer coding.
LOGV("GetKeyRequestResponse: server_url: %s", server_url.c_str());
UrlRequest url_request(server_url + client_auth);
if (!url_request.is_connected()) {
return "";
@@ -315,19 +403,19 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
};
// Test that service certificate is initially absent.
TEST_F(WvCdmEnginePreProvTest, ServiceCertificateInitialNoneTest) {
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateInitialNoneTest) {
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
};
// Test that service certificate can be properly installed.
TEST_F(WvCdmEnginePreProvTest, ServiceCertificateGoodTest) {
ASSERT_EQ(cdm_engine_.SetServiceCertificate(g_service_certificate),
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateGoodTest) {
ASSERT_EQ(cdm_engine_.SetServiceCertificate(g_license_service_certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
};
// Test that service certificate can be retrieved from the license server.
TEST_F(WvCdmEnginePreProvTest, ServiceCertificateRequestResponse) {
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateRequestResponse) {
CdmKeyMessage request;
std::string certificate;
@@ -335,65 +423,110 @@ TEST_F(WvCdmEnginePreProvTest, ServiceCertificateRequestResponse) {
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
// Generate request.
// The request will be a base64 encode of a serialized protobuf message.
// The request will be a serialized protobuf message.
ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request));
LOGV("ret'd request message:\"%s\"", request.c_str());
std::string response;
ASSERT_TRUE(LicenseServerRequestResponse(request, &response));
// Extract the service certificate
if (cdm_engine_.ParseServiceCertificateResponse(response, &certificate) ==
NO_ERROR) {
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
LOGV("ret'd cert:\"%s\"", b2a_hex(certificate).c_str());
return;
}
// Message did not parse. Possibly it is because of base64 encoding
// of the request. Try again with binary (base64 decoded) message.
LOGE("Base64 encoded request failed - RETRY with binary request");
while (request.size() % 4 != 0) {
request = request + "="; // add padding if necessary
}
std::vector<uint8_t> binary_vector = Base64SafeDecode(request);
std::string binary_request(binary_vector.begin(), binary_vector.end());
LOGV("raw_string=%s", b2a_hex(binary_request).c_str());
ASSERT_TRUE(LicenseServerRequestResponse(binary_request, &response));
ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate),
NO_ERROR);
LOGV("ret'd cert:\"%s\"", b2a_hex(certificate).c_str());
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str());
};
// Test that service certificate can be retrieved from the license server.
TEST_F(WvCdmEnginePreProvTestUat, ServiceCertificateRequestResponse) {
CdmKeyMessage request;
std::string certificate;
// Initial condition - no service certificate.
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
// Generate request.
// The request will be a serialized protobuf message.
ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request));
std::string response;
ASSERT_TRUE(LicenseServerRequestResponse(request, &response));
// Extract the service certificate
ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str());
};
// Test that service certificate can be retrieved from the license server.
TEST_F(WvCdmEnginePreProvTestProd, ServiceCertificateRequestResponse) {
CdmKeyMessage request;
std::string certificate;
// Initial condition - no service certificate.
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
// Generate request.
// The request will be a serialized protobuf message.
ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request));
std::string response;
ASSERT_TRUE(LicenseServerRequestResponse(request, &response));
// Extract the service certificate
ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str());
};
// Test that empty service certificate fails.
TEST_F(WvCdmEnginePreProvTest, ServiceCertificateEmptyFailTest) {
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateEmptyFailTest) {
std::string empty_cert;
ASSERT_EQ(cdm_engine_.SetServiceCertificate(g_service_certificate),
ASSERT_EQ(cdm_engine_.SetServiceCertificate(g_license_service_certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
};
// Test that provisioning works, even if device is already provisioned.
TEST_F(WvCdmEngineTest, DISABLED_Provisioning30Test) {
TEST_F(WvCdmEnginePreProvTestStaging, DISABLED_ProvisioningTest) {
uint32_t nonce = 0;
uint8_t buffer[1];
size_t size = 0;
int result = OEMCrypto_RewrapDeviceRSAKey(
0, buffer, 0, buffer, 0, &nonce, buffer, 0, buffer, buffer, &size);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
int result30 = OEMCrypto_RewrapDeviceRSAKey30(
0, &nonce, buffer, 0, buffer, 0, buffer, buffer, &size);
int method = OEMCrypto_GetProvisioningMethod(kLevelDefault);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED &&
result30 == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
LOGW("WARNING: Skipping ProvisioningTest because the device does not "
"support provisioning. If you are using a baked-in certificate, this "
"is expected. Otherwise, something is wrong.");
return;
"support provisioning. If you are using a baked-in certificate, "
"this is expected. Otherwise, something is wrong.");
ASSERT_EQ(method, OEMCrypto_DrmCertificate);
} else {
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(method, OEMCrypto_OEMCertificate);
} else {
ASSERT_EQ(method, OEMCrypto_Keybox);
}
}
Provision();
}
TEST_F(WvCdmEnginePreProvTestStagingProv30, ProvisioningTest) {
Provision();
}
// Test that provisioning works, even if device is already provisioned.
TEST_F(WvCdmEngineTest, DISABLED_ProvisioningTest) {
Provision();
}
@@ -403,7 +536,7 @@ TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, BaseWebmMessageTest) {
TEST_F(WvCdmEngineTest, DISABLED_BaseWebmMessageTest) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
@@ -423,7 +556,7 @@ TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, NormalDecryptionWebm) {
TEST_F(WvCdmEngineTest, DISABLED_NormalDecryptionWebm) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
}

View File

@@ -3,6 +3,12 @@
#include "config_test_env.h"
#include "string_conversions.h"
// Holds the data needed to talk to the various provisioning and
// license servers.
//
// Define a series of configurations, and specify the specific
// data items needed for that configuration.
namespace wvcdm {
namespace {
@@ -95,18 +101,14 @@ const std::string kUatServiceCertificate =
"38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A"
"250A4EB9C84AB3E6539F6B6FDF56899EA29914";
// QA/Test server (kContentProtectionTestQAServer)
//
const std::string kQALicenseServerUrl =
"https://widevine-proxy-qa.corp.google.com/proxy";
const std::string kQAProvisioningServerUrl =
"http://www-googleapis-test.sandbox.google.com/"
"certificateprovisioning/v1/devicecertificates/create";
// Content Protection license server (Production) data
const std::string kCpProdLicenseServer =
"https://widevine-proxy.appspot.com/proxy";
// Content Protection license server (UAT) data
const std::string kCpUatLicenseServer =
"http://widevine-proxy.appspot.com/proxy";
"https://proxy.uat.widevine.com/proxy";
const std::string kCpClientAuth = "";
const std::string kCpKeyId =
"00000042" // blob size
@@ -130,7 +132,7 @@ const std::string kCpOfflineKeyId =
// Content Protection license server (staging) data
const std::string kCpStagingLicenseServer =
"http://wv-staging-proxy.appspot.com/proxy";
"https://proxy.staging.widevine.com/proxy";
// Google Play license server data
const std::string kGpLicenseServer =
@@ -181,28 +183,47 @@ const std::string kStagingProvisioningServerUrl =
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, "", kStagingProvisioningServerUrl},
{kContentProtectionUatServer, kCpUatLicenseServer, kCpClientAuth,
kCpKeyId, kCpOfflineKeyId, kProdServiceCertificate,
kStagingProvisioningServerUrl},
{kContentProtectionStagingServer, kCpStagingLicenseServer,
kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kProdServiceCertificate,
kStagingProvisioningServerUrl},
{kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, kStagingProvisioningServerUrl, ""},
{kContentProtectionProdLicense, kCpProdLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kProductionProvisioningServerUrl, kProdServiceCertificate},
{kContentProtectionUatLicense, kCpUatLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kProductionProvisioningServerUrl, kProdServiceCertificate},
{kContentProtectionStagingLicense, kCpStagingLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionProdPlusProv30, kCpProdLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionUatPlusProv30, kCpUatLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionStagingPlusProv30, kCpStagingLicenseServer,
kStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
};
} // namespace
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) { Init(server_id); }
ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id) {
Init(server_id);
}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) {
ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id, bool streaming) {
Init(server_id);
if (!streaming) key_id_ = license_servers[server_id].offline_key_id;
}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id, bool streaming,
bool renew, bool release) {
Init(server_id);
if (!streaming) {
@@ -220,13 +241,16 @@ ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
}
}
void ConfigTestEnv::Init(LicenseServerId server_id) {
void ConfigTestEnv::Init(ServerConfigurationId server_id) {
client_auth_ = license_servers[server_id].client_tag;
key_id_ = license_servers[server_id].key_id;
key_system_ = kWidevineKeySystem;
license_server_ = license_servers[server_id].url;
provisioning_server_ = license_servers[server_id].provisioning_server;
service_certificate_ = a2bs_hex(license_servers[server_id].service_certificate);
license_server_ = license_servers[server_id].license_server_url;
provisioning_server_ = license_servers[server_id].provisioning_server_url;
license_service_certificate_ =
a2bs_hex(license_servers[server_id].license_service_certificate);
provisioning_service_certificate_ =
a2bs_hex(license_servers[server_id].provisioning_service_certificate);
wrong_key_id_ = kWrongKeyId;
}

View File

@@ -6,30 +6,50 @@
#include <string>
#include "wv_cdm_types.h"
// Declare class ConfigTestEnv - holds the configuration settings needed
// to talk to the various provisioning and license servers.
//
// License Servers
// QA - early test server (corporate access only, not generally usable).
// UAT - test server with non-production data.
// Staging - test server with access to production data.
// Production - live, production server.
// Google Play - Allows testing on Google Play servers (very stale).
//
// Provisioning Servers
// UAT - early access provisioning server.
// Staging - early access to features.
// Production - live production server.
// Useful configurations
namespace wvcdm {
typedef enum {
kGooglePlayServer,
kContentProtectionUatServer,
kContentProtectionStagingServer,
kContentProtectionTestQAServer,
} LicenseServerId;
kGooglePlayServer, // not tested recently
kContentProtectionProdLicense,
kContentProtectionUatLicense,
kContentProtectionStagingLicense,
kContentProtectionProdPlusProv30,
kContentProtectionUatPlusProv30,
kContentProtectionStagingPlusProv30,
} ServerConfigurationId;
// Configures default test environment.
class ConfigTestEnv {
public:
typedef struct {
LicenseServerId id;
std::string url;
ServerConfigurationId id;
std::string license_server_url;
std::string license_service_certificate;
std::string client_tag;
std::string key_id;
std::string offline_key_id;
std::string service_certificate;
std::string provisioning_server;
std::string provisioning_server_url;
std::string provisioning_service_certificate;
} LicenseServerConfiguration;
explicit ConfigTestEnv(LicenseServerId server_id);
ConfigTestEnv(LicenseServerId server_id, bool streaming);
ConfigTestEnv(LicenseServerId server_id, bool streaming, bool renew,
explicit ConfigTestEnv(ServerConfigurationId server_id);
ConfigTestEnv(ServerConfigurationId server_id, bool streaming);
ConfigTestEnv(ServerConfigurationId server_id, bool streaming, bool renew,
bool release);
~ConfigTestEnv() {};
@@ -40,8 +60,11 @@ class ConfigTestEnv {
const std::string& provisioning_server() const {
return provisioning_server_;
}
const std::string& service_certificate() const {
return service_certificate_;
const std::string& license_service_certificate() const {
return license_service_certificate_;
}
const std::string& provisioning_service_certificate() const {
return provisioning_service_certificate_;
}
const KeyId& wrong_key_id() const { return wrong_key_id_; }
@@ -54,14 +77,15 @@ class ConfigTestEnv {
}
private:
void Init(LicenseServerId server_id);
void Init(ServerConfigurationId server_id);
std::string client_auth_;
KeyId key_id_;
CdmKeySystem key_system_;
std::string license_server_;
std::string provisioning_server_;
std::string service_certificate_;
std::string license_service_certificate_;
std::string provisioning_service_certificate_;
KeyId wrong_key_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);

View File

@@ -124,6 +124,7 @@ struct LicenseInfo {
int64_t grace_period_end_time;
std::string app_parameters;
std::string usage_entry;
uint32_t usage_entry_number;
std::string file_data;
};
@@ -230,9 +231,9 @@ LicenseInfo license_test_data[] = {
"0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF"
"A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0"
"BEA6CABACA1C2C"),
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", "",
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", "", 0,
a2bs_hex(
"0AAC150802100122A5150801121408011210303132333435363738394142434445461"
"0AAE150802100122A7150801121408011210303132333435363738394142434445461"
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
"7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302"
"5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B"
@@ -311,8 +312,9 @@ LicenseInfo license_test_data[] = {
"106B63746C0000000000ECDCBE0000000020DBDFA68F051A20182F029E35047A3841F"
"A176C74E5B387350E8D58DEA6878FF0BEA6CABACA1C2C3A2E68747470733A2F2F7465"
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
"3654000480058006200122039CB77169260F923E5D4BA5BBF6A7611117483253F2869"
"9DF3D9D14C3718E309")},
"365400048005800620068001220785CE1756656A049E77F28C8449AB2DD115B6C43B2"
"FF232D23F98B72F1DCE96A"
)},
// license 1
{"ksidC8EAA2579A282EB0", DeviceFiles::kLicenseStateReleasing,
@@ -415,8 +417,9 @@ LicenseInfo license_test_data[] = {
a2bs_hex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021"
"22232425262728292a2b2c2d2e2f"),
5,
a2bs_hex(
"0AF5150802100122EE150802121408011210303132333435363738394142434445461"
"0AF7150802100122F0150802121408011210303132333435363738394142434445461"
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
"7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302"
"5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B"
@@ -497,8 +500,8 @@ LicenseInfo license_test_data[] = {
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
"36540F8ACD1910148E58ED29101520F0A054E616D6531120656616C75653158006230"
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212"
"2232425262728292A2B2C2D2E2F1220312487214ACA5A9AF5ED9D45F209DC77E03CA1"
"DBFFDF86C44A35A1351CE968B9")},
"2232425262728292A2B2C2D2E2F6805122010DB816A045F2AA5865B17FE2F20DA2114"
"17B2F8B2D7511C9DE89A87CB5208AB")},
// license 2
{"ksidE8C37662C88DC673", DeviceFiles::kLicenseStateReleasing,
@@ -601,8 +604,9 @@ LicenseInfo license_test_data[] = {
a2bs_hex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021"
"22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"),
12,
a2bs_hex(
"0AAB160802100122A4160802121408011210303132333435363738394142434445461"
"0AAD160802100122A6160802121408011210303132333435363738394142434445461"
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
"7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302"
"5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B"
@@ -684,8 +688,9 @@ LicenseInfo license_test_data[] = {
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910152150A054E616D6531120C5661"
"6C756531204E616D653252160A0C4E616D653220506172616D321206506172616D325"
"8006240000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E"
"1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F122"
"07CEAAE401B81E635808AC830A0F3F43308A38FAB16069E542F994B6EC1325280")}};
"1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F680"
"C12206AA0237760D1F06E5CB78F5AFC3D124BBF7C26921CB3CC2EA44766801E25D34"
"F")}};
// Sample license data and related data for storage and use for offline
// playback. The license data and URLs in this test are not real.
@@ -792,8 +797,9 @@ LicenseInfo license_update_test_data[] = {
a2bs_hex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021"
"22232425262728292a2b2c2d2e2f"),
15,
a2bs_hex(
"0AEC150802100122E5150801121408011210303132333435363738394142434445461"
"0AEE150802100122E7150801121408011210303132333435363738394142434445461"
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
"7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82"
"6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD"
@@ -874,14 +880,14 @@ LicenseInfo license_update_test_data[] = {
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910158006230000102030405060708"
"090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2"
"B2C2D2E2F1220A33C179B7718632337DFDC32D3711FD543336EBE838979DFDEB3A168"
"BC9AFB27")},
"B2C2D2E2F680F122009B8588A8E9926339289BA373DB8479A71F7AA1164083D90613F"
"766D60B07CBC")},
// license being released. all fields are identical except for license
// state and hashed file data
{"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "", 0, 0, 0,
"", "",
"", "", 15,
a2bs_hex(
"0AEC150802100122E5150802121408011210303132333435363738394142434445461"
"0AEE150802100122E7150802121408011210303132333435363738394142434445461"
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
"7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82"
"6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD"
@@ -962,8 +968,8 @@ LicenseInfo license_update_test_data[] = {
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910158006230000102030405060708"
"090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2"
"B2C2D2E2F1220394BA01DFB519C1A7311115F8B2A0AC3141F981FFEA09FCD48A8EFA3"
"A045AAE6")}};
"B2C2D2E2F680F12202F5B77A3168AC2A81832231A435D0587F6D1DF3B905A7058C5E8"
"565C81B96CA6")}};
// Application parameters were added to the License message. This data
// is used to verify that a License saved without application parameters can
@@ -1065,7 +1071,7 @@ LicenseInfo license_app_parameters_backwards_compatibility_test_data = {
"0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF"
"A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0"
"BEA6CABACA1C2C"),
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", "",
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", "", 0,
a2bs_hex(
"0AA8150802100122A1150801121408011210303132333435363738394142434445461"
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
@@ -1154,11 +1160,12 @@ struct UsageInfo {
std::string license_request;
std::string license;
std::string usage_entry;
uint32_t usage_entry_number;
std::string file_data;
};
UsageInfo kUsageInfoTestData[] = {
{"", "", "", "", // 0 usage info records
{"", "", "", "", 0, // 0 usage info records
a2bs_hex("0A06080210012A00122095053501C5FA405B7EF01DA94685C6B20CB36493"
"A9CF1653B720E2BEA3B77929")},
{// 1 usage info record
@@ -1196,6 +1203,7 @@ UsageInfo kUsageInfoTestData[] = {
a2bs_hex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021"
"22232425262728292a2b2c2d2e2f"),
6,
a2bs_hex(
"0AB307080210012AAC070AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
@@ -1261,6 +1269,7 @@ UsageInfo kUsageInfoTestData[] = {
a2bs_hex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021"
"22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"),
14,
a2bs_hex(
"0ADF0E080210012AD80E0AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
@@ -1353,6 +1362,7 @@ UsageInfo kUsageInfoTestData[] = {
a2bs_hex(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021"
"22232425262728292a2b2c2d2e2f"),
19,
a2bs_hex(
"0A8B16080210012A84160AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
@@ -1465,88 +1475,82 @@ HlsAttributesInfo kHlsAttributesTestData[] = {
// Usage Table and Entry Test Data
// Note: Make sure the number of entries in kUsageEntriesTestData and
// kUsageTableInfoTestData are equal.
DeviceFiles::UsageEntryInfo kUsageEntriesTestData[] = {
CdmUsageEntryInfo kUsageEntriesTestData[] = {
// usage entry 0
{
DeviceFiles::kStorageLicense, "ksid0", "", "",
kStorageLicense, "ksid0", "",
},
// usage entry 1
{
DeviceFiles::kStorageLicense, "ksid1", "", "",
kStorageLicense, "ksid1", "",
},
// usage entry 2
{
DeviceFiles::kStorageUsageInfo, "", "provider_session_token_2",
"app_id_2",
kStorageUsageInfo, "", "app_id_2",
},
// usage entry 3
{
DeviceFiles::kStorageUsageInfo, "", "provider_session_token_3",
"app_id_3",
kStorageUsageInfo, "", "app_id_3",
},
// usage entry 4
{
DeviceFiles::kStorageLicense, "ksid4", "", "",
kStorageLicense, "ksid4", "",
},
// usage entry 5
{
DeviceFiles::kStorageUsageInfo, "", "provider_session_token_5",
"app_id_5",
kStorageUsageInfo, "", "app_id_5",
},
};
struct UsageTableTestInfo {
std::string usage_table_header;
CdmUsageTableHeader usage_table_header;
std::string file_data;
};
UsageTableTestInfo kUsageTableInfoTestData[] = {
// usage table 0
{a2bs_hex("5574517CCC"),
a2bs_hex("0A18080510013A120A055574517CCC1209080112056B73696430122018268E3F"
"384F28D04BEE00304089C000463C22E987532855390915FD02C36B5C")},
// usage table 1
{a2bs_hex("CA870203010001288001"),
a2bs_hex("0A28080510013A220A0ACA8702030100012880011209080112056B7369643012"
"09080112056B7369643112202D3638164ADC3B4276734A8EDE96C40BFE14DDB2"
"8013337A3A1A9DFC09F34923")},
a2bs_hex("0A2C080510013A260A0ACA870203010001288001120B080112056B736964301A"
"00120B080112056B736964311A00122049A8F3481444A5B64B6C4F05FBCC2EF8"
"CB67444A08654763F2F5B80F658D7B38")},
// usage table 2
{a2bs_hex("7A7D507618A5D3A68F05228E023082010A028201"),
a2bs_hex("0A5A080510013A540A147A7D507618A5D3A68F05228E023082010A0282011209"
"080112056B736964301209080112056B73696431122608021A1870726F766964"
"65725F73657373696F6E5F746F6B656E5F3222086170705F69645F321220CB07"
"CA08A1E76C61A5F45067176B960A9DB40D169025AF245CF1AFC66C979F47")},
a2bs_hex("0A46080510013A400A147A7D507618A5D3A68F05228E023082010A028201120B"
"080112056B736964301A00120B080112056B736964311A00120E080212001A08"
"6170705F69645F321220783E93A02223BDB94E743856C0F69C35B213ACCDDE91"
"93E48E9186AA83B80584")},
// usage table 3
{a2bs_hex("E83A4902772DAFD2740B7748E9C3B1752D6F12859CED07E82969B4EC"),
a2bs_hex("0A8B01080510013A84010A1CE83A4902772DAFD2740B7748E9C3B1752D6F1285"
"9CED07E82969B4EC1209080112056B736964301209080112056B736964311226"
"08021A1870726F76696465725F73657373696F6E5F746F6B656E5F3222086170"
"705F69645F32122608021A1870726F76696465725F73657373696F6E5F746F6B"
"656E5F3322086170705F69645F331220C4157F80E81C923A9F0885CE6B928D15"
"7E1648384C3E44F04A966815EB09B260")},
a2bs_hex("0A5E080510013A580A1CE83A4902772DAFD2740B7748E9C3B1752D6F12859CED"
"07E82969B4EC120B080112056B736964301A00120B080112056B736964311A00"
"120E080212001A086170705F69645F32120E080212001A086170705F69645F33"
"122084E67F1338727291BC3D92E28442DC8B0F44CB5AF7B98A799313B7EB7F55"
"ED18")},
// usage table 4
{a2bs_hex("CA870203010001288001300112800250D1F8B1ECF849B60FF93E37C4DEEF"
"52F1CCFC047EF42300131F9C4758F4"),
a2bs_hex("0AA701080510013AA0010A2DCA870203010001288001300112800250D1F8B1EC"
"F849B60FF93E37C4DEEF52F1CCFC047EF42300131F9C4758F41209080112056B"
"736964301209080112056B73696431122608021A1870726F76696465725F7365"
"7373696F6E5F746F6B656E5F3222086170705F69645F32122608021A1870726F"
"76696465725F73657373696F6E5F746F6B656E5F3322086170705F69645F3312"
"09080112056B7369643412203F75C53693E7A3DC9BA5BF3E23D7EFCF3C05687A"
"A6082E3AB78F563525981999")},
a2bs_hex("0A7C080510013A760A2DCA870203010001288001300112800250D1F8B1ECF849"
"B60FF93E37C4DEEF52F1CCFC047EF42300131F9C4758F4120B080112056B7369"
"64301A00120B080112056B736964311A00120E080212001A086170705F69645F"
"32120E080212001A086170705F69645F33120B080112056B736964341A001220"
"1CDFCFED5E58A1DF77E1B335305424E1F0260340F9CC15985684C43A4207652"
"1")},
// usage table 5
{a2bs_hex("EC83A4902772DAFD2740B7748E9C3B1752D6F12859CED07E8882969B433E"
"C29AC6FDBE79230B0FAED5D94CF6B829A420BBE3270323941776EE60DD6B"),
a2bs_hex("0ADE01080510013AD7010A3CEC83A4902772DAFD2740B7748E9C3B1752D6F128"
a2bs_hex("0A9C01080510013A95010A3CEC83A4902772DAFD2740B7748E9C3B1752D6F128"
"59CED07E8882969B433EC29AC6FDBE79230B0FAED5D94CF6B829A420BBE32703"
"23941776EE60DD6B1209080112056B736964301209080112056B736964311226"
"08021A1870726F76696465725F73657373696F6E5F746F6B656E5F3222086170"
"705F69645F32122608021A1870726F76696465725F73657373696F6E5F746F6B"
"656E5F3322086170705F69645F331209080112056B73696434122608021A1870"
"726F76696465725F73657373696F6E5F746F6B656E5F3522086170705F69645F"
"3512203B35BD5C615BBA79008A7A1DA29AFA69F5CD529DFDE794A0544E423B72"
"1CB8E8")},
"23941776EE60DD6B120B080112056B736964301A00120B080112056B73696431"
"1A00120E080212001A086170705F69645F32120E080212001A086170705F6964"
"5F33120B080112056B736964341A00120E080212001A086170705F69645F3512"
"20305C7A27A918268119E1996FC182C153DF805034A387F90C3585749E764731"
"32")},
};
class MockFile : public File {
@@ -1889,7 +1893,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
license_test_data[license_num].playback_start_time,
license_test_data[license_num].last_playback_time,
license_test_data[license_num].grace_period_end_time, app_parameters,
license_test_data[license_num].usage_entry));
license_test_data[license_num].usage_entry,
license_test_data[license_num].usage_entry_number));
}
INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, ::testing::Bool());
@@ -1939,7 +1944,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
license_test_data[i].playback_start_time,
license_test_data[i].last_playback_time,
license_test_data[i].grace_period_end_time, app_parameters,
license_test_data[i].usage_entry));
license_test_data[i].usage_entry,
license_test_data[i].usage_entry_number));
}
}
@@ -1981,6 +1987,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
std::string release_server_url;
CdmAppParameterMap app_parameters;
std::string usage_entry;
uint32_t usage_entry_number;
for (size_t i = 0; i < kNumberOfLicenses; i++) {
DeviceFiles::LicenseState license_state;
@@ -1989,7 +1996,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
&key_request, &key_response, &key_renewal_request,
&key_renewal_response, &release_server_url, &playback_start_time,
&last_playback_time, &grace_period_end_time, &app_parameters,
&usage_entry));
&usage_entry, &usage_entry_number));
EXPECT_EQ(license_test_data[i].license_state, license_state);
EXPECT_EQ(license_test_data[i].pssh_data, pssh_data);
EXPECT_EQ(license_test_data[i].key_request, key_request);
@@ -2001,6 +2008,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
EXPECT_EQ(license_test_data[i].grace_period_end_time,
grace_period_end_time);
EXPECT_EQ(license_test_data[i].usage_entry, usage_entry);
EXPECT_EQ(license_test_data[i].usage_entry_number, usage_entry_number);
std::map<std::string, std::string>::iterator itr;
for (itr = app_parameters.begin(); itr != app_parameters.end(); ++itr) {
@@ -2049,12 +2057,14 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
std::string release_server_url;
CdmAppParameterMap app_parameters;
std::string usage_entry;
uint32_t usage_entry_number;
EXPECT_TRUE(device_files.RetrieveLicense(
test_data->key_set_id, &license_state, &pssh_data, &key_request,
&key_response, &key_renewal_request, &key_renewal_response,
&release_server_url, &playback_start_time, &last_playback_time,
&grace_period_end_time, &app_parameters, &usage_entry));
&grace_period_end_time, &app_parameters, &usage_entry,
&usage_entry_number));
EXPECT_EQ(test_data->license_state, license_state);
EXPECT_EQ(test_data->pssh_data, pssh_data);
EXPECT_EQ(test_data->key_request, key_request);
@@ -2066,6 +2076,7 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
EXPECT_EQ(test_data->grace_period_end_time, grace_period_end_time);
EXPECT_EQ(0u, app_parameters.size());
EXPECT_EQ(test_data->usage_entry, usage_entry);
EXPECT_EQ(test_data->usage_entry_number, usage_entry_number);
}
TEST_F(DeviceFilesTest, UpdateLicenseState) {
@@ -2102,7 +2113,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
license_update_test_data[0].last_playback_time,
license_update_test_data[0].grace_period_end_time,
GetAppParameters(license_test_data[0].app_parameters),
license_update_test_data[0].usage_entry));
license_update_test_data[0].usage_entry,
license_update_test_data[0].usage_entry_number));
EXPECT_TRUE(device_files.StoreLicense(
license_update_test_data[0].key_set_id,
@@ -2117,7 +2129,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
license_update_test_data[0].last_playback_time,
license_update_test_data[0].grace_period_end_time,
GetAppParameters(license_test_data[0].app_parameters),
license_update_test_data[0].usage_entry));
license_update_test_data[0].usage_entry,
license_update_test_data[0].usage_entry_number));
}
TEST_F(DeviceFilesTest, DeleteLicense) {
@@ -2158,12 +2171,14 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
int64_t playback_start_time, last_playback_time, grace_period_end_time;
CdmAppParameterMap app_parameters;
std::string usage_entry;
uint32_t usage_entry_number;
EXPECT_TRUE(device_files.RetrieveLicense(
license_test_data[0].key_set_id, &license_state, &pssh_data, &key_request,
&key_response, &key_renewal_request, &key_renewal_response,
&release_server_url, &playback_start_time, &last_playback_time,
&grace_period_end_time, &app_parameters, &usage_entry));
&grace_period_end_time, &app_parameters, &usage_entry,
&usage_entry_number));
EXPECT_EQ(license_test_data[0].license_state, license_state);
EXPECT_EQ(license_test_data[0].pssh_data, pssh_data);
EXPECT_EQ(license_test_data[0].key_request, key_request);
@@ -2181,6 +2196,7 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
std::string::npos);
}
EXPECT_EQ(license_test_data[0].usage_entry, usage_entry);
EXPECT_EQ(license_test_data[0].usage_entry_number, usage_entry_number);
EXPECT_TRUE(device_files.DeleteLicense(license_test_data[0].key_set_id));
EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id));
@@ -2235,7 +2251,9 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
ASSERT_TRUE(device_files.RetrieveUsageInfo(app_id, &license_info));
ASSERT_TRUE(device_files.RetrieveUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
&license_info));
if (index >= 0) {
EXPECT_EQ(static_cast<size_t>(index), license_info.size());
for (size_t i = 0; i < license_info.size(); ++i) {
@@ -2263,6 +2281,7 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
std::string license(GenerateRandomData(kLicenseLen));
std::string key_set_id(GenerateRandomData(kKeySetIdLen));
std::string usage_entry(GenerateRandomData(kUsageEntryLen));
uint32_t usage_entry_number = 16;
std::string path =
device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
@@ -2300,8 +2319,10 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
ASSERT_TRUE(device_files.StoreUsageInfo(pst, license_request, license, app_id,
key_set_id, usage_entry));
ASSERT_TRUE(device_files.StoreUsageInfo(
pst, license_request, license,
DeviceFiles::GetUsageInfoFileName(app_id),
key_set_id, usage_entry, usage_entry_number));
}
TEST_P(DeviceFilesUsageInfoTest, Delete) {
@@ -2349,9 +2370,13 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
if (index >= 1) {
ASSERT_TRUE(device_files.DeleteUsageInfo(app_id, pst));
ASSERT_TRUE(device_files.DeleteUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
pst));
} else {
ASSERT_FALSE(device_files.DeleteUsageInfo(app_id, pst));
ASSERT_FALSE(device_files.DeleteUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
pst));
}
}
@@ -2385,7 +2410,9 @@ TEST_P(DeviceFilesUsageInfoTest, DeleteAll) {
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
std::vector<std::string> psts;
ASSERT_TRUE(device_files.DeleteAllUsageInfoForApp(app_id, &psts));
ASSERT_TRUE(device_files.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&psts));
if (index < 0) {
EXPECT_EQ(0u, psts.size());
} else {
@@ -2479,20 +2506,14 @@ TEST_P(DeviceFilesUsageTableTest, Store) {
size_t entry_data_length = 0;
std::vector<std::string> entry_data;
std::vector<DeviceFiles::UsageEntryInfo> usage_entry_info;
std::vector<CdmUsageEntryInfo> usage_entry_info;
usage_entry_info.resize(index + 1);
for (int i = 0; i <= index; ++i) {
usage_entry_info[i] = kUsageEntriesTestData[i];
if (kUsageEntriesTestData[i].storage_type == DeviceFiles::kStorageLicense) {
entry_data.push_back(kUsageEntriesTestData[i].key_set_id);
entry_data_length += kUsageEntriesTestData[i].key_set_id.size();
} else {
entry_data.push_back(kUsageEntriesTestData[i].provider_session_token);
entry_data.push_back(kUsageEntriesTestData[i].app_id);
entry_data_length +=
kUsageEntriesTestData[i].provider_session_token.size() +
kUsageEntriesTestData[i].app_id.size();
}
entry_data.push_back(kUsageEntriesTestData[i].key_set_id);
entry_data.push_back(kUsageEntriesTestData[i].usage_info_file_name);
entry_data_length += kUsageEntriesTestData[i].key_set_id.size() +
kUsageEntriesTestData[i].usage_info_file_name.size();
}
entry_data.push_back(kUsageTableInfoTestData[index].usage_table_header);
entry_data_length += kUsageTableInfoTestData[index].usage_table_header.size();
@@ -2516,7 +2537,7 @@ TEST_P(DeviceFilesUsageTableTest, Store) {
TEST_P(DeviceFilesUsageTableTest, Read) {
MockFileSystem file_system;
MockFile file;
int index = GetParam();
size_t index = GetParam();
std::string path = device_base_path_ + DeviceFiles::GetUsageTableFileName();
@@ -2535,28 +2556,22 @@ TEST_P(DeviceFilesUsageTableTest, Read) {
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
std::vector<DeviceFiles::UsageEntryInfo> usage_entry_info;
std::string usage_table_header;
std::vector<CdmUsageEntryInfo> usage_entry_info;
CdmUsageTableHeader usage_table_header;
ASSERT_TRUE(device_files.RetrieveUsageTableInfo(&usage_table_header,
&usage_entry_info));
EXPECT_EQ(kUsageTableInfoTestData[index].usage_table_header,
usage_table_header);
EXPECT_EQ(index + 1u, usage_entry_info.size());
for (int i = 0; i <= index; ++i) {
for (size_t i = 0; i <= index; ++i) {
EXPECT_EQ(kUsageEntriesTestData[i].storage_type,
usage_entry_info[i].storage_type);
if (usage_entry_info[i].storage_type == DeviceFiles::kStorageLicense) {
EXPECT_EQ(kUsageEntriesTestData[i].key_set_id,
usage_entry_info[i].key_set_id);
EXPECT_EQ(kEmptyString, usage_entry_info[i].provider_session_token);
EXPECT_EQ(kEmptyString, usage_entry_info[i].app_id);
} else {
EXPECT_EQ(kEmptyString, usage_entry_info[i].key_set_id);
EXPECT_EQ(kUsageEntriesTestData[i].provider_session_token,
usage_entry_info[i].provider_session_token);
EXPECT_EQ(kUsageEntriesTestData[i].app_id, usage_entry_info[i].app_id);
}
EXPECT_EQ(kUsageEntriesTestData[i].key_set_id,
usage_entry_info[i].key_set_id);
EXPECT_EQ(
kUsageEntriesTestData[i].usage_info_file_name,
usage_entry_info[i].usage_info_file_name);
}
}

View File

@@ -25,7 +25,50 @@ namespace {
const std::string kKeySystem = "com.widevine.alpha";
std::string g_provisioning_server;
std::string g_service_certificate;
std::string g_license_service_certificate;
std::string g_provisioning_service_certificate;
/*
* Locate the portion of the server's response message that is between
* the strings jason_start_substr and json_end_substr. Returns the string
* through *result. If the start substring match fails, assume the entire
* string represents a serialized protobuf mesaage and return true with
* the entire string. If the end_substring match fails, return false with
* an empty *result.
*/
bool ExtractSignedMessage(const std::string& response,
const std::string& json_start_substr,
const std::string& json_end_substr,
std::string* result) {
std::string b64_string;
size_t start = response.find(json_start_substr);
if (start == response.npos) {
// Assume web safe protobuf
b64_string.assign(response);
} else {
// Assume JSON-wrapped protobuf
size_t end = response.find(json_end_substr,
start + json_start_substr.length());
if (end == response.npos) {
LOGE("ExtractSignedMessage cannot locate end substring");
result->clear();
return false;
}
size_t b64_string_size = end - start - json_start_substr.length();
b64_string.assign(response, start + json_start_substr.length(),
b64_string_size);
}
if (b64_string.empty()) {
LOGE("Response message is empty");
result->clear();
return false;
}
result->swap(b64_string);
return true;
}
} // namespace
@@ -38,13 +81,11 @@ class WvGenericOperationsTest : public testing::Test {
virtual void SetUp() {
::testing::Test::SetUp();
#if defined(QA_TEST_SERVER)
ConfigTestEnv config(kContentProtectionTestQAServer);
#else
ConfigTestEnv config(kContentProtectionUatServer);
#endif // !defined(QA_TEST_SERVER)
ConfigTestEnv config(kContentProtectionStagingPlusProv30);
g_service_certificate.assign(config.service_certificate());
g_provisioning_service_certificate.assign(
config.provisioning_service_certificate());
g_license_service_certificate.assign(config.license_service_certificate());
g_provisioning_server.assign(config.provisioning_server());
cdm_engine_ = NULL;
@@ -52,15 +93,13 @@ class WvGenericOperationsTest : public testing::Test {
// TODO(fredgc or gmorgan): This should be updated for provisioning 3.0
// Load test keybox. This keybox will be used by any CryptoSession
// created by the CDM under test.
#if defined(PROVISIONING_30)
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey());
#else
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox());
#endif // !defined(PROVISIONING_30)
// Perform CdmEngine setup
cdm_engine_ = new CdmEngine(&file_system_);
Provision();
CdmResponseType status =
cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
@@ -177,29 +216,55 @@ class WvGenericOperationsTest : public testing::Test {
}
protected:
void Provision() {
virtual void Provision() {
LOGE("WvCdmEnginePreProvTest::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
cdm_engine_->SetServiceCertificate(g_service_certificate);
ASSERT_EQ(NO_ERROR,
cdm_engine_->GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
ASSERT_EQ(NO_ERROR, cdm_engine_->SetServiceCertificate(
g_provisioning_service_certificate));
ASSERT_EQ(NO_ERROR, cdm_engine_->GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok);
LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n",
http_message.c_str());
// extract provisioning response from received message
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string base64_response;
EXPECT_TRUE (ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
&base64_response)) <<
"Failed to extract signed serialized response from JSON response";
LOGV("WvCdmEnginePreProvTest::Provision: extracted response "
"message: \n%s\n", base64_response.c_str());
ASSERT_EQ(NO_ERROR,
cdm_engine_->HandleProvisioningResponse(message, &cert,
&wrapped_key));
cdm_engine_->HandleProvisioningResponse(base64_response,
&cert, &wrapped_key));
ASSERT_EQ(NO_ERROR,
cdm_engine_->SetServiceCertificate(
g_license_service_certificate));
}
// This CryptoSession object handles Initialization and Termination

View File

@@ -47,7 +47,8 @@ wvcdm::KeyId g_key_id;
wvcdm::CdmKeySystem g_key_system;
std::string g_license_server;
wvcdm::KeyId g_wrong_key_id;
wvcdm::LicenseServerId g_license_server_id = wvcdm::kContentProtectionUatServer;
wvcdm::ServerConfigurationId g_license_server_id =
wvcdm::kContentProtectionUatServer;
// TODO(rfrias): refactor to print out the decryption test names
struct SubSampleInfo {
@@ -940,7 +941,9 @@ TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) {
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::vector<std::string> provider_session_tokens;
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp("", &provider_session_tokens));
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(""),
&provider_session_tokens));
for (size_t i = 0; i < kMaxUsageTableSize + 100; ++i) {
decryptor_.OpenSession(g_key_system, property_set, kDefaultCdmIdentifier,

View File

@@ -66,7 +66,8 @@ wvcdm::KeyId g_key_id;
wvcdm::CdmKeySystem g_key_system;
std::string g_license_server;
wvcdm::KeyId g_wrong_key_id;
wvcdm::LicenseServerId g_license_server_id = wvcdm::kContentProtectionUatServer;
wvcdm::ServerConfigurationId g_license_server_id =
wvcdm::kContentProtectionUatServer;
std::string g_service_certificate;
// TODO(rfrias): refactor to print out the decryption test names
@@ -2378,7 +2379,9 @@ TEST_P(WvCdmUsageTest, WithClientId) {
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::vector<std::string> psts;
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(app_id, &psts));
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&psts));
RenewWithClientIdTestConfiguration* config = GetParam();
std::string key_id;
@@ -2496,7 +2499,9 @@ TEST_F(WvCdmRequestLicenseTest, UsageInfoRetryTest) {
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::vector<std::string> psts;
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(app_id, &psts));
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&psts));
SubSampleInfo* data = &usage_info_sub_samples_icp[0];
decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL,
@@ -2579,7 +2584,9 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) {
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::vector<std::string> psts;
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(usage_info_data->app_id, &psts));
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(usage_info_data->app_id),
&psts));
for (size_t i = 0; i < usage_info_data->usage_info; ++i) {
SubSampleInfo* data = usage_info_data->sub_sample + i;
@@ -2653,7 +2660,8 @@ TEST_F(WvCdmRequestLicenseTest, UsageReleaseAllTest) {
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::vector<std::string> psts;
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp("", &psts));
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(""), &psts));
for (size_t i = 0; i < N_ELEM(usage_info_sub_samples_icp); ++i) {
SubSampleInfo* data = usage_info_sub_samples_icp + i;