From 22af5533b1d6b0544c08897f3d83113a5ab826b5 Mon Sep 17 00:00:00 2001 From: srebrnyp Date: Thu, 24 Jan 2019 19:39:54 +0100 Subject: [PATCH] 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. --- AUTHORS | 1 + CONTRIBUTORS | 1 + packager/media/base/key_source.h | 3 +++ packager/media/base/playready_key_source.cc | 1 + packager/media/base/playready_key_source.h | 1 + packager/media/base/raw_key_source.cc | 1 + packager/media/base/raw_key_source.h | 1 + packager/media/base/widevine_key_source.cc | 7 +++++++ packager/media/base/widevine_key_source.h | 2 ++ packager/media/base/widevine_key_source_unittest.cc | 10 +++++++--- packager/media/crypto/encryption_handler.cc | 5 ++++- packager/media/crypto/encryption_handler_unittest.cc | 8 +++++--- 12 files changed, 34 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index 86aa1eafe3..d2bdbd68e2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -20,6 +20,7 @@ Leandro Moreira Leo Law More Screens Ltd. <*@morescreens.net> Philo Inc. <*@philo.com> +Piotr Srebrny Richard Eklycke Sergio Ammirata The Chromium Authors <*@chromium.org> diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 300f2ca8a9..3bdbfc1a95 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -33,6 +33,7 @@ Joey Parrish Kongqun Yang Leandro Moreira Leo Law +Piotr Srebrny Richard Eklycke Rintaro Kuroiwa Sergio Ammirata diff --git a/packager/media/base/key_source.h b/packager/media/base/key_source.h index 55ea6950e7..7523186a86 100644 --- a/packager/media/base/key_source.h +++ b/packager/media/base/key_source.h @@ -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; diff --git a/packager/media/base/playready_key_source.cc b/packager/media/base/playready_key_source.cc index c390dad53b..bc30a2ead7 100644 --- a/packager/media/base/playready_key_source.cc +++ b/packager/media/base/playready_key_source.cc @@ -225,6 +225,7 @@ Status PlayReadyKeySource::GetKey(const std::vector& 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. diff --git a/packager/media/base/playready_key_source.h b/packager/media/base/playready_key_source.h index d119ab3f75..058ed3995d 100644 --- a/packager/media/base/playready_key_source.h +++ b/packager/media/base/playready_key_source.h @@ -56,6 +56,7 @@ class PlayReadyKeySource : public KeySource { Status GetKey(const std::vector& 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; /// @} diff --git a/packager/media/base/raw_key_source.cc b/packager/media/base/raw_key_source.cc index 63af61170e..a2a76f35d9 100644 --- a/packager/media/base/raw_key_source.cc +++ b/packager/media/base/raw_key_source.cc @@ -59,6 +59,7 @@ Status RawKeySource::GetKey(const std::vector& 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)); diff --git a/packager/media/base/raw_key_source.h b/packager/media/base/raw_key_source.h index 1a088468b0..0c6ea0b478 100644 --- a/packager/media/base/raw_key_source.h +++ b/packager/media/base/raw_key_source.h @@ -30,6 +30,7 @@ class RawKeySource : public KeySource { Status GetKey(const std::vector& 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; /// @} diff --git a/packager/media/base/widevine_key_source.cc b/packager/media/base/widevine_key_source.cc index 5f75eb2176..9a852f4a95 100644 --- a/packager/media/base/widevine_key_source.cc +++ b/packager/media/base/widevine_key_source.cc @@ -213,6 +213,7 @@ Status WidevineKeySource::GetKey(const std::vector& 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()) diff --git a/packager/media/base/widevine_key_source.h b/packager/media/base/widevine_key_source.h index a76590dca4..f28c56b856 100644 --- a/packager/media/base/widevine_key_source.h +++ b/packager/media/base/widevine_key_source.h @@ -48,6 +48,7 @@ class WidevineKeySource : public KeySource { Status GetKey(const std::vector& 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 group_id_; bool enable_entitlement_license_ = false; std::unique_ptr key_pool_; diff --git a/packager/media/base/widevine_key_source_unittest.cc b/packager/media/base/widevine_key_source_unittest.cc index 70d5f67fb2..a883d04772 100644 --- a/packager/media/base/widevine_key_source_unittest.cc +++ b/packager/media/base/widevine_key_source_unittest.cc @@ -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()); } diff --git a/packager/media/crypto/encryption_handler.cc b/packager/media/crypto/encryption_handler.cc index be4a9e9eb8..4d590a2f34 100644 --- a/packager/media/crypto/encryption_handler.cc +++ b/packager/media/crypto/encryption_handler.cc @@ -186,10 +186,13 @@ Status EncryptionHandler::ProcessMediaSample( // in that case. const int64_t dts = std::max(clear_sample->dts(), static_cast(0)); const int64_t current_crypto_period_index = dts / crypto_period_duration_; + const uint32_t crypto_period_duration_in_seconds = + static_cast(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; diff --git a/packager/media/crypto/encryption_handler_unittest.cc b/packager/media/crypto/encryption_handler_unittest.cc index 272ecf866f..0a98bd7811 100644 --- a/packager/media/crypto/encryption_handler_unittest.cc +++ b/packager/media/crypto/encryption_handler_unittest.cc @@ -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.