From a4659c40dddd75723da5a60bf99770538884c523 Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Tue, 5 Jan 2016 15:17:32 -0800 Subject: [PATCH] Fix AssetId overflow for classic WVM decryption Javascript/JSON does not support int64_t or unsigned numbers. asset_id, if greater than 0x80000000 will be converted to negative number. Use double to represent asset_id instead as 32-bit integer can be lossless represented using double. Bug: 26309000 Change-Id: I3e800c396a1231375776295154fb0d96156e980b --- packager/media/base/widevine_key_source.cc | 14 ++++++-- .../base/widevine_key_source_unittest.cc | 33 ++++++++++++++++--- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/packager/media/base/widevine_key_source.cc b/packager/media/base/widevine_key_source.cc index b2eb1d948d..ea63d761b1 100644 --- a/packager/media/base/widevine_key_source.cc +++ b/packager/media/base/widevine_key_source.cc @@ -184,7 +184,9 @@ Status WidevineKeySource::FetchKeys(const std::vector& pssh_data) { Status WidevineKeySource::FetchKeys(uint32_t asset_id) { base::AutoLock scoped_lock(lock_); request_dict_.Clear(); - request_dict_.SetInteger("asset_id", asset_id); + // Javascript/JSON does not support int64_t or unsigned numbers. Use double + // instead as 32-bit integer can be lossless represented using double. + request_dict_.SetDouble("asset_id", asset_id); return FetchKeysInternal(!kEnableKeyRotation, 0, true); } @@ -381,12 +383,18 @@ void WidevineKeySource::FillRequest(bool enable_key_rotation, // Build key rotation fields. if (enable_key_rotation) { - request_dict_.SetInteger("first_crypto_period_index", + // Javascript/JSON does not support int64_t or unsigned numbers. Use double + // instead as 32-bit integer can be lossless represented using double. + request_dict_.SetDouble("first_crypto_period_index", first_crypto_period_index); request_dict_.SetInteger("crypto_period_count", crypto_period_count_); } - base::JSONWriter::Write(request_dict_, request); + base::JSONWriter::WriteWithOptions( + request_dict_, + // Write doubles that have no fractional part as a normal integer, i.e. + // without using exponential notation or appending a '.0'. + base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, request); } Status WidevineKeySource::GenerateKeyMessage(const std::string& request, diff --git a/packager/media/base/widevine_key_source_unittest.cc b/packager/media/base/widevine_key_source_unittest.cc index beb10ab8df..e214134805 100644 --- a/packager/media/base/widevine_key_source_unittest.cc +++ b/packager/media/base/widevine_key_source_unittest.cc @@ -43,6 +43,12 @@ const char kLicenseStatusUnknownError[] = "UNKNOWN_ERROR"; const char kExpectedRequestMessageFormat[] = "{\"content_id\":\"%s\",\"drm_types\":[\"WIDEVINE\"],\"policy\":\"%s\"," "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; +const char kExpectedRequestMessageWithAssetIdFormat[] = + "{\"asset_id\":%u,\"drm_types\":[\"WIDEVINE\"]," + "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; +const char kExpectedRequestMessageWithPsshFormat[] = + "{\"drm_types\":[\"WIDEVINE\"],\"pssh_data\":\"%s\"," + "\"tracks\":[{\"type\":\"SD\"},{\"type\":\"HD\"},{\"type\":\"AUDIO\"}]}"; const char kExpectedSignedMessageFormat[] = "{\"request\":\"%s\",\"signature\":\"%s\",\"signer\":\"%s\"}"; const char kTrackFormat[] = @@ -52,7 +58,9 @@ const char kClassicTrackFormat[] = "{\"type\":\"%s\",\"key\":\"%s\"}"; const char kLicenseResponseFormat[] = "{\"status\":\"%s\",\"tracks\":[%s]}"; const char kHttpResponseFormat[] = "{\"response\":\"%s\"}"; const char kRequestPsshData[] = "PSSH data"; -const uint32_t kClassicAssetId = 1234; +// 32-bit with leading bit set, to verify that big uint32_t can be handled +// correctly. +const uint32_t kClassicAssetId = 0x80038cd9; std::string Base64Encode(const std::string& input) { std::string output; @@ -212,7 +220,8 @@ TEST_F(WidevineKeySourceTest, GenerateSignatureFailure) { TEST_F(WidevineKeySourceTest, HttpFetchFailure) { std::string expected_message = base::StringPrintf( kExpectedRequestMessageFormat, Base64Encode(kContentId).c_str(), kPolicy); - EXPECT_CALL(*mock_request_signer_, GenerateSignature(expected_message, _)) + EXPECT_CALL(*mock_request_signer_, + GenerateSignature(StrEq(expected_message), _)) .WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true))); std::string expected_post_data = @@ -258,29 +267,43 @@ TEST_F(WidevineKeySourceTest, LicenseStatusCencNotOK) { } TEST_F(WidevineKeySourceTest, LicenseStatusCencWithPsshDataOK) { + std::string expected_message = + base::StringPrintf(kExpectedRequestMessageWithPsshFormat, + Base64Encode(kRequestPsshData).c_str()); + EXPECT_CALL(*mock_request_signer_, + GenerateSignature(StrEq(expected_message), _)) + .WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true))); + std::string mock_response = base::StringPrintf( kHttpResponseFormat, Base64Encode(GenerateMockLicenseResponse()).c_str()); - EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _)) .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); CreateWidevineKeySource(); + widevine_key_source_->set_signer(mock_request_signer_.Pass()); std::vector pssh_data( reinterpret_cast(kRequestPsshData), - reinterpret_cast(kRequestPsshData) + strlen(kContentId)); + reinterpret_cast(kRequestPsshData) + + strlen(kRequestPsshData)); ASSERT_OK(widevine_key_source_->FetchKeys(pssh_data)); VerifyKeys(false); } TEST_F(WidevineKeySourceTest, LicenseStatusClassicOK) { + std::string expected_message = base::StringPrintf( + kExpectedRequestMessageWithAssetIdFormat, kClassicAssetId); + EXPECT_CALL(*mock_request_signer_, + GenerateSignature(StrEq(expected_message), _)) + .WillOnce(DoAll(SetArgPointee<1>(kMockSignature), Return(true))); + std::string mock_response = base::StringPrintf( kHttpResponseFormat, Base64Encode( GenerateMockClassicLicenseResponse()).c_str()); - EXPECT_CALL(*mock_key_fetcher_, FetchKeys(_, _, _)) .WillOnce(DoAll(SetArgPointee<2>(mock_response), Return(Status::OK))); CreateWidevineKeySource(); + widevine_key_source_->set_signer(mock_request_signer_.Pass()); ASSERT_OK(widevine_key_source_->FetchKeys(kClassicAssetId)); VerifyKeys(true); }