Merge "Support Per-Origin Provisioning"

This commit is contained in:
John "Juce" Bruce
2015-04-10 18:20:54 +00:00
committed by Android (Google) Code Review
19 changed files with 627 additions and 313 deletions

View File

@@ -32,13 +32,14 @@ class CdmEngine {
// Session related methods
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
const std::string& origin,
WvCdmEventListener* event_listener,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual CdmResponseType OpenKeySetSession(
const CdmKeySetId& key_set_id, const CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener);
const std::string& origin, WvCdmEventListener* event_listener);
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
// License related methods
@@ -113,13 +114,18 @@ class CdmEngine {
// Provisioning related methods
virtual CdmResponseType GetProvisioningRequest(
CdmCertificateType cert_type, const std::string& cert_authority,
CdmProvisioningRequest* request, std::string* default_url);
const std::string& origin, CdmProvisioningRequest* request,
std::string* default_url);
virtual CdmResponseType HandleProvisioningResponse(
CdmProvisioningResponse& response, std::string* cert,
std::string* wrapped_key);
const std::string& origin, CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key);
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
virtual bool IsProvisioned(CdmSecurityLevel security_level,
const std::string& origin);
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level,
const std::string& origin);
// Usage related methods for streaming licenses
// Retrieve a random usage info from the list of all usage infos for this app

View File

@@ -4,6 +4,7 @@
#define WVCDM_CORE_CDM_SESSION_H_
#include <set>
#include <string>
#include "crypto_session.h"
#include "device_files.h"
@@ -22,7 +23,7 @@ class WvCdmEventListener;
class CdmSession {
public:
CdmSession(const CdmClientPropertySet* cdm_client_property_set,
WvCdmEventListener* event_listener);
const std::string& origin, WvCdmEventListener* event_listener);
virtual ~CdmSession();
virtual CdmResponseType Init();
@@ -128,6 +129,7 @@ class CdmSession {
// instance variables
bool initialized_;
CdmSessionId session_id_;
const std::string origin_;
scoped_ptr<CdmLicense> license_parser_;
scoped_ptr<CryptoSession> crypto_session_;
scoped_ptr<PolicyEngine> policy_engine_;

View File

@@ -3,6 +3,8 @@
#ifndef WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
#define WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
#include <string>
#include "crypto_session.h"
#include "oemcrypto_adapter.h"
#include "wv_cdm_types.h"
@@ -20,9 +22,11 @@ class CertificateProvisioning {
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
CdmCertificateType cert_type,
const std::string& cert_authority,
const std::string& origin,
CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response,
CdmResponseType HandleProvisioningResponse(const std::string& origin,
CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key);

View File

@@ -4,6 +4,7 @@
#define WVCDM_CORE_DEVICE_FILES_H_
#include <unistd.h>
#include <string>
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
@@ -32,10 +33,14 @@ class DeviceFiles {
return Init(security_level);
}
virtual bool StoreCertificate(const std::string& certificate,
virtual bool StoreCertificate(const std::string& origin,
const std::string& certificate,
const std::string& wrapped_private_key);
virtual bool RetrieveCertificate(std::string* certificate,
virtual bool RetrieveCertificate(const std::string& origin,
std::string* certificate,
std::string* wrapped_private_key);
virtual bool HasCertificate(const std::string& origin);
virtual bool RemoveCertificate(const std::string& origin);
virtual bool StoreLicense(const std::string& key_set_id,
const LicenseState state,
@@ -100,18 +105,21 @@ class DeviceFiles {
// stored in a common directory and need to be copied over.
virtual void SecurityLevelPathBackwardCompatibility();
// For testing only:
static std::string GetCertificateFileName();
static std::string GetCertificateFileName(const std::string& origin);
static std::string GetLicenseFileNameExtension();
static std::string GetUsageInfoFileName(const std::string& app_id);
static std::string GetBlankFileData();
static std::string GetFileNameSafeHash(const std::string& input);
// For testing only:
void SetTestFile(File* file);
#if defined(UNIT_TEST)
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
FRIEND_TEST(DeviceFilesStoreTest, StoreCertificate);
FRIEND_TEST(DeviceCertificateStoreTest, StoreCertificate);
FRIEND_TEST(DeviceCertificateTest, ReadCertificate);
FRIEND_TEST(DeviceCertificateTest, HasCertificate);
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
FRIEND_TEST(DeviceFilesTest, ReadCertificate);
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIds);
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility);

View File

@@ -75,6 +75,8 @@ static const std::string WEBM_VIDEO_MIME_TYPE = "video/webm";
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
static const std::string CENC_INIT_DATA_FORMAT = "cenc";
static const std::string WEBM_INIT_DATA_FORMAT = "webm";
static const char EMPTY_ORIGIN[] = "";
} // namespace wvcdm
#endif // WVCDM_CORE_WV_CDM_CONSTANTS_H_

View File

@@ -76,6 +76,7 @@ CdmEngine::~CdmEngine() {
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
const std::string& origin,
WvCdmEventListener* event_listener,
CdmSessionId* session_id) {
LOGI("CdmEngine::OpenSession");
@@ -91,7 +92,7 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
}
scoped_ptr<CdmSession> new_session(
new CdmSession(property_set, event_listener));
new CdmSession(property_set, origin, event_listener));
if (new_session->session_id().empty()) {
LOGE("CdmEngine::OpenSession: failure to generate session ID");
return UNKNOWN_ERROR;
@@ -115,7 +116,7 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CdmResponseType CdmEngine::OpenKeySetSession(
const CdmKeySetId& key_set_id, const CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener) {
const std::string& origin, WvCdmEventListener* event_listener) {
LOGI("CdmEngine::OpenKeySetSession");
if (key_set_id.empty()) {
@@ -125,7 +126,8 @@ CdmResponseType CdmEngine::OpenKeySetSession(
CdmSessionId session_id;
CdmResponseType sts =
OpenSession(KEY_SYSTEM, property_set, event_listener, &session_id);
OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
&session_id);
if (sts != NO_ERROR) return sts;
@@ -503,9 +505,10 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
*/
CdmResponseType CdmEngine::GetProvisioningRequest(
CdmCertificateType cert_type, const std::string& cert_authority,
CdmProvisioningRequest* request, std::string* default_url) {
const std::string& origin, CdmProvisioningRequest* request,
std::string* default_url) {
if (!request || !default_url) {
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
return UNKNOWN_ERROR;
}
if (NULL == cert_provisioning_.get()) {
@@ -513,7 +516,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
}
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_type, cert_authority,
request, default_url);
origin, request, default_url);
if (ret != NO_ERROR) {
cert_provisioning_.reset(NULL); // Release resources.
}
@@ -528,8 +531,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CdmEngine::HandleProvisioningResponse(
CdmProvisioningResponse& response, std::string* cert,
std::string* wrapped_key) {
const std::string& origin, CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
if (response.empty()) {
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
cert_provisioning_.reset(NULL);
@@ -553,35 +556,55 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
return UNKNOWN_ERROR;
}
CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
response, cert, wrapped_key);
origin, response, cert, wrapped_key);
cert_provisioning_.reset(NULL); // Release resources.
return ret;
}
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level,
const std::string& origin) {
DeviceFiles handle;
if (!handle.Init(security_level)) {
LOGE("CdmEngine::IsProvisioned: unable to initialize device files");
return false;
}
return handle.HasCertificate(origin);
}
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level,
const std::string& origin) {
DeviceFiles handle;
if (!handle.Init(security_level)) {
LOGE("CdmEngine::Unprovision: unable to initialize device files");
return UNKNOWN_ERROR;
}
if (!handle.DeleteAllFiles()) {
LOGE("CdmEngine::Unprovision: unable to delete files");
return UNKNOWN_ERROR;
}
if (origin != EMPTY_ORIGIN) {
if (!handle.RemoveCertificate(origin)) {
LOGE("CdmEngine::Unprovision: unable to delete certificate for origin %s",
origin.c_str());
return UNKNOWN_ERROR;
}
return NO_ERROR;
} else {
if (!handle.DeleteAllFiles()) {
LOGE("CdmEngine::Unprovision: unable to delete files");
return UNKNOWN_ERROR;
}
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (NO_ERROR != status) {
LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status);
return UNKNOWN_ERROR;
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (NO_ERROR != status) {
LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status);
return UNKNOWN_ERROR;
}
status = crypto_session->DeleteAllUsageReports();
if (status != NO_ERROR) {
LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status);
}
return status;
}
status = crypto_session->DeleteAllUsageReports();
if (status != NO_ERROR) {
LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status);
}
return status;
}
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
@@ -592,7 +615,8 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
}
usage_property_set_->set_security_level(kLevelDefault);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(usage_property_set_.get(), NULL));
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL));
CdmResponseType status = usage_session_->Init();
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
@@ -610,7 +634,8 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
&license_response)) {
usage_property_set_->set_security_level(kLevel3);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(usage_property_set_.get(), NULL));
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL));
status = usage_session_->Init();
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
@@ -678,7 +703,8 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
usage_property_set_->set_security_level(requested_security_level);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(usage_property_set_.get(), NULL));
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL));
CdmResponseType status = usage_session_->Init();
if (NO_ERROR != status) {

View File

@@ -24,9 +24,11 @@ const size_t kKeySetIdLength = 14;
namespace wvcdm {
CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set,
const std::string& origin,
WvCdmEventListener* event_listener)
: initialized_(false),
session_id_(GenerateSessionId()),
origin_(origin),
license_parser_(new CdmLicense),
crypto_session_(new CryptoSession),
policy_engine_(
@@ -70,7 +72,7 @@ CdmResponseType CdmSession::Init() {
if (Properties::use_certificates_as_identification()) {
std::string wrapped_key;
if (!file_handle_->Init(security_level_) ||
!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
!file_handle_->RetrieveCertificate(origin_, &token, &wrapped_key) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING;
}

View File

@@ -57,8 +57,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
*/
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SecurityLevel requested_security_level, CdmCertificateType cert_type,
const std::string& cert_authority, CdmProvisioningRequest* request,
std::string* default_url) {
const std::string& cert_authority, const std::string& origin,
CdmProvisioningRequest* request, std::string* default_url) {
if (!default_url) {
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
return UNKNOWN_ERROR;
@@ -113,6 +113,15 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
if (origin != EMPTY_ORIGIN) {
std::string device_unique_id;
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
LOGE("GetProvisioningRequest: fails to get device unique ID");
return UNKNOWN_ERROR;
}
provisioning_request.set_stable_id(device_unique_id + origin);
}
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
@@ -180,8 +189,8 @@ bool CertificateProvisioning::ParseJsonResponse(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
CdmProvisioningResponse& response, std::string* cert,
std::string* wrapped_key) {
const std::string& origin, CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
@@ -255,7 +264,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
return UNKNOWN_ERROR;
}
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
if (!handle.StoreCertificate(origin, device_certificate, wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
return UNKNOWN_ERROR;
}

View File

@@ -10,6 +10,7 @@
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#if defined(__APPLE__)
#include <CommonCrypto/CommonDigest.h>
@@ -31,7 +32,8 @@ using video_widevine_client::sdk::UsageInfo_ProviderSession;
namespace {
const char kCertificateFileName[] = "cert.bin";
const char kCertificateFileNamePrefix[] = "cert";
const char kCertificateFileNameExt[] = ".bin";
const char kUsageInfoFileNamePrefix[] = "usage";
const char kUsageInfoFileNameExt[] = ".bin";
const char kLicenseFileNameExt[] = ".lic";
@@ -86,7 +88,8 @@ bool DeviceFiles::Init(CdmSecurityLevel security_level) {
return true;
}
bool DeviceFiles::StoreCertificate(const std::string& certificate,
bool DeviceFiles::StoreCertificate(const std::string& origin,
const std::string& certificate,
const std::string& wrapped_private_key) {
if (!initialized_) {
LOGW("DeviceFiles::StoreCertificate: not initialized");
@@ -106,10 +109,11 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(kCertificateFileName, serialized_file);
return StoreFileWithHash(GetCertificateFileName(origin), serialized_file);
}
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
bool DeviceFiles::RetrieveCertificate(const std::string& origin,
std::string* certificate,
std::string* wrapped_private_key) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
@@ -121,7 +125,9 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
}
std::string serialized_file;
if (!RetrieveHashedFile(kCertificateFileName, &serialized_file)) return false;
if (!RetrieveHashedFile(GetCertificateFileName(origin), &serialized_file)) {
return false;
}
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
@@ -151,6 +157,24 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
return true;
}
bool DeviceFiles::HasCertificate(const std::string& origin) {
if (!initialized_) {
LOGW("DeviceFiles::HasCertificate: not initialized");
return false;
}
return FileExists(GetCertificateFileName(origin));
}
bool DeviceFiles::RemoveCertificate(const std::string& origin) {
if (!initialized_) {
LOGW("DeviceFiles::RemoveCertificate: not initialized");
return false;
}
return RemoveFile(GetCertificateFileName(origin));
}
bool DeviceFiles::StoreLicense(const std::string& key_set_id,
const LicenseState state,
const CdmInitData& pssh_data,
@@ -749,8 +773,12 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
}
}
std::string DeviceFiles::GetCertificateFileName() {
return kCertificateFileName;
std::string DeviceFiles::GetCertificateFileName(const std::string& origin) {
std::string hash;
if (origin != EMPTY_ORIGIN) {
hash = GetFileNameSafeHash(origin);
}
return kCertificateFileNamePrefix + hash + kCertificateFileNameExt;
}
std::string DeviceFiles::GetLicenseFileNameExtension() {
@@ -758,20 +786,23 @@ std::string DeviceFiles::GetLicenseFileNameExtension() {
}
std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
if (app_id == "") {
return std::string(kUsageInfoFileNamePrefix) +
std::string(kUsageInfoFileNameExt);
std::string hash;
if (app_id != "") {
hash = GetFileNameSafeHash(app_id);
}
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
const unsigned char* input =
reinterpret_cast<const unsigned char*>(app_id.data());
MD5(input, app_id.size(), &hash[0]);
return std::string(kUsageInfoFileNamePrefix) + wvcdm::Base64SafeEncode(hash) +
std::string(kUsageInfoFileNameExt);
return kUsageInfoFileNamePrefix + hash + kUsageInfoFileNameExt;
}
std::string DeviceFiles::GetBlankFileData() { return kBlankFileData; }
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
const unsigned char* input_ptr =
reinterpret_cast<const unsigned char*>(input.data());
MD5(input_ptr, input.size(), &hash[0]);
return wvcdm::Base64SafeEncode(hash);
}
void DeviceFiles::SetTestFile(File* file) {
file_.reset(file);
test_file_ = true;

View File

@@ -48,10 +48,12 @@ class WvCdmEngineTest : public testing::Test {
public:
virtual void SetUp() {
CdmResponseType status =
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
&session_id_);
if (status == NEED_PROVISIONING) {
Provision();
status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
status = cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
&session_id_);
}
ASSERT_EQ(NO_ERROR, status);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
@@ -67,14 +69,15 @@ class WvCdmEngineTest : public testing::Test {
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
cert_type, cert_authority, EMPTY_ORIGIN,
&prov_request, &provisioning_server_url));
UrlRequest url_request(provisioning_server_url);
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
EXPECT_TRUE(ok);
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(message, &cert,
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(EMPTY_ORIGIN,
message, &cert,
&wrapped_key));
}

View File

@@ -8,6 +8,7 @@
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "test_printers.h"
#include "wv_cdm_constants.h"
namespace {
const std::string kToken = wvcdm::a2bs_hex(
@@ -83,6 +84,9 @@ const std::string kWrappedKey = wvcdm::a2bs_hex(
"33EF70621A98184DDAB5E14BC971CF98CF6C91A37FFA83B00AD3BCABBAAB2DEF1C52F43003"
"E74C92B44F9205D22262FB47948654229DE1920F8EDF96A19A88A1CA1552F8856FB4CBF83B"
"AA3348419159D207F65FCE9C1A500C6818");
const std::string kTestOrigin = "com.google";
} // namespace
namespace wvcdm {
@@ -99,7 +103,8 @@ using ::testing::StrEq;
class MockDeviceFiles : public DeviceFiles {
public:
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*));
MOCK_METHOD3(RetrieveCertificate, bool(const std::string&, std::string*,
std::string*));
};
class MockCryptoSession : public CryptoSession {
@@ -127,7 +132,7 @@ class MockCdmLicense : public CdmLicense {
class CdmSessionTest : public ::testing::Test {
protected:
virtual void SetUp() {
cdm_session_.reset(new CdmSession(NULL, NULL));
cdm_session_.reset(new CdmSession(NULL, kTestOrigin, NULL));
// Inject testing mocks.
license_parser_ = new MockCdmLicense();
cdm_session_->set_license_parser(license_parser_);
@@ -156,8 +161,9 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
.InSequence(crypto_session_seq)
@@ -202,8 +208,9 @@ TEST_F(CdmSessionTest, ReInitFail) {
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
.InSequence(crypto_session_seq)
@@ -238,7 +245,8 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
NotNull()))
.WillOnce(Return(false));
Properties::set_use_certificates_as_identification(true);

View File

@@ -1,11 +1,13 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "device_files.h"
#include "file_store.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
namespace wvcdm {
@@ -1230,6 +1232,8 @@ UsageInfo kUsageInfoTestData[] = {
"0E12202FF1FBA9926A24A1F79970EC427DDF87B4421488F7952499BC33CEB282D9E48"
"A")}};
const std::string kTestOrigin = "com.google";
} // namespace
class MockFile : public File {
@@ -1278,6 +1282,23 @@ class DeviceFilesTest : public ::testing::Test {
class DeviceFilesStoreTest : public DeviceFilesTest,
public ::testing::WithParamInterface<bool> {};
struct CertStorageVariant {
CertStorageVariant(bool dir_exists_value, const std::string& origin_value)
: dir_exists(dir_exists_value),
origin(origin_value) {}
const bool dir_exists;
const std::string origin;
};
class DeviceCertificateStoreTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<CertStorageVariant> {};
class DeviceCertificateTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<std::string> {};
class DeviceFilesSecurityLevelTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<CdmSecurityLevel> {};
@@ -1323,17 +1344,17 @@ MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") {
data.find(str6) != std::string::npos);
}
TEST_P(DeviceFilesStoreTest, StoreCertificate) {
TEST_P(DeviceCertificateStoreTest, StoreCertificate) {
MockFile file;
CertStorageVariant params = GetParam();
std::string certificate(GenerateRandomData(kCertificateLen));
std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen));
std::string device_certificate_path =
device_base_path_ + DeviceFiles::GetCertificateFileName();
device_base_path_ + DeviceFiles::GetCertificateFileName(params.origin);
bool dir_exists = GetParam();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillOnce(Return(dir_exists));
if (dir_exists) {
.WillOnce(Return(params.dir_exists));
if (params.dir_exists) {
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
} else {
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_)))
@@ -1352,16 +1373,22 @@ TEST_P(DeviceFilesStoreTest, StoreCertificate) {
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
EXPECT_TRUE(device_files.StoreCertificate(params.origin, certificate,
wrapped_private_key));
}
INSTANTIATE_TEST_CASE_P(StoreCertificate, DeviceFilesStoreTest,
::testing::Bool());
INSTANTIATE_TEST_CASE_P(
StoreCertificate, DeviceCertificateStoreTest,
::testing::Values(CertStorageVariant(true, EMPTY_ORIGIN),
CertStorageVariant(true, kTestOrigin),
CertStorageVariant(false, EMPTY_ORIGIN),
CertStorageVariant(false, kTestOrigin)));
TEST_F(DeviceFilesTest, ReadCertificate) {
TEST_P(DeviceCertificateTest, ReadCertificate) {
MockFile file;
std::string origin = GetParam();
std::string device_certificate_path =
device_base_path_ + DeviceFiles::GetCertificateFileName();
device_base_path_ + DeviceFiles::GetCertificateFileName(origin);
std::string data = a2bs_hex(kTestCertificateFileData);
EXPECT_CALL(file, Exists(StrEq(device_certificate_path)))
@@ -1382,11 +1409,36 @@ TEST_F(DeviceFilesTest, ReadCertificate) {
std::string certificate, wrapped_private_key;
ASSERT_TRUE(
device_files.RetrieveCertificate(&certificate, &wrapped_private_key));
device_files.RetrieveCertificate(origin, &certificate,
&wrapped_private_key));
EXPECT_EQ(kTestCertificate, b2a_hex(certificate));
EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key));
}
TEST_P(DeviceCertificateTest, HasCertificate) {
MockFile file;
std::string origin = GetParam();
std::string device_certificate_path =
device_base_path_ + DeviceFiles::GetCertificateFileName(origin);
EXPECT_CALL(file, Exists(StrEq(device_certificate_path)))
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_CALL(file, Open(_, _)).Times(0);
DeviceFiles device_files;
ASSERT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
// MockFile returns false.
EXPECT_FALSE(device_files.HasCertificate(origin));
// MockFile returns true.
EXPECT_TRUE(device_files.HasCertificate(origin));
}
INSTANTIATE_TEST_CASE_P(CertificateUseTests, DeviceCertificateTest,
::testing::Values(EMPTY_ORIGIN, kTestOrigin));
TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
MockFile file;
std::string certificate(GenerateRandomData(kCertificateLen));
@@ -1397,7 +1449,7 @@ TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
ASSERT_TRUE(
Properties::GetDeviceFilesBasePath(security_level, &device_base_path));
std::string device_certificate_path =
device_base_path + DeviceFiles::GetCertificateFileName();
device_base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path)))
.WillOnce(Return(false));
@@ -1416,7 +1468,8 @@ TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(security_level));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
EXPECT_TRUE(device_files.StoreCertificate(EMPTY_ORIGIN, certificate,
wrapped_private_key));
}
INSTANTIATE_TEST_CASE_P(SecurityLevel, DeviceFilesSecurityLevelTest,
@@ -1595,13 +1648,15 @@ TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
EXPECT_CALL(file, CreateDirectory(StrEq(new_path))).WillOnce(Return(true));
}
std::string old_path = base_path + DeviceFiles::GetCertificateFileName();
old_files.push_back(DeviceFiles::GetCertificateFileName());
std::string old_path =
base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
old_files.push_back(DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN));
EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true));
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
for (size_t i = 0; i < security_dirs.size(); ++i) {
new_path =
base_path + security_dirs[i] + DeviceFiles::GetCertificateFileName();
base_path + security_dirs[i] +
DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
.WillOnce(Return(true));
}
@@ -1625,7 +1680,8 @@ TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
std::string data = a2bs_hex(kTestCertificateFileData);
new_path = device_base_path_ + DeviceFiles::GetCertificateFileName();
new_path =
device_base_path_ + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
EXPECT_CALL(file, Exists(StrEq(new_path))).WillOnce(Return(true));
EXPECT_CALL(file, FileSize(_)).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(_, _)).WillOnce(Return(true));
@@ -1642,7 +1698,8 @@ TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
Properties::Init();
std::string certificate, wrapped_private_key;
ASSERT_TRUE(
device_files.RetrieveCertificate(&certificate, &wrapped_private_key));
device_files.RetrieveCertificate(EMPTY_ORIGIN, &certificate,
&wrapped_private_key));
}
TEST_F(DeviceFilesTest, UpdateLicenseState) {