Support Per-Origin Provisioning

This is a merge of several Widevine-side commits that, cumulatively,
allow callers to specify an origin to be used to isolate data storage
as specified in the W3C Encrypted Media Extension specification.
Separate origins have separate certificates, and consequently cannot
share device identifiers with each other.

The changes included in this are:

Add Ability to Check for Existing Certificates
    http://go/wvgerrit/13974
Add Ability to Remove the Certificate
    http://go/wvgerrit/13975
Make CDM Origin-Aware
    http://go/wvgerrit/13977
Add Per-Origin Storage to Widevine CDM on Android
    http://go/wvgerrit/14026
Remove Automatic Origin Generation
    http://go/wvgerrit/14031

Bug: 19771858
Change-Id: I6a01c705d9b6b4887a9c7e6ff4399a125f781569
This commit is contained in:
John "Juce" Bruce
2015-04-09 19:02:31 -07:00
parent 786bbba499
commit 59811eed57
19 changed files with 627 additions and 313 deletions

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) {