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:
parent
6d6db76c80
commit
22af5533b1
1
AUTHORS
1
AUTHORS
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
/// @}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
/// @}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue