Source release v3.0.4

This commit is contained in:
Joey Parrish
2015-12-14 10:01:38 -08:00
parent 3d5571eaa1
commit 973eb25a17
21 changed files with 734 additions and 191 deletions

View File

@@ -146,6 +146,8 @@ class CdmEngine {
virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id);
virtual CdmResponseType ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message);
virtual CdmResponseType LoadUsageSession(const CdmKeySetId& key_set_id,
CdmKeyMessage* release_message);
// Decryption and key related methods
// Accept encrypted buffer and return decrypted data.
@@ -173,6 +175,7 @@ class CdmEngine {
private:
// private methods
void DeleteAllUsageReportsUponFactoryReset();
bool ValidateKeySystem(const CdmKeySystem& key_system);
CdmResponseType GetUsageInfo(const std::string& app_id,
SecurityLevel requested_security_level,

View File

@@ -104,6 +104,8 @@ class CdmSession {
virtual bool is_release() { return is_release_; }
virtual bool is_offline() { return is_offline_; }
virtual bool is_temporary() { return is_temporary_; }
virtual bool license_received() { return license_received_; }
// ReleaseCrypto() - Closes the underlying crypto session but leaves this
// object alive. It is invalid to call any method that requires a crypto
@@ -141,6 +143,7 @@ class CdmSession {
bool license_received_;
bool is_offline_;
bool is_release_;
bool is_temporary_;
CdmSecurityLevel security_level_;
SecurityLevel requested_security_level_;
CdmAppParameterMap app_parameters_;

View File

@@ -67,11 +67,13 @@ class DeviceFiles {
virtual bool DeleteAllLicenses();
virtual bool LicenseExists(const std::string& key_set_id);
virtual bool ReserveLicenseId(const std::string& key_set_id);
virtual bool UnreserveLicenseId(const std::string& key_set_id);
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& app_id,
const std::string& key_set_id);
virtual bool DeleteUsageInfo(const std::string& app_id,
const std::string& provider_session_token);
// Delete usage information from the file system. Puts a list of all the
@@ -90,6 +92,12 @@ class DeviceFiles {
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response);
// 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);
private:
// Helpers that wrap the File interface and automatically handle hashing, as

View File

@@ -56,6 +56,10 @@ class CdmLicense {
return provider_session_token_;
}
virtual bool is_offline() {
return is_offline_;
}
static CdmResponseType VerifySignedServiceCertificate(
const std::string& signed_service_certificate);
@@ -89,6 +93,7 @@ class CdmLicense {
std::set<KeyId> loaded_keys_;
std::string provider_session_token_;
bool renew_with_client_id_;
bool is_offline_;
// Used for certificate based licensing
CdmKeyMessage key_request_;

View File

@@ -207,6 +207,12 @@ enum CdmResponseType {
DUPLICATE_SESSION_ID_SPECIFIED,
LICENSE_RENEWAL_PROHIBITED,
EMPTY_PROVISIONING_CERTIFICATE_2,
OFFLINE_LICENSE_PROHIBITED,
STORAGE_PROHIBITED,
EMPTY_KEYSET_ID_ENG_5,
SESSION_NOT_FOUND_11,
LOAD_USAGE_INFO_FILE_ERROR,
LOAD_USAGE_INFO_MISSING,
};
enum CdmKeyStatus {
@@ -229,6 +235,9 @@ enum CdmLicenseType {
// If the original request was saved to make a service certificate request,
// use Deferred for the license type in the subsequent request.
kLicenseTypeDeferred,
// Like Streaming, but stricter. Does not permit storage of any kind.
// Named after the 'temporary' session type in EME, which has this behavior.
kLicenseTypeTemporary,
};
enum SecurityLevel {

View File

@@ -2,6 +2,7 @@
#include "cdm_engine.h"
#include <assert.h>
#include <stdlib.h>
#include <iostream>
@@ -10,6 +11,7 @@
#include "cdm_session.h"
#include "clock.h"
#include "device_files.h"
#include "file_store.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
@@ -227,7 +229,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
key_request->clear();
if (license_type == kLicenseTypeRelease) {
if (license_type == kLicenseTypeRelease &&
!iter->second->license_received()) {
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
if (sts != KEY_ADDED) {
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
@@ -338,7 +341,7 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
}
return sts; // TODO ewew
return sts;
}
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
@@ -602,6 +605,9 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
return INVALID_PROVISIONING_REQUEST_PARAM_2;
}
DeleteAllUsageReportsUponFactoryReset();
if (NULL == cert_provisioning_.get()) {
cert_provisioning_.reset(new CertificateProvisioning());
}
@@ -663,6 +669,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
}
return NO_ERROR;
}
CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
origin, response, cert, wrapped_key);
// Release resources only on success. It is possible that a provisioning
@@ -928,6 +935,66 @@ CdmResponseType CdmEngine::ReleaseUsageInfo(
return status;
}
CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
CdmKeyMessage* release_message) {
LOGI("CdmEngine::LoadUsageSession");
// This method is currently only used by the CE CDM, in which all session IDs
// are key set IDs.
assert(Properties::AlwaysUseKeySetIds());
if (key_set_id.empty()) {
LOGE("CdmEngine::LoadUsageSession: invalid key set id");
return EMPTY_KEYSET_ID_ENG_5;
}
CdmSessionMap::iterator iter = sessions_.find(key_set_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::LoadUsageSession: session_id not found = %s ",
key_set_id.c_str());
return SESSION_NOT_FOUND_11;
}
DeviceFiles handle;
if (!handle.Init(iter->second->GetSecurityLevel())) {
LOGE("CdmEngine::LoadUsageSession: unable to initialize device files");
return LOAD_USAGE_INFO_FILE_ERROR;
}
std::string app_id;
iter->second->GetApplicationId(&app_id);
CdmKeyMessage key_message;
CdmKeyResponse key_response;
if (!handle.RetrieveUsageInfoByKeySetId(app_id, key_set_id, &key_message,
&key_response)) {
LOGE("CdmEngine::LoadUsageSession: unable to find usage information");
return LOAD_USAGE_INFO_MISSING;
}
CdmResponseType status =
iter->second->RestoreUsageSession(key_message, key_response);
if (KEY_ADDED != status) {
LOGE("CdmEngine::LoadUsageSession: usage session error %ld", status);
return status;
}
std::string server_url;
status = iter->second->GenerateReleaseRequest(release_message, &server_url);
switch (status) {
case KEY_MESSAGE:
break;
case KEY_CANCELED: // usage information not present in
iter->second->DeleteLicense(); // OEMCrypto, delete and try again
break;
default:
LOGE("CdmEngine::LoadUsageSession: generate release request error: %d",
status);
break;
}
return status;
}
CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters) {
if (parameters.key_id == NULL) {
@@ -1097,4 +1164,34 @@ std::string CdmEngine::MapHdcpVersion(
return "";
}
void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
std::string device_base_path_level1 = "";
std::string device_base_path_level3 = "";
Properties::GetDeviceFilesBasePath(kSecurityLevelL1,
&device_base_path_level1);
Properties::GetDeviceFilesBasePath(kSecurityLevelL3,
&device_base_path_level3);
File file;
if (!file.Exists(device_base_path_level1) &&
!file.Exists(device_base_path_level3)) {
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
cert_provisioning_requested_security_level_);
if (NO_ERROR == status) {
status = crypto_session->DeleteAllUsageReports();
if (NO_ERROR != status) {
LOGW(
"CdmEngine::GetProvisioningRequest: "
"Fails to delete usage reports: %d", status);
}
} else {
LOGW(
"CdmEngine::GetProvisioningRequest: "
"Fails to open crypto session: error=%d.\n"
"Usage reports are not removed after factory reset.", status);
}
}
}
} // namespace wvcdm

View File

@@ -37,6 +37,7 @@ CdmSession::CdmSession(CdmClientPropertySet* cdm_client_property_set,
license_received_(false),
is_offline_(false),
is_release_(false),
is_temporary_(false),
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_decryption_(true),
@@ -66,7 +67,13 @@ CdmSession::CdmSession(CdmClientPropertySet* cdm_client_property_set,
}
}
CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); }
CdmSession::~CdmSession() {
if (!key_set_id_.empty()) {
// Unreserve the license ID.
file_handle_->UnreserveLicenseId(key_set_id_);
}
Properties::RemoveSessionPropertySet(session_id_);
}
CdmResponseType CdmSession::Init() {
if (session_id_.empty()) {
@@ -184,6 +191,9 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
switch (license_type) {
case kLicenseTypeTemporary:
is_temporary_ = true;
break;
case kLicenseTypeStreaming:
is_offline_ = false;
break;
@@ -207,6 +217,8 @@ CdmResponseType CdmSession::GenerateKeyRequest(
license_type = kLicenseTypeRelease;
} else if (is_offline_) {
license_type = kLicenseTypeOffline;
} else if (is_temporary_) {
license_type = kLicenseTypeTemporary;
} else {
license_type = kLicenseTypeStreaming;
}
@@ -484,12 +496,22 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
}
CdmResponseType CdmSession::StoreLicense() {
if (is_temporary_) {
LOGE("CdmSession::StoreLicense: Session type prohibits storage.");
return STORAGE_PROHIBITED;
}
if (is_offline_) {
if (key_set_id_.empty()) {
LOGE("CdmSession::StoreLicense: No key set ID");
return EMPTY_KEYSET_ID;
}
if (!license_parser_->is_offline()) {
LOGE("CdmSession::StoreLicense: License policy prohibits storage.");
return OFFLINE_LICENSE_PROHIBITED;
}
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::StoreLicense: Unable to store license");
CdmResponseType sts = Init();
@@ -502,7 +524,7 @@ CdmResponseType CdmSession::StoreLicense() {
return STORE_LICENSE_ERROR_1;
}
return NO_ERROR;
}
} // if (is_offline_)
std::string provider_session_token =
license_parser_->provider_session_token();
@@ -519,7 +541,7 @@ CdmResponseType CdmSession::StoreLicense() {
std::string app_id;
GetApplicationId(&app_id);
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
key_response_, app_id)) {
key_response_, app_id, key_set_id_)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return STORE_USAGE_INFO_ERROR;
}

View File

@@ -343,10 +343,20 @@ bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
return true;
}
bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) {
if (!initialized_) {
LOGW("DeviceFiles::UnreserveLicenseId: not initialized");
return false;
}
reserved_license_ids_.erase(key_set_id);
return true;
}
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const std::string& app_id) {
const std::string& app_id,
const std::string& key_set_id) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
@@ -372,6 +382,7 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
provider_session_token.size());
provider_session->set_license_request(key_request.data(), key_request.size());
provider_session->set_license(key_response.data(), key_response.size());
provider_session->set_key_set_id(key_set_id.data(), key_set_id.size());
file.SerializeToString(&serialized_file);
return StoreFileWithHash(file_name, serialized_file);
@@ -513,22 +524,48 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
return false;
}
int index = 0;
bool found = false;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).token() == provider_session_token) {
found = true;
break;
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
return true;
}
}
if (!found) {
return false;
}
bool DeviceFiles::RetrieveUsageInfoByKeySetId(
const std::string& app_id,
const std::string& key_set_id,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: not initialized");
return false;
}
std::string serialized_file;
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveHashedFile(file_name, &serialized_file)) return false;
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: Unable to parse file");
return false;
}
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
return true;
int index = 0;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).key_set_id() == key_set_id) {
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
return true;
}
}
return false;
}
bool DeviceFiles::StoreFileWithHash(const std::string& name,

View File

@@ -46,6 +46,7 @@ message UsageInfo {
optional bytes token = 1;
optional bytes license_request = 2;
optional bytes license = 3;
optional bytes key_set_id = 4;
}
repeated ProviderSession sessions = 1;

View File

@@ -132,6 +132,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
clock_(new Clock()) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
@@ -139,7 +140,8 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
policy_engine_(NULL),
session_id_(session_id),
initialized_(false),
renew_with_client_id_(false) {
renew_with_client_id_(false),
is_offline_(false) {
clock_.reset(clock);
}
@@ -522,6 +524,10 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return NO_CONTENT_KEY;
}
if (license.id().type() == video_widevine_server::sdk::OFFLINE &&
license.policy().can_persist())
is_offline_ = true;
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
@@ -1102,6 +1108,7 @@ bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
break;
case kLicenseTypeStreaming:
case kLicenseTypeTemporary:
content_id->set_license_type(video_widevine_server::sdk::STREAMING);
break;
default:

View File

@@ -200,7 +200,7 @@ TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, DISABLED_BaseWebmMessageTest) {
TEST_F(WvCdmEngineTest, BaseWebmMessageTest) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
@@ -220,7 +220,7 @@ TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, DISABLED_NormalDecryptionWebm) {
TEST_F(WvCdmEngineTest, NormalDecryptionWebm) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
}

View File

@@ -21,6 +21,7 @@ const uint32_t kProtobufEstimatedOverhead = 75;
const uint32_t kLicenseRequestLen = 300;
const uint32_t kLicenseLen = 500;
const uint32_t kProviderSessionTokenLen = 128;
const uint32_t kKeySetIdLen = 20;
// Structurally valid test certificate.
// The data elements in this module are used to test the storage and
@@ -1536,6 +1537,16 @@ MATCHER_P4(Contains, str1, str2, str3, size, "") {
data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos);
}
MATCHER_P5(Contains, str1, str2, str3, str4, size, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, size + str1.size() + str2.size() + str3.size() +
str4.size() + kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos &&
data.find(str4) != std::string::npos);
}
MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
@@ -1549,7 +1560,6 @@ MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") {
data.find(str5) != std::string::npos &&
data.find(str6) != std::string::npos);
}
MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
@@ -2140,6 +2150,8 @@ TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) {
EXPECT_TRUE(device_files.ReserveLicenseId(license_test_data[i].key_set_id));
// Validate that the license IDs are actually reserved.
EXPECT_TRUE(device_files.LicenseExists(license_test_data[i].key_set_id));
// Unreserve these IDs to avoid polluting other tests.
EXPECT_TRUE(device_files.UnreserveLicenseId(license_test_data[i].key_set_id));
}
}
@@ -2205,6 +2217,7 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
std::string pst(GenerateRandomData(kProviderSessionTokenLen));
std::string license_request(GenerateRandomData(kLicenseRequestLen));
std::string license(GenerateRandomData(kLicenseLen));
std::string key_set_id(GenerateRandomData(kKeySetIdLen));
std::string path =
device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
@@ -2234,8 +2247,10 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
}
EXPECT_CALL(file,
Write(Contains(pst, license_request, license, data.size()),
Gt(pst.size() + license_request.size() + license.size())))
Write(Contains(pst, license_request, license, key_set_id,
data.size()),
Gt(pst.size() + license_request.size() + license.size() +
key_set_id.size())))
.WillOnce(ReturnArg<1>());
DeviceFiles device_files;
@@ -2243,7 +2258,8 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
device_files.SetTestFile(&file);
ASSERT_TRUE(
device_files.StoreUsageInfo(pst, license_request, license, app_id));
device_files.StoreUsageInfo(pst, license_request, license, app_id,
key_set_id));
}
TEST_P(DeviceFilesUsageInfoTest, Delete) {