Merge "Allows sharing of keys between sessions" into klp-dev

This commit is contained in:
Jeff Tinker
2013-10-11 17:02:00 +00:00
committed by Android (Google) Code Review
14 changed files with 308 additions and 34 deletions

View File

@@ -16,6 +16,9 @@ class CdmClientPropertySet {
virtual std::string security_level() const = 0;
virtual bool use_privacy_mode() const = 0;
virtual std::vector<uint8_t> service_certificate() const = 0;
virtual bool is_session_sharing_enabled() const = 0;
virtual uint32_t session_sharing_id() const = 0;
virtual void set_session_sharing_id(uint32_t id) = 0;
};
} // namespace wvcdm

View File

@@ -93,6 +93,7 @@ class CdmEngine : public TimerHandler {
// Is the key known to any session?
virtual bool IsKeyValid(const KeyId& key_id);
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
// Event listener related methods
virtual bool AttachEventListener(const CdmSessionId& session_id,

View File

@@ -3,6 +3,8 @@
#ifndef CDM_BASE_LICENSE_H_
#define CDM_BASE_LICENSE_H_
#include <set>
#include "wv_cdm_types.h"
namespace video_widevine_server {
@@ -41,6 +43,7 @@ class CdmLicense {
CdmKeyResponse& license_response,
CdmKeyResponse& license_renewal_response);
bool HasInitData() { return !init_data_.empty(); }
bool IsKeyLoaded(const KeyId& key_id);
private:
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
@@ -58,6 +61,7 @@ class CdmLicense {
std::string service_certificate_;
std::string init_data_;
bool initialized_;
std::set<KeyId> loaded_keys_;
// Used for certificate based licensing
CdmKeyMessage key_request_;

View File

@@ -66,6 +66,7 @@ class Properties {
static const std::vector<uint8_t> GetServiceCertificate(
const CdmSessionId& session_id);
static bool UsePrivacyMode(const CdmSessionId& session_id);
static uint32_t GetSessionSharingId(const CdmSessionId& session_id);
static bool AddSessionPropertySet(
const CdmSessionId& session_id,

View File

@@ -525,6 +525,29 @@ bool CdmEngine::IsKeyValid(const KeyId& key_id) {
return false;
}
bool CdmEngine::FindSessionForKey(
const KeyId& key_id,
CdmSessionId* session_id) {
if (NULL == session_id) {
LOGE("CdmEngine::FindSessionForKey: session id not provided");
return false;
}
uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id);
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
CdmSessionId id = iter->second->session_id();
if (Properties::GetSessionSharingId(id) == session_sharing_id) {
if (iter->second->IsKeyValid(key_id)) {
*session_id = id;
return true;
}
}
}
return false;
}
bool CdmEngine::AttachEventListener(
const CdmSessionId& session_id,
WvCdmEventListener* listener) {

View File

@@ -324,22 +324,7 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
}
bool CdmSession::IsKeyValid(const KeyId& key_id) {
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
return false;
if (key_id_ != key_id) {
// OEMCrypto does not provide a way to query the existence/validity of a
// key. SelectKey can be used to check whether a key is valid, but there
// is also a side effect - the key is selected for decryption, which might
// be undesirable and it posts restriction on the use of IsKeyValid API.
// TODO(kqyang, gmorgan): consider adding a function in OEMCrypto to check
// if a key is valid.
if (!crypto_session_->SelectKey(key_id)) {
return false;
}
key_id_ = key_id;
}
return true;
return license_parser_.IsKeyLoaded(key_id);
}
CdmSessionId CdmSession::GenerateSessionId() {

View File

@@ -539,9 +539,22 @@ CdmResponseType CdmLicense::HandleKeyResponse(
// merge from Eureka)
policy_engine_->SetLicense(license);
return session_->LoadKeys(signed_response.msg(), signed_response.signature(),
mac_key_iv, mac_key, key_array.size(),
&key_array[0]);
CdmResponseType resp = session_->LoadKeys(signed_response.msg(),
signed_response.signature(),
mac_key_iv,
mac_key,
key_array.size(),
&key_array[0]);
if (KEY_ADDED == resp) {
loaded_keys_.clear();
for (std::vector<CryptoKey>::iterator it = key_array.begin();
it != key_array.end();
++it) {
loaded_keys_.insert(it->key_id());
}
}
return resp;
}
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
@@ -753,4 +766,8 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
}
}
bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}
} // namespace wvcdm

View File

@@ -100,6 +100,17 @@ bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
return property_set->use_privacy_mode();
}
uint32_t Properties::GetSessionSharingId(const CdmSessionId& session_id) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
LOGE("Properties::GetSessionSharingId: cannot find property set for %s",
session_id.c_str());
return 0;
}
return property_set->session_sharing_id();
}
bool Properties::GetSecurityLevelDirectories(std::vector<std::string>* dirs) {
dirs->resize(sizeof(kSecurityLevelDirs)/sizeof(const char*));
for (size_t i = 0; i < dirs->size(); ++i) {

View File

@@ -21,7 +21,7 @@ class WvContentDecryptionModule {
// Session related methods
virtual CdmResponseType OpenSession(
const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
CdmClientPropertySet* property_set,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
@@ -84,6 +84,7 @@ class WvContentDecryptionModule {
WvCdmEventListener* listener);
private:
uint32_t GenerateSessionSharingId();
// instance variables
UniquePtr<CdmEngine> cdm_engine_;

View File

@@ -4,8 +4,10 @@
#include <iostream>
#include "cdm_client_property_set.h"
#include "cdm_engine.h"
#include "log.h"
#include "properties.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
@@ -18,8 +20,13 @@ WvContentDecryptionModule::~WvContentDecryptionModule() {}
CdmResponseType WvContentDecryptionModule::OpenSession(
const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
CdmClientPropertySet* property_set,
CdmSessionId* session_id) {
if (property_set && property_set->is_session_sharing_enabled()) {
if (property_set->session_sharing_id() == 0)
property_set->set_session_sharing_id(GenerateSessionSharingId());
}
return cdm_engine_->OpenSession(key_system, property_set, session_id);
}
@@ -111,7 +118,15 @@ CdmResponseType WvContentDecryptionModule::ReleaseSecureStops(
CdmResponseType WvContentDecryptionModule::Decrypt(
const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters) {
return cdm_engine_->Decrypt(session_id, parameters);
CdmSessionId id = session_id;
if (Properties::GetSessionSharingId(session_id) != 0) {
bool status = cdm_engine_->FindSessionForKey(*parameters.key_id, &id);
if (!status) {
LOGE("WvContentDecryptionModule::Decrypt: unable to find session");
return KEY_ERROR;
}
}
return cdm_engine_->Decrypt(id, parameters);
}
bool WvContentDecryptionModule::AttachEventListener(
@@ -124,4 +139,9 @@ bool WvContentDecryptionModule::DetachEventListener(
return cdm_engine_->DetachEventListener(session_id, listener);
}
uint32_t WvContentDecryptionModule::GenerateSessionSharingId() {
static int next_session_sharing_id = 0;
return ++next_session_sharing_id;
}
} // namespace wvcdm

View File

@@ -205,7 +205,10 @@ SubSampleInfo partial_offset_single_encrypted_sub_sample = {
namespace wvcdm {
class TestWvCdmClientPropertySet : public CdmClientPropertySet {
public:
TestWvCdmClientPropertySet() : use_privacy_mode_(false) {}
TestWvCdmClientPropertySet()
: use_privacy_mode_(false),
is_session_sharing_enabled_(false),
session_sharing_id_(0) {}
virtual ~TestWvCdmClientPropertySet() {}
virtual std::string security_level() const { return security_level_; }
@@ -213,6 +216,10 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
return service_certificate_;
}
virtual bool use_privacy_mode() const { return use_privacy_mode_; }
bool is_session_sharing_enabled() const {
return is_session_sharing_enabled_;
}
uint32_t session_sharing_id() const { return session_sharing_id_; }
void set_security_level(const std::string& security_level) {
if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
@@ -227,11 +234,17 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
void set_use_privacy_mode(bool use_privacy_mode) {
use_privacy_mode_ = use_privacy_mode;
}
void set_session_sharing_mode(bool enable) {
is_session_sharing_enabled_ = enable;
}
void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; }
private:
std::string security_level_;
std::vector<uint8_t> service_certificate_;
bool use_privacy_mode_;
bool is_session_sharing_enabled_;
uint32_t session_sharing_id_;
};
class TestWvCdmEventListener : public WvCdmEventListener {
@@ -377,6 +390,10 @@ class WvCdmDecryptionTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<SubSampleInfo*> {};
class WvCdmSessionSharingTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<bool> {};
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
std::string provisioning_server_url;
@@ -523,6 +540,54 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest)
decryptor_.CloseSession(session_id_);
}
TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
bool enable_session_sharing = GetParam();
TestWvCdmClientPropertySet property_set;
property_set.set_session_sharing_mode(enable_session_sharing);
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
CdmSessionId gp_session_id_1 = session_id_;
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false);
// TODO(rfrias): Move content information to ConfigTestEnv
std::string gp_client_auth2 =
"?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine";
std::string gp_key_id2 =
wvcdm::a2bs_hex(
"000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
"08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
CdmSessionId gp_session_id_2 = session_id_;
GenerateKeyRequest(g_key_system, gp_key_id2, kLicenseTypeStreaming);
VerifyKeyRequestResponse(g_license_server, gp_client_auth2, gp_key_id2, false);
SubSampleInfo* data = &single_encrypted_sub_sample;
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(&data->key_id,
&data->encrypt_data.front(),
data->encrypt_data.size(),
&data->iv,
data->block_offset,
&decrypt_buffer[0]);
decryption_parameters.is_encrypted = data->is_encrypted;
decryption_parameters.is_secure = data->is_secure;
EXPECT_EQ(
NO_ERROR == decryptor_.Decrypt(gp_session_id_2, decryption_parameters),
enable_session_sharing);
EXPECT_EQ(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
decrypt_buffer.begin()),
enable_session_sharing);
decryptor_.CloseSession(gp_session_id_1);
decryptor_.CloseSession(gp_session_id_2);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingTest, ::testing::Bool());
TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) {
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming);