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>
|
Leo Law <leoltlaw.gh@gmail.com>
|
||||||
More Screens Ltd. <*@morescreens.net>
|
More Screens Ltd. <*@morescreens.net>
|
||||||
Philo Inc. <*@philo.com>
|
Philo Inc. <*@philo.com>
|
||||||
|
Piotr Srebrny <srebrny.piotr@gmail.com>
|
||||||
Richard Eklycke <richard@eklycke.se>
|
Richard Eklycke <richard@eklycke.se>
|
||||||
Sergio Ammirata <sergio@ammirata.net>
|
Sergio Ammirata <sergio@ammirata.net>
|
||||||
The Chromium Authors <*@chromium.org>
|
The Chromium Authors <*@chromium.org>
|
||||||
|
|
|
@ -33,6 +33,7 @@ Joey Parrish <joeyparrish@google.com>
|
||||||
Kongqun Yang <kqyang@google.com>
|
Kongqun Yang <kqyang@google.com>
|
||||||
Leandro Moreira <leandro.ribeiro.moreira@gmail.com>
|
Leandro Moreira <leandro.ribeiro.moreira@gmail.com>
|
||||||
Leo Law <leoltlaw.gh@gmail.com>
|
Leo Law <leoltlaw.gh@gmail.com>
|
||||||
|
Piotr Srebrny <srebrny.piotr@gmail.com>
|
||||||
Richard Eklycke <richard@eklycke.se>
|
Richard Eklycke <richard@eklycke.se>
|
||||||
Rintaro Kuroiwa <rkuroiwa@google.com>
|
Rintaro Kuroiwa <rkuroiwa@google.com>
|
||||||
Sergio Ammirata <sergio@ammirata.net>
|
Sergio Ammirata <sergio@ammirata.net>
|
||||||
|
|
|
@ -77,11 +77,14 @@ class KeySource {
|
||||||
/// Get encryption key of the specified track type at the specified index.
|
/// Get encryption key of the specified track type at the specified index.
|
||||||
/// @param crypto_period_index is the sequence number of the key rotation
|
/// @param crypto_period_index is the sequence number of the key rotation
|
||||||
/// period for which the key is being retrieved.
|
/// 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 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
|
/// @param key is a pointer to the EncryptionKey which will hold the retrieved
|
||||||
/// key. Owner retains ownership, and may not be NULL.
|
/// key. Owner retains ownership, and may not be NULL.
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
virtual Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
virtual Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key) = 0;
|
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,
|
Status PlayReadyKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key) {
|
EncryptionKey* key) {
|
||||||
// TODO(robinconnell): Implement key rotation.
|
// TODO(robinconnell): Implement key rotation.
|
||||||
|
|
|
@ -56,6 +56,7 @@ class PlayReadyKeySource : public KeySource {
|
||||||
Status GetKey(const std::vector<uint8_t>& key_id,
|
Status GetKey(const std::vector<uint8_t>& key_id,
|
||||||
EncryptionKey* key) override;
|
EncryptionKey* key) override;
|
||||||
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key) override;
|
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,
|
Status RawKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key) {
|
EncryptionKey* key) {
|
||||||
RETURN_IF_ERROR(GetKey(stream_label, 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,
|
Status GetKey(const std::vector<uint8_t>& key_id,
|
||||||
EncryptionKey* key) override;
|
EncryptionKey* key) override;
|
||||||
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key) override;
|
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,
|
Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key) {
|
EncryptionKey* key) {
|
||||||
DCHECK(key_production_thread_.HasBeenStarted());
|
DCHECK(key_production_thread_.HasBeenStarted());
|
||||||
|
@ -220,6 +221,7 @@ Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
{
|
{
|
||||||
base::AutoLock scoped_lock(lock_);
|
base::AutoLock scoped_lock(lock_);
|
||||||
if (!key_production_started_) {
|
if (!key_production_started_) {
|
||||||
|
crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds;
|
||||||
// Another client may have a slightly smaller starting crypto period
|
// Another client may have a slightly smaller starting crypto period
|
||||||
// index. Set the initial value to account for that.
|
// index. Set the initial value to account for that.
|
||||||
first_crypto_period_index_ =
|
first_crypto_period_index_ =
|
||||||
|
@ -230,6 +232,10 @@ Status WidevineKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
new EncryptionKeyQueue(queue_size, first_crypto_period_index_));
|
new EncryptionKeyQueue(queue_size, first_crypto_period_index_));
|
||||||
start_key_production_.Signal();
|
start_key_production_.Signal();
|
||||||
key_production_started_ = true;
|
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);
|
return GetKeyInternal(crypto_period_index, stream_label, key);
|
||||||
|
@ -353,6 +359,7 @@ void WidevineKeySource::FillRequest(bool enable_key_rotation,
|
||||||
if (enable_key_rotation) {
|
if (enable_key_rotation) {
|
||||||
request->set_first_crypto_period_index(first_crypto_period_index);
|
request->set_first_crypto_period_index(first_crypto_period_index);
|
||||||
request->set_crypto_period_count(crypto_period_count_);
|
request->set_crypto_period_count(crypto_period_count_);
|
||||||
|
request->set_crypto_period_seconds(crypto_period_duration_in_seconds_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!group_id_.empty())
|
if (!group_id_.empty())
|
||||||
|
|
|
@ -48,6 +48,7 @@ class WidevineKeySource : public KeySource {
|
||||||
Status GetKey(const std::vector<uint8_t>& key_id,
|
Status GetKey(const std::vector<uint8_t>& key_id,
|
||||||
EncryptionKey* key) override;
|
EncryptionKey* key) override;
|
||||||
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
Status GetCryptoPeriodKey(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key) override;
|
EncryptionKey* key) override;
|
||||||
/// @}
|
/// @}
|
||||||
|
@ -129,6 +130,7 @@ class WidevineKeySource : public KeySource {
|
||||||
bool key_production_started_ = false;
|
bool key_production_started_ = false;
|
||||||
base::WaitableEvent start_key_production_;
|
base::WaitableEvent start_key_production_;
|
||||||
uint32_t first_crypto_period_index_ = 0;
|
uint32_t first_crypto_period_index_ = 0;
|
||||||
|
uint32_t crypto_period_duration_in_seconds_ = 0;
|
||||||
std::vector<uint8_t> group_id_;
|
std::vector<uint8_t> group_id_;
|
||||||
bool enable_entitlement_license_ = false;
|
bool enable_entitlement_license_ = false;
|
||||||
std::unique_ptr<EncryptionKeyQueue> key_pool_;
|
std::unique_ptr<EncryptionKeyQueue> key_pool_;
|
||||||
|
|
|
@ -515,6 +515,7 @@ const char kCryptoPeriodRequestMessageFormat[] =
|
||||||
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
|
R"({"type":"UHD2"},{"type":"AUDIO"}],)"
|
||||||
R"("drm_types":["WIDEVINE"],)"
|
R"("drm_types":["WIDEVINE"],)"
|
||||||
R"("first_crypto_period_index":%u,"crypto_period_count":%u,)"
|
R"("first_crypto_period_index":%u,"crypto_period_count":%u,)"
|
||||||
|
R"("crypto_period_seconds":%u,)"
|
||||||
R"("protection_scheme":"%s"})";
|
R"("protection_scheme":"%s"})";
|
||||||
|
|
||||||
const char kCryptoPeriodTrackFormat[] =
|
const char kCryptoPeriodTrackFormat[] =
|
||||||
|
@ -557,6 +558,7 @@ std::string GenerateMockKeyRotationLicenseResponse(
|
||||||
TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
|
TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
|
||||||
const uint32_t kFirstCryptoPeriodIndex = 8;
|
const uint32_t kFirstCryptoPeriodIndex = 8;
|
||||||
const uint32_t kCryptoPeriodCount = 10;
|
const uint32_t kCryptoPeriodCount = 10;
|
||||||
|
const uint32_t kCryptoPeriodSeconds = 100;
|
||||||
// Array of indexes to be checked.
|
// Array of indexes to be checked.
|
||||||
const uint32_t kCryptoPeriodIndexes[] = {
|
const uint32_t kCryptoPeriodIndexes[] = {
|
||||||
kFirstCryptoPeriodIndex, 17, 37, 38, 36, 89};
|
kFirstCryptoPeriodIndex, 17, 37, 38, 36, 89};
|
||||||
|
@ -580,7 +582,7 @@ TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
|
||||||
std::string expected_message = base::StringPrintf(
|
std::string expected_message = base::StringPrintf(
|
||||||
kCryptoPeriodRequestMessageFormat, Base64Encode(kContentId).c_str(),
|
kCryptoPeriodRequestMessageFormat, Base64Encode(kContentId).c_str(),
|
||||||
kPolicy, first_crypto_period_index, kCryptoPeriodCount,
|
kPolicy, first_crypto_period_index, kCryptoPeriodCount,
|
||||||
GetExpectedProtectionScheme().c_str());
|
kCryptoPeriodSeconds, GetExpectedProtectionScheme().c_str());
|
||||||
EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _))
|
EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _))
|
||||||
.WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true)));
|
.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 (size_t i = 0; i < arraysize(kCryptoPeriodIndexes); ++i) {
|
||||||
for (const std::string& stream_label : kStreamLabels) {
|
for (const std::string& stream_label : kStreamLabels) {
|
||||||
ASSERT_OK(widevine_key_source_->GetCryptoPeriodKey(
|
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]),
|
EXPECT_EQ(GetMockKey(stream_label, kCryptoPeriodIndexes[i]),
|
||||||
ToString(encryption_key.key));
|
ToString(encryption_key.key));
|
||||||
}
|
}
|
||||||
|
@ -613,7 +616,8 @@ TEST_P(WidevineKeySourceParameterizedTest, KeyRotationTest) {
|
||||||
|
|
||||||
// The old crypto period indexes should have been garbage collected.
|
// The old crypto period indexes should have been garbage collected.
|
||||||
Status status = widevine_key_source_->GetCryptoPeriodKey(
|
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());
|
EXPECT_EQ(error::INVALID_ARGUMENT, status.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,10 +186,13 @@ Status EncryptionHandler::ProcessMediaSample(
|
||||||
// in that case.
|
// in that case.
|
||||||
const int64_t dts = std::max(clear_sample->dts(), static_cast<int64_t>(0));
|
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 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_) {
|
if (current_crypto_period_index != prev_crypto_period_index_) {
|
||||||
EncryptionKey encryption_key;
|
EncryptionKey encryption_key;
|
||||||
RETURN_IF_ERROR(key_source_->GetCryptoPeriodKey(
|
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))
|
if (!CreateEncryptor(encryption_key))
|
||||||
return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
|
return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
|
||||||
prev_crypto_period_index_ = current_crypto_period_index;
|
prev_crypto_period_index_ = current_crypto_period_index;
|
||||||
|
|
|
@ -62,8 +62,9 @@ class MockKeySource : public RawKeySource {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD2(GetKey,
|
MOCK_METHOD2(GetKey,
|
||||||
Status(const std::string& stream_label, EncryptionKey* key));
|
Status(const std::string& stream_label, EncryptionKey* key));
|
||||||
MOCK_METHOD3(GetCryptoPeriodKey,
|
MOCK_METHOD4(GetCryptoPeriodKey,
|
||||||
Status(uint32_t crypto_period_index,
|
Status(uint32_t crypto_period_index,
|
||||||
|
uint32_t crypto_period_duration_in_seconds,
|
||||||
const std::string& stream_label,
|
const std::string& stream_label,
|
||||||
EncryptionKey* key));
|
EncryptionKey* key));
|
||||||
};
|
};
|
||||||
|
@ -420,8 +421,9 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) {
|
||||||
for (int i = 0; i < 5; ++i) {
|
for (int i = 0; i < 5; ++i) {
|
||||||
if ((i % kSegmentsPerCryptoPeriod) == 0) {
|
if ((i % kSegmentsPerCryptoPeriod) == 0) {
|
||||||
EXPECT_CALL(mock_key_source_,
|
EXPECT_CALL(mock_key_source_,
|
||||||
GetCryptoPeriodKey(i / kSegmentsPerCryptoPeriod, _, _))
|
GetCryptoPeriodKey(i / kSegmentsPerCryptoPeriod,
|
||||||
.WillOnce(DoAll(SetArgPointee<2>(GetMockEncryptionKey()),
|
kCryptoPeriodDurationInSeconds, _, _))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<3>(GetMockEncryptionKey()),
|
||||||
Return(Status::OK)));
|
Return(Status::OK)));
|
||||||
}
|
}
|
||||||
// Use single-frame segment for testing.
|
// Use single-frame segment for testing.
|
||||||
|
|
Loading…
Reference in New Issue