Add crypto_period_seconds to Widevine key request (#545)

Add crypto_period_seconds to Widevine key request

When using key rotation with Widevine DRM, a key server has to know
the duration of crypto period to relate generated keys to the media
playback time. This helps the server to provide relevant keys to
a client during license request.

Closes #544.
This commit is contained in:
srebrnyp 2019-01-24 19:39:54 +01:00 committed by Kongqun Yang
parent 6d6db76c80
commit 22af5533b1
12 changed files with 34 additions and 7 deletions

View File

@ -20,6 +20,7 @@ Leandro Moreira <leandro.ribeiro.moreira@gmail.com>
Leo Law <leoltlaw.gh@gmail.com>
More Screens Ltd. <*@morescreens.net>
Philo Inc. <*@philo.com>
Piotr Srebrny <srebrny.piotr@gmail.com>
Richard Eklycke <richard@eklycke.se>
Sergio Ammirata <sergio@ammirata.net>
The Chromium Authors <*@chromium.org>

View File

@ -33,6 +33,7 @@ Joey Parrish <joeyparrish@google.com>
Kongqun Yang <kqyang@google.com>
Leandro Moreira <leandro.ribeiro.moreira@gmail.com>
Leo Law <leoltlaw.gh@gmail.com>
Piotr Srebrny <srebrny.piotr@gmail.com>
Richard Eklycke <richard@eklycke.se>
Rintaro Kuroiwa <rkuroiwa@google.com>
Sergio Ammirata <sergio@ammirata.net>

View File

@ -77,11 +77,14 @@ class KeySource {
/// Get encryption key of the specified track type at the specified index.
/// @param crypto_period_index is the sequence number of the key rotation
/// period for which the key is being retrieved.
/// @param crypto_period_duration_in_seconds is the duration of the crypto
/// period in seconds.
/// @param stream_label is the label of stream for which retrieving the key.
/// @param key is a pointer to the EncryptionKey which will hold the retrieved
/// key. Owner retains ownership, and may not be NULL.
/// @return OK on success, an error status otherwise.
virtual Status GetCryptoPeriodKey(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key) = 0;

View File

@ -225,6 +225,7 @@ Status PlayReadyKeySource::GetKey(const std::vector<uint8_t>& key_id,
}
Status PlayReadyKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key) {
// TODO(robinconnell): Implement key rotation.

View File

@ -56,6 +56,7 @@ class PlayReadyKeySource : public KeySource {
Status GetKey(const std::vector<uint8_t>& key_id,
EncryptionKey* key) override;
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key) override;
/// @}

View File

@ -59,6 +59,7 @@ Status RawKeySource::GetKey(const std::vector<uint8_t>& key_id,
}
Status RawKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key) {
RETURN_IF_ERROR(GetKey(stream_label, key));

View File

@ -30,6 +30,7 @@ class RawKeySource : public KeySource {
Status GetKey(const std::vector<uint8_t>& key_id,
EncryptionKey* key) override;
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key) override;
/// @}

View File

@ -213,6 +213,7 @@ Status WidevineKeySource::GetKey(const std::vector<uint8_t>& key_id,
}
Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key) {
DCHECK(key_production_thread_.HasBeenStarted());
@ -220,6 +221,7 @@ Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
{
base::AutoLock scoped_lock(lock_);
if (!key_production_started_) {
crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds;
// Another client may have a slightly smaller starting crypto period
// index. Set the initial value to account for that.
first_crypto_period_index_ =
@ -230,6 +232,10 @@ Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
new EncryptionKeyQueue(queue_size, first_crypto_period_index_));
start_key_production_.Signal();
key_production_started_ = true;
} else if (crypto_period_duration_in_seconds_ !=
crypto_period_duration_in_seconds) {
return Status(error::INVALID_ARGUMENT,
"Crypto period duration should not change.");
}
}
return GetKeyInternal(crypto_period_index, stream_label, key);
@ -353,6 +359,7 @@ void WidevineKeySource::FillRequest(bool enable_key_rotation,
if (enable_key_rotation) {
request->set_first_crypto_period_index(first_crypto_period_index);
request->set_crypto_period_count(crypto_period_count_);
request->set_crypto_period_seconds(crypto_period_duration_in_seconds_);
}
if (!group_id_.empty())

View File

@ -48,6 +48,7 @@ class WidevineKeySource : public KeySource {
Status GetKey(const std::vector<uint8_t>& key_id,
EncryptionKey* key) override;
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key) override;
/// @}
@ -129,6 +130,7 @@ class WidevineKeySource : public KeySource {
bool key_production_started_ = false;
base::WaitableEvent start_key_production_;
uint32_t first_crypto_period_index_ = 0;
uint32_t crypto_period_duration_in_seconds_ = 0;
std::vector<uint8_t> group_id_;
bool enable_entitlement_license_ = false;
std::unique_ptr<EncryptionKeyQueue> key_pool_;

View File

@ -515,6 +515,7 @@ const char kCryptoPeriodRequestMessageFormat[] =
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
R"("drm_types":["WIDEVINE"],)"
R"("first_crypto_period_index":%u,"crypto_period_count":%u,)"
R"("crypto_period_seconds":%u,)"
R"("protection_scheme":"%s"})";
const char kCryptoPeriodTrackFormat[] =
@ -557,6 +558,7 @@ std::string GenerateMockKeyRotationLicenseResponse(
TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
const uint32_t kFirstCryptoPeriodIndex = 8;
const uint32_t kCryptoPeriodCount = 10;
const uint32_t kCryptoPeriodSeconds = 100;
// Array of indexes to be checked.
const uint32_t kCryptoPeriodIndexes[] = {
kFirstCryptoPeriodIndex, 17, 37, 38, 36, 89};
@ -580,7 +582,7 @@ TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
std::string expected_message = base::StringPrintf(
kCryptoPeriodRequestMessageFormat, Base64Encode(kContentId).c_str(),
kPolicy, first_crypto_period_index, kCryptoPeriodCount,
GetExpectedProtectionScheme().c_str());
kCryptoPeriodSeconds, GetExpectedProtectionScheme().c_str());
EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _))
.WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true)));
@ -605,7 +607,8 @@ TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
for (size_t i = 0; i < arraysize(kCryptoPeriodIndexes); ++i) {
for (const std::string& stream_label : kStreamLabels) {
ASSERT_OK(widevine_key_source_->GetCryptoPeriodKey(
kCryptoPeriodIndexes[i], stream_label, &encryption_key));
kCryptoPeriodIndexes[i], kCryptoPeriodSeconds, stream_label,
&encryption_key));
EXPECT_EQ(GetMockKey(stream_label, kCryptoPeriodIndexes[i]),
ToString(encryption_key.key));
}
@ -613,7 +616,8 @@ TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
// The old crypto period indexes should have been garbage collected.
Status status = widevine_key_source_->GetCryptoPeriodKey(
kFirstCryptoPeriodIndex, kStreamLabels[0], &encryption_key);
kFirstCryptoPeriodIndex, kCryptoPeriodSeconds, kStreamLabels[0],
&encryption_key);
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
}

View File

@ -186,10 +186,13 @@ Status EncryptionHandler::ProcessMediaSample(
// in that case.
const int64_t dts = std::max(clear_sample->dts(), static_cast<int64_t>(0));
const int64_t current_crypto_period_index = dts / crypto_period_duration_;
const uint32_t crypto_period_duration_in_seconds =
static_cast<uint32_t>(encryption_params_.crypto_period_duration_in_seconds);
if (current_crypto_period_index != prev_crypto_period_index_) {
EncryptionKey encryption_key;
RETURN_IF_ERROR(key_source_->GetCryptoPeriodKey(
current_crypto_period_index, stream_label_, &encryption_key));
current_crypto_period_index, crypto_period_duration_in_seconds,
stream_label_, &encryption_key));
if (!CreateEncryptor(encryption_key))
return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
prev_crypto_period_index_ = current_crypto_period_index;

View File

@ -62,8 +62,9 @@ class MockKeySource : public RawKeySource {
public:
MOCK_METHOD2(GetKey,
Status(const std::string& stream_label, EncryptionKey* key));
MOCK_METHOD3(GetCryptoPeriodKey,
MOCK_METHOD4(GetCryptoPeriodKey,
Status(uint32_t crypto_period_index,
uint32_t crypto_period_duration_in_seconds,
const std::string& stream_label,
EncryptionKey* key));
};
@ -420,8 +421,9 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) {
for (int i = 0; i < 5; ++i) {
if ((i % kSegmentsPerCryptoPeriod) == 0) {
EXPECT_CALL(mock_key_source_,
GetCryptoPeriodKey(i / kSegmentsPerCryptoPeriod, _, _))
.WillOnce(DoAll(SetArgPointee<2>(GetMockEncryptionKey()),
GetCryptoPeriodKey(i / kSegmentsPerCryptoPeriod,
kCryptoPeriodDurationInSeconds, _, _))
.WillOnce(DoAll(SetArgPointee<3>(GetMockEncryptionKey()),
Return(Status::OK)));
}
// Use single-frame segment for testing.