Key Rotation: Get the initial crypto index from first request

This change also updates the WidevineEncryptionKeySource to be able to support
KeyRotation enabled and disabled requests simultaneously.

Change-Id: I5178cafc0dbabbb64ac9af9969d3bf7d8117a4dd
This commit is contained in:
KongQun Yang 2014-06-30 08:40:02 -07:00 committed by Gerrit Code Review
parent 33a87aa84b
commit f609b2947c
4 changed files with 112 additions and 117 deletions

View File

@ -70,8 +70,7 @@ scoped_ptr<EncryptionKeySource> CreateEncryptionKeySource() {
FLAGS_key_server_url, FLAGS_key_server_url,
FLAGS_content_id, FLAGS_content_id,
FLAGS_policy, FLAGS_policy,
signer.Pass(), signer.Pass()));
FLAGS_crypto_period_duration == 0 ? kDisableKeyRotation : 0));
Status status = widevine_encryption_key_source->Initialize(); Status status = widevine_encryption_key_source->Initialize();
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << "Widevine encryption key source failed to initialize: " LOG(ERROR) << "Widevine encryption key source failed to initialize: "

View File

@ -14,6 +14,7 @@
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/values.h" #include "base/values.h"
#include "media/base/http_fetcher.h" #include "media/base/http_fetcher.h"
#include "media/base/producer_consumer_queue.h"
#include "media/base/request_signer.h" #include "media/base/request_signer.h"
#define RCHECK(x) \ #define RCHECK(x) \
@ -26,6 +27,8 @@
namespace { namespace {
const bool kEnableKeyRotation = true;
const char kLicenseStatusOK[] = "OK"; const char kLicenseStatusOK[] = "OK";
// Server may return INTERNAL_ERROR intermittently, which is a transient error // Server may return INTERNAL_ERROR intermittently, which is a transient error
// and the next client request may succeed without problem. // and the next client request may succeed without problem.
@ -128,50 +131,52 @@ WidevineEncryptionKeySource::WidevineEncryptionKeySource(
const std::string& server_url, const std::string& server_url,
const std::string& content_id, const std::string& content_id,
const std::string& policy, const std::string& policy,
scoped_ptr<RequestSigner> signer, scoped_ptr<RequestSigner> signer)
int first_crypto_period_index)
: http_fetcher_(new SimpleHttpFetcher(kHttpTimeoutInSeconds)), : http_fetcher_(new SimpleHttpFetcher(kHttpTimeoutInSeconds)),
server_url_(server_url), server_url_(server_url),
content_id_(content_id), content_id_(content_id),
policy_(policy), policy_(policy),
signer_(signer.Pass()), signer_(signer.Pass()),
key_rotation_enabled_(first_crypto_period_index >= 0),
crypto_period_count_(kDefaultCryptoPeriodCount), crypto_period_count_(kDefaultCryptoPeriodCount),
first_crypto_period_index_(first_crypto_period_index), key_production_started_(false),
start_key_production_(false, false),
first_crypto_period_index_(0),
key_production_thread_( key_production_thread_(
"KeyProductionThread", "KeyProductionThread",
base::Bind(&WidevineEncryptionKeySource::FetchKeysTask, base::Bind(&WidevineEncryptionKeySource::FetchKeysTask,
base::Unretained(this))), base::Unretained(this))) {
key_pool_(kDefaultCryptoPeriodCount,
key_rotation_enabled_ ? first_crypto_period_index : 0) {
DCHECK(signer_); DCHECK(signer_);
} }
WidevineEncryptionKeySource::~WidevineEncryptionKeySource() { WidevineEncryptionKeySource::~WidevineEncryptionKeySource() {
key_pool_.Stop(); if (key_pool_)
if (key_production_thread_.HasBeenStarted()) key_pool_->Stop();
if (key_production_thread_.HasBeenStarted()) {
// Signal the production thread to start key production if it is not
// signaled yet so the thread can be joined.
start_key_production_.Signal();
key_production_thread_.Join(); key_production_thread_.Join();
}
} }
Status WidevineEncryptionKeySource::Initialize() { Status WidevineEncryptionKeySource::Initialize() {
// |first_crypto_period_index| might be updated after starting production.
// Make a local copy for prime later.
const uint32 first_crypto_period_index = first_crypto_period_index_;
DCHECK(!key_production_thread_.HasBeenStarted()); DCHECK(!key_production_thread_.HasBeenStarted());
key_production_thread_.Start(); key_production_thread_.Start();
// Perform a GetKey request to find out common encryption request status. // Perform a fetch request to find out if the key source is healthy.
// It also primes the key_pool if successful. // It also stores the keys fetched for consumption later.
return key_rotation_enabled_ ? GetCryptoPeriodKey(first_crypto_period_index, return FetchKeys(!kEnableKeyRotation, 0);
TRACK_TYPE_SD, NULL)
: GetKey(TRACK_TYPE_SD, NULL);
} }
Status WidevineEncryptionKeySource::GetKey(TrackType track_type, Status WidevineEncryptionKeySource::GetKey(TrackType track_type,
EncryptionKey* key) { EncryptionKey* key) {
DCHECK(key_production_thread_.HasBeenStarted()); DCHECK(key);
DCHECK(!key_rotation_enabled_); if (encryption_key_map_.find(track_type) == encryption_key_map_.end()) {
return GetKeyInternal(0u, track_type, key); return Status(error::INTERNAL_ERROR,
"Cannot find key of type " + TrackTypeToString(track_type));
}
*key = *encryption_key_map_[track_type];
return Status::OK;
} }
Status WidevineEncryptionKeySource::GetCryptoPeriodKey( Status WidevineEncryptionKeySource::GetCryptoPeriodKey(
@ -179,7 +184,20 @@ Status WidevineEncryptionKeySource::GetCryptoPeriodKey(
TrackType track_type, TrackType track_type,
EncryptionKey* key) { EncryptionKey* key) {
DCHECK(key_production_thread_.HasBeenStarted()); DCHECK(key_production_thread_.HasBeenStarted());
DCHECK(key_rotation_enabled_); // TODO(kqyang): This is not elegant. Consider refactoring later.
{
base::AutoLock scoped_lock(lock_);
if (!key_production_started_) {
// Another client may have a slightly smaller starting crypto period
// index. Set the initial value to account for that.
first_crypto_period_index_ = crypto_period_index ? crypto_period_index - 1 : 0;
DCHECK(!key_pool_);
key_pool_.reset(new EncryptionKeyQueue(crypto_period_count_,
first_crypto_period_index_));
start_key_production_.Signal();
key_production_started_ = true;
}
}
return GetKeyInternal(crypto_period_index, track_type, key); return GetKeyInternal(crypto_period_index, track_type, key);
} }
@ -192,13 +210,15 @@ Status WidevineEncryptionKeySource::GetKeyInternal(
uint32 crypto_period_index, uint32 crypto_period_index,
TrackType track_type, TrackType track_type,
EncryptionKey* key) { EncryptionKey* key) {
DCHECK(key_pool_);
DCHECK(key);
DCHECK_LE(track_type, NUM_VALID_TRACK_TYPES); DCHECK_LE(track_type, NUM_VALID_TRACK_TYPES);
DCHECK_NE(track_type, TRACK_TYPE_UNKNOWN); DCHECK_NE(track_type, TRACK_TYPE_UNKNOWN);
scoped_refptr<RefCountedEncryptionKeyMap> ref_counted_encryption_key_map; scoped_refptr<RefCountedEncryptionKeyMap> ref_counted_encryption_key_map;
Status status = key_pool_.Peek(crypto_period_index, Status status =
&ref_counted_encryption_key_map, key_pool_->Peek(crypto_period_index, &ref_counted_encryption_key_map,
kGetKeyTimeoutInSeconds * 1000); kGetKeyTimeoutInSeconds * 1000);
if (!status.ok()) { if (!status.ok()) {
if (status.error_code() == error::STOPPED) { if (status.error_code() == error::STOPPED) {
CHECK(!common_encryption_request_status_.ok()); CHECK(!common_encryption_request_status_.ok());
@ -212,27 +232,30 @@ Status WidevineEncryptionKeySource::GetKeyInternal(
return Status(error::INTERNAL_ERROR, return Status(error::INTERNAL_ERROR,
"Cannot find key of type " + TrackTypeToString(track_type)); "Cannot find key of type " + TrackTypeToString(track_type));
} }
if (key) *key = *encryption_key_map[track_type];
*key = *encryption_key_map[track_type];
return Status::OK; return Status::OK;
} }
void WidevineEncryptionKeySource::FetchKeysTask() { void WidevineEncryptionKeySource::FetchKeysTask() {
Status status = FetchKeys(first_crypto_period_index_); // Wait until key production is signaled.
if (key_rotation_enabled_) { start_key_production_.Wait();
while (status.ok()) { if (!key_pool_ || key_pool_->Stopped())
first_crypto_period_index_ += crypto_period_count_; return;
status = FetchKeys(first_crypto_period_index_);
} Status status = FetchKeys(kEnableKeyRotation, first_crypto_period_index_);
while (status.ok()) {
first_crypto_period_index_ += crypto_period_count_;
status = FetchKeys(kEnableKeyRotation, first_crypto_period_index_);
} }
common_encryption_request_status_ = status; common_encryption_request_status_ = status;
key_pool_.Stop(); key_pool_->Stop();
} }
Status WidevineEncryptionKeySource::FetchKeys( Status WidevineEncryptionKeySource::FetchKeys(
uint32 first_crypto_period_index) { bool enable_key_rotation, uint32 first_crypto_period_index) {
std::string request; std::string request;
FillRequest(content_id_, first_crypto_period_index, &request); FillRequest(content_id_, enable_key_rotation, first_crypto_period_index,
&request);
std::string message; std::string message;
Status status = SignRequest(request, &message); Status status = SignRequest(request, &message);
@ -257,7 +280,7 @@ Status WidevineEncryptionKeySource::FetchKeys(
} }
bool transient_error = false; bool transient_error = false;
if (ExtractEncryptionKey(response, &transient_error)) if (ExtractEncryptionKey(enable_key_rotation, response, &transient_error))
return Status::OK; return Status::OK;
if (!transient_error) { if (!transient_error) {
@ -281,6 +304,7 @@ Status WidevineEncryptionKeySource::FetchKeys(
} }
void WidevineEncryptionKeySource::FillRequest(const std::string& content_id, void WidevineEncryptionKeySource::FillRequest(const std::string& content_id,
bool enable_key_rotation,
uint32 first_crypto_period_index, uint32 first_crypto_period_index,
std::string* request) { std::string* request) {
DCHECK(request); DCHECK(request);
@ -313,7 +337,7 @@ void WidevineEncryptionKeySource::FillRequest(const std::string& content_id,
request_dict.Set("drm_types", drm_types); request_dict.Set("drm_types", drm_types);
// Build key rotation fields. // Build key rotation fields.
if (key_rotation_enabled_) { if (enable_key_rotation) {
request_dict.SetInteger("first_crypto_period_index", request_dict.SetInteger("first_crypto_period_index",
first_crypto_period_index); first_crypto_period_index);
request_dict.SetInteger("crypto_period_count", crypto_period_count_); request_dict.SetInteger("crypto_period_count", crypto_period_count_);
@ -368,6 +392,7 @@ bool WidevineEncryptionKeySource::DecodeResponse(
} }
bool WidevineEncryptionKeySource::ExtractEncryptionKey( bool WidevineEncryptionKeySource::ExtractEncryptionKey(
bool enable_key_rotation,
const std::string& response, const std::string& response,
bool* transient_error) { bool* transient_error) {
DCHECK(transient_error); DCHECK(transient_error);
@ -392,7 +417,7 @@ bool WidevineEncryptionKeySource::ExtractEncryptionKey(
const base::ListValue* tracks; const base::ListValue* tracks;
RCHECK(license_dict->GetList("tracks", &tracks)); RCHECK(license_dict->GetList("tracks", &tracks));
RCHECK(key_rotation_enabled_ RCHECK(enable_key_rotation
? tracks->GetSize() >= NUM_VALID_TRACK_TYPES * crypto_period_count_ ? tracks->GetSize() >= NUM_VALID_TRACK_TYPES * crypto_period_count_
: tracks->GetSize() >= NUM_VALID_TRACK_TYPES); : tracks->GetSize() >= NUM_VALID_TRACK_TYPES);
@ -403,7 +428,7 @@ bool WidevineEncryptionKeySource::ExtractEncryptionKey(
const base::DictionaryValue* track_dict; const base::DictionaryValue* track_dict;
RCHECK(tracks->GetDictionary(i, &track_dict)); RCHECK(tracks->GetDictionary(i, &track_dict));
if (key_rotation_enabled_) { if (enable_key_rotation) {
int crypto_period_index; int crypto_period_index;
RCHECK( RCHECK(
track_dict->GetInteger("crypto_period_index", &crypto_period_index)); track_dict->GetInteger("crypto_period_index", &crypto_period_index));
@ -438,16 +463,21 @@ bool WidevineEncryptionKeySource::ExtractEncryptionKey(
} }
DCHECK(!encryption_key_map.empty()); DCHECK(!encryption_key_map.empty());
if (!enable_key_rotation) {
encryption_key_map_ = encryption_key_map;
return true;
}
return PushToKeyPool(&encryption_key_map); return PushToKeyPool(&encryption_key_map);
} }
bool WidevineEncryptionKeySource::PushToKeyPool( bool WidevineEncryptionKeySource::PushToKeyPool(
EncryptionKeyMap* encryption_key_map) { EncryptionKeyMap* encryption_key_map) {
DCHECK(key_pool_);
DCHECK(encryption_key_map); DCHECK(encryption_key_map);
Status status = Status status =
key_pool_.Push(scoped_refptr<RefCountedEncryptionKeyMap>( key_pool_->Push(scoped_refptr<RefCountedEncryptionKeyMap>(
new RefCountedEncryptionKeyMap(encryption_key_map)), new RefCountedEncryptionKeyMap(encryption_key_map)),
kInfiniteTimeout); kInfiniteTimeout);
encryption_key_map->clear(); encryption_key_map->clear();
if (!status.ok()) { if (!status.ok()) {
DCHECK_EQ(error::STOPPED, status.error_code()); DCHECK_EQ(error::STOPPED, status.error_code());

View File

@ -11,16 +11,14 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "media/base/closure_thread.h" #include "media/base/closure_thread.h"
#include "media/base/encryption_key_source.h" #include "media/base/encryption_key_source.h"
#include "media/base/producer_consumer_queue.h"
namespace media { namespace media {
/// A negative crypto period index disables key rotation.
static const int kDisableKeyRotation = -1;
class HttpFetcher; class HttpFetcher;
class RequestSigner; class RequestSigner;
template <class T> class ProducerConsumerQueue;
/// WidevineEncryptionKeySource talks to the Widevine encryption service to /// WidevineEncryptionKeySource talks to the Widevine encryption service to
/// acquire the encryption keys. /// acquire the encryption keys.
@ -30,13 +28,10 @@ class WidevineEncryptionKeySource : public EncryptionKeySource {
/// @param content_id the unique id identify the content to be encrypted. /// @param content_id the unique id identify the content to be encrypted.
/// @param policy specifies the DRM content rights. /// @param policy specifies the DRM content rights.
/// @param signer signs the request message. It should not be NULL. /// @param signer signs the request message. It should not be NULL.
/// @param first_crypto_period_index indicates the starting crypto period
/// index. Set it to kDisableKeyRotation to disable key rotation.
WidevineEncryptionKeySource(const std::string& server_url, WidevineEncryptionKeySource(const std::string& server_url,
const std::string& content_id, const std::string& content_id,
const std::string& policy, const std::string& policy,
scoped_ptr<RequestSigner> signer, scoped_ptr<RequestSigner> signer);
int first_crypto_period_index);
virtual ~WidevineEncryptionKeySource(); virtual ~WidevineEncryptionKeySource();
/// Initialize the key source. Must be called before calling GetKey or /// Initialize the key source. Must be called before calling GetKey or
@ -59,6 +54,8 @@ class WidevineEncryptionKeySource : public EncryptionKeySource {
private: private:
typedef std::map<TrackType, EncryptionKey*> EncryptionKeyMap; typedef std::map<TrackType, EncryptionKey*> EncryptionKeyMap;
class RefCountedEncryptionKeyMap; class RefCountedEncryptionKeyMap;
typedef ProducerConsumerQueue<scoped_refptr<RefCountedEncryptionKeyMap> >
EncryptionKeyQueue;
// Internal routine for getting keys. // Internal routine for getting keys.
Status GetKeyInternal(uint32 crypto_period_index, Status GetKeyInternal(uint32 crypto_period_index,
@ -69,11 +66,12 @@ class WidevineEncryptionKeySource : public EncryptionKeySource {
void FetchKeysTask(); void FetchKeysTask();
// Fetch keys from server. // Fetch keys from server.
Status FetchKeys(uint32 first_crypto_period_index); Status FetchKeys(bool enable_key_rotation, uint32 first_crypto_period_index);
// Fill |request| with necessary fields for Widevine encryption request. // Fill |request| with necessary fields for Widevine encryption request.
// |request| should not be NULL. // |request| should not be NULL.
void FillRequest(const std::string& content_id, void FillRequest(const std::string& content_id,
bool enable_key_rotation,
uint32 first_crypto_period_index, uint32 first_crypto_period_index,
std::string* request); std::string* request);
// Sign and properly format |request|. // Sign and properly format |request|.
@ -86,7 +84,8 @@ class WidevineEncryptionKeySource : public EncryptionKeySource {
// formatted. |transient_error| will be set to true if it fails and the // formatted. |transient_error| will be set to true if it fails and the
// failure is because of a transient error from the server. |transient_error| // failure is because of a transient error from the server. |transient_error|
// should not be NULL. // should not be NULL.
bool ExtractEncryptionKey(const std::string& response, bool* transient_error); bool ExtractEncryptionKey(bool enable_key_rotation,
const std::string& response, bool* transient_error);
// Push the keys to the key pool. // Push the keys to the key pool.
bool PushToKeyPool(EncryptionKeyMap* encryption_key_map); bool PushToKeyPool(EncryptionKeyMap* encryption_key_map);
@ -99,11 +98,14 @@ class WidevineEncryptionKeySource : public EncryptionKeySource {
std::string policy_; std::string policy_;
scoped_ptr<RequestSigner> signer_; scoped_ptr<RequestSigner> signer_;
const bool key_rotation_enabled_;
const uint32 crypto_period_count_; const uint32 crypto_period_count_;
base::Lock lock_;
bool key_production_started_;
base::WaitableEvent start_key_production_;
uint32 first_crypto_period_index_; uint32 first_crypto_period_index_;
ClosureThread key_production_thread_; ClosureThread key_production_thread_;
ProducerConsumerQueue<scoped_refptr<RefCountedEncryptionKeyMap> > key_pool_; scoped_ptr<EncryptionKeyQueue> key_pool_;
EncryptionKeyMap encryption_key_map_; // For non key rotation request.
Status common_encryption_request_status_; Status common_encryption_request_status_;
DISALLOW_COPY_AND_ASSIGN(WidevineEncryptionKeySource); DISALLOW_COPY_AND_ASSIGN(WidevineEncryptionKeySource);

View File

@ -132,13 +132,12 @@ class WidevineEncryptionKeySourceTest : public ::testing::Test {
mock_http_fetcher_(new MockHttpFetcher()) {} mock_http_fetcher_(new MockHttpFetcher()) {}
protected: protected:
void CreateWidevineEncryptionKeySource(int first_crypto_period_index) { void CreateWidevineEncryptionKeySource() {
widevine_encryption_key_source_.reset(new WidevineEncryptionKeySource( widevine_encryption_key_source_.reset(new WidevineEncryptionKeySource(
kServerUrl, kServerUrl,
kContentId, kContentId,
kPolicy, kPolicy,
mock_request_signer_.PassAs<RequestSigner>(), mock_request_signer_.PassAs<RequestSigner>()));
first_crypto_period_index));
widevine_encryption_key_source_->set_http_fetcher( widevine_encryption_key_source_->set_http_fetcher(
mock_http_fetcher_.PassAs<HttpFetcher>()); mock_http_fetcher_.PassAs<HttpFetcher>());
} }
@ -180,15 +179,9 @@ TEST_F(WidevineEncryptionKeySourceTest, GenerateSignatureFailure) {
EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _)) EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _))
.WillOnce(Return(false)); .WillOnce(Return(false));
CreateWidevineEncryptionKeySource(kDisableKeyRotation); CreateWidevineEncryptionKeySource();
ASSERT_EQ(Status(error::INTERNAL_ERROR, "Signature generation failed."), ASSERT_EQ(Status(error::INTERNAL_ERROR, "Signature generation failed."),
widevine_encryption_key_source_->Initialize()); widevine_encryption_key_source_->Initialize());
// GetKey should return the same failure.
EncryptionKey encryption_key;
ASSERT_EQ(Status(error::INTERNAL_ERROR, "Signature generation failed."),
widevine_encryption_key_source_->GetKey(
EncryptionKeySource::TRACK_TYPE_SD, &encryption_key));
} }
// Check whether expected request message and post data was generated and // Check whether expected request message and post data was generated and
@ -208,15 +201,9 @@ TEST_F(WidevineEncryptionKeySourceTest, HttpPostFailure) {
EXPECT_CALL(*mock_http_fetcher_, Post(kServerUrl, expected_post_data, _)) EXPECT_CALL(*mock_http_fetcher_, Post(kServerUrl, expected_post_data, _))
.WillOnce(Return(kMockStatus)); .WillOnce(Return(kMockStatus));
CreateWidevineEncryptionKeySource(kDisableKeyRotation); CreateWidevineEncryptionKeySource();
ASSERT_EQ(kMockStatus, ASSERT_EQ(kMockStatus,
widevine_encryption_key_source_->Initialize()); widevine_encryption_key_source_->Initialize());
// GetKey should return the same failure.
EncryptionKey encryption_key;
ASSERT_EQ(kMockStatus,
widevine_encryption_key_source_->GetKey(
EncryptionKeySource::TRACK_TYPE_SD, &encryption_key));
} }
TEST_F(WidevineEncryptionKeySourceTest, LicenseStatusOK) { TEST_F(WidevineEncryptionKeySourceTest, LicenseStatusOK) {
@ -229,7 +216,7 @@ TEST_F(WidevineEncryptionKeySourceTest, LicenseStatusOK) {
EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineEncryptionKeySource(kDisableKeyRotation); CreateWidevineEncryptionKeySource();
ASSERT_OK(widevine_encryption_key_source_->Initialize()); ASSERT_OK(widevine_encryption_key_source_->Initialize());
VerifyKeys(); VerifyKeys();
} }
@ -246,7 +233,7 @@ TEST_F(WidevineEncryptionKeySourceTest, RetryOnHttpTimeout) {
.WillOnce(Return(Status(error::TIME_OUT, ""))) .WillOnce(Return(Status(error::TIME_OUT, "")))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineEncryptionKeySource(kDisableKeyRotation); CreateWidevineEncryptionKeySource();
ASSERT_OK(widevine_encryption_key_source_->Initialize()); ASSERT_OK(widevine_encryption_key_source_->Initialize());
VerifyKeys(); VerifyKeys();
} }
@ -269,7 +256,7 @@ TEST_F(WidevineEncryptionKeySourceTest, RetryOnTransientError) {
.WillOnce(DoAll(SetArgPointee<2>(expected_retried_response), .WillOnce(DoAll(SetArgPointee<2>(expected_retried_response),
Return(Status::OK))); Return(Status::OK)));
CreateWidevineEncryptionKeySource(kDisableKeyRotation); CreateWidevineEncryptionKeySource();
ASSERT_OK(widevine_encryption_key_source_->Initialize()); ASSERT_OK(widevine_encryption_key_source_->Initialize());
VerifyKeys(); VerifyKeys();
} }
@ -286,7 +273,7 @@ TEST_F(WidevineEncryptionKeySourceTest, NoRetryOnUnknownError) {
EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
CreateWidevineEncryptionKeySource(kDisableKeyRotation); CreateWidevineEncryptionKeySource();
ASSERT_EQ(error::SERVER_ERROR, ASSERT_EQ(error::SERVER_ERROR,
widevine_encryption_key_source_->Initialize().error_code()); widevine_encryption_key_source_->Initialize().error_code());
} }
@ -307,8 +294,8 @@ std::string GetMockKey(const std::string& track_type, uint32 index) {
return "MockKey" + track_type + "@" + base::UintToString(index); return "MockKey" + track_type + "@" + base::UintToString(index);
} }
std::string GenerateMockLicenseResponse(uint32 initial_crypto_period_index, std::string GenerateMockKeyRotationLicenseResponse(
uint32 crypto_period_count) { uint32 initial_crypto_period_index, uint32 crypto_period_count) {
const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"};
std::string tracks; std::string tracks;
for (uint32 index = initial_crypto_period_index; for (uint32 index = initial_crypto_period_index;
@ -340,9 +327,18 @@ TEST_F(WidevineEncryptionKeySourceTest, KeyRotationTest) {
// Generate expectations in sequence. // Generate expectations in sequence.
InSequence dummy; InSequence dummy;
// Expecting a non-key rotation enabled request on Initialize().
EXPECT_CALL(*mock_request_signer_, GenerateSignature(_, _))
.WillOnce(Return(true));
std::string mock_response = base::StringPrintf(
kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str());
EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
for (uint32 i = 0; i < kCryptoIterations; ++i) { for (uint32 i = 0; i < kCryptoIterations; ++i) {
uint32 first_crypto_period_index = uint32 first_crypto_period_index =
kFirstCryptoPeriodIndex + i * kCryptoPeriodCount; kFirstCryptoPeriodIndex - 1 + i * kCryptoPeriodCount;
std::string expected_message = std::string expected_message =
base::StringPrintf(kCryptoPeriodRequestMessageFormat, base::StringPrintf(kCryptoPeriodRequestMessageFormat,
Base64Encode(kContentId).c_str(), Base64Encode(kContentId).c_str(),
@ -354,24 +350,17 @@ TEST_F(WidevineEncryptionKeySourceTest, KeyRotationTest) {
std::string mock_response = base::StringPrintf( std::string mock_response = base::StringPrintf(
kHttpResponseFormat, kHttpResponseFormat,
Base64Encode(GenerateMockLicenseResponse(first_crypto_period_index, Base64Encode(GenerateMockKeyRotationLicenseResponse(
kCryptoPeriodCount)).c_str()); first_crypto_period_index, kCryptoPeriodCount))
.c_str());
EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _)) EXPECT_CALL(*mock_http_fetcher_, Post(_, _, _))
.WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK)));
} }
CreateWidevineEncryptionKeySource(kFirstCryptoPeriodIndex); CreateWidevineEncryptionKeySource();
ASSERT_OK(widevine_encryption_key_source_->Initialize()); ASSERT_OK(widevine_encryption_key_source_->Initialize());
EncryptionKey encryption_key; EncryptionKey encryption_key;
// Index before kFirstCryptoPeriodIndex is invalid.
Status status = widevine_encryption_key_source_->GetCryptoPeriodKey(
kFirstCryptoPeriodIndex - 1,
EncryptionKeySource::TRACK_TYPE_SD,
&encryption_key);
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
for (size_t i = 0; i < arraysize(kCryptoPeriodIndexes); ++i) { for (size_t i = 0; i < arraysize(kCryptoPeriodIndexes); ++i) {
const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"}; const std::string kTrackTypes[] = {"SD", "HD", "AUDIO"};
for (size_t j = 0; j < 3; ++j) { for (size_t j = 0; j < 3; ++j) {
@ -385,36 +374,11 @@ TEST_F(WidevineEncryptionKeySourceTest, KeyRotationTest) {
} }
// The old crypto period indexes should have been garbage collected. // The old crypto period indexes should have been garbage collected.
status = widevine_encryption_key_source_->GetCryptoPeriodKey( Status status = widevine_encryption_key_source_->GetCryptoPeriodKey(
kFirstCryptoPeriodIndex, kFirstCryptoPeriodIndex,
EncryptionKeySource::TRACK_TYPE_SD, EncryptionKeySource::TRACK_TYPE_SD,
&encryption_key); &encryption_key);
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code()); EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
} }
class WidevineEncryptionKeySourceDeathTest
: public WidevineEncryptionKeySourceTest {};
TEST_F(WidevineEncryptionKeySourceDeathTest,
GetCryptoPeriodKeyOnNonKeyRotationSource) {
CreateWidevineEncryptionKeySource(kDisableKeyRotation);
widevine_encryption_key_source_->Initialize();
EncryptionKey encryption_key;
EXPECT_DEBUG_DEATH(
widevine_encryption_key_source_->GetCryptoPeriodKey(
0, EncryptionKeySource::TRACK_TYPE_SD, &encryption_key),
"");
}
TEST_F(WidevineEncryptionKeySourceDeathTest, GetKeyOnKeyRotationSource) {
CreateWidevineEncryptionKeySource(0);
widevine_encryption_key_source_->Initialize();
EncryptionKey encryption_key;
EXPECT_DEBUG_DEATH(widevine_encryption_key_source_->GetKey(
EncryptionKeySource::TRACK_TYPE_SD, &encryption_key),
"");
}
} // namespace media } // namespace media