From 13a4fe8e4c68543aaeb2d3d6a678d5552da7658e Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Tue, 9 Aug 2016 15:10:12 -0700 Subject: [PATCH] Do not convert the protection scheme to network byte order [DO NOT MERGE ANYWHERE] [ Merge of http://go/wvgerrit/19901 ] Protections schemes are specified using a 4CC code {"cbc1", "cbcs", "cenc", "cens"}. A host to network conversion was performed when the PSSH was created and inserted into the license request. A reverse conversion was performed when the code was extracted from the license response. These conversions are problematic if the PSSH is created externally and passed into mediaDrm. To address this, the conversions have been removed and allow protobuf to handle byte ordering. For backward compatibility we allow codes in either ordering. b/30713238 Change-Id: I721b375e446526e544856759cada76a1fa6c7be5 --- .../cdm/core/src/initialization_data.cpp | 4 +- libwvdrmengine/cdm/core/src/license.cpp | 23 +++- .../test/initialization_data_unittest.cpp | 2 +- .../cdm/test/request_license_test.cpp | 110 ++++++++++++++++++ 4 files changed, 131 insertions(+), 8 deletions(-) diff --git a/libwvdrmengine/cdm/core/src/initialization_data.cpp b/libwvdrmengine/cdm/core/src/initialization_data.cpp index ea692e01..2ce009b6 100644 --- a/libwvdrmengine/cdm/core/src/initialization_data.cpp +++ b/libwvdrmengine/cdm/core/src/initialization_data.cpp @@ -476,9 +476,9 @@ bool InitializationData::ConstructWidevineInitData( cenc_header.set_provider(provider); cenc_header.set_content_id(content_id); if (method == kHlsMethodAes128) - cenc_header.set_protection_scheme(htonl(kFourCcCbc1)); + cenc_header.set_protection_scheme(kFourCcCbc1); else - cenc_header.set_protection_scheme(htonl(kFourCcCbcs)); + cenc_header.set_protection_scheme(kFourCcCbcs); cenc_header.SerializeToString(init_data_proto); return true; } diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 04603769..fdc205a2 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -65,6 +65,8 @@ const unsigned char kServiceCertificateCAPublicKey[] = { } const uint32_t kFourCcCbc1 = 0x63626331; const uint32_t kFourCcCbcs = 0x63626373; +const uint32_t kFourCcLittleEndianCbc1 = 0x31636263; +const uint32_t kFourCcLittleEndianCbcs = 0x73636263; const uint32_t kFourCcCenc = 0x63656e63; const uint32_t kFourCcCens = 0x63656e73; @@ -115,12 +117,23 @@ static std::vector ExtractContentKeys(const License& license) { } uint32_t four_cc = kFourCcCenc; if (license.has_protection_scheme()) { - four_cc = ntohl(license.protection_scheme()); + four_cc = license.protection_scheme(); + } + switch (four_cc) { + // b/30713238: Android N assumed that the "protection scheme" Four + // CC code, after being extracted from the protobuf, was host byte + // order dependent. Later versions do not assume this, and thus, + // for backwards compatibility, must support both byte orders. + case kFourCcCbc1: + case kFourCcCbcs: + case kFourCcLittleEndianCbc1: + case kFourCcLittleEndianCbcs: + key.set_cipher_mode(kCipherModeCbc); + break; + default: + key.set_cipher_mode(kCipherModeCtr); + break; } - if (four_cc == kFourCcCbc1 || four_cc == kFourCcCbcs) - key.set_cipher_mode(kCipherModeCbc); - else - key.set_cipher_mode(kCipherModeCtr); key_array.push_back(key); break; } diff --git a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp index e8de2ea2..c9836726 100644 --- a/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/initialization_data_unittest.cpp @@ -624,7 +624,7 @@ TEST_P(HlsConstructionTest, InitData) { case kHlsMethodSampleAes: protection_scheme = kFourCcCbcs; break; default: break; } - EXPECT_EQ(protection_scheme, ntohl(cenc_header.protection_scheme())); + EXPECT_EQ(protection_scheme, cenc_header.protection_scheme()); } } diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 6c5acd82..fd33acc7 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -755,6 +755,62 @@ HlsDecryptionInfo kHlsDecryptionTestVectors[] = { {true, 1, &kSampleAes192ByteSegmentInfo[0], kAttributeListSampleAes}, {true, 1, &kSampleAes338ByteSegmentInfo[0], kAttributeListSampleAes}, }; + +const std::string kHlsPsshAes128LittleEndianFourCC = wvcdm::a2bs_hex( + "00000060" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000040" // pssh data size + // pssh data: + "08011210AB6531FF6E6EA15E387B019E" + "59C2DE0A1A0D7769646576696E655F74" + "6573742215686C735F6165735F313238" + "5F73747265616D696E6748E3C48D8B03"); +const std::string kHlsPsshSampleAesLittleEndianFourCC = wvcdm::a2bs_hex( + "00000063" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000043" // pssh data size + // pssh data: + "08011210613DB35603320EB8E7EA24BD" + "EEA3FDB81A0D7769646576696E655F74" + "6573742218686C735F73616D706C655F" + "6165735F73747265616D696E6748E3C4" + "8D9B07"); +const std::string kHlsPsshAes128FourCC = wvcdm::a2bs_hex( + "00000060" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000040" // pssh data size + // pssh data: + "08011210AB6531FF6E6EA15E387B019E" + "59C2DE0A1A0D7769646576696E655F74" + "6573742215686C735F6165735F313238" + "5F73747265616D696E6748B1C6899B06"); +const std::string kHlsPsshSampleAesFourCC = wvcdm::a2bs_hex( + "00000063" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000043" // pssh data size + // pssh data: + "08011210613DB35603320EB8E7EA24BD" + "EEA3FDB81A0D7769646576696E655F74" + "6573742218686C735F73616D706C655F" + "6165735F73747265616D696E6748F3C6" + "899B06"); + +HlsDecryptionInfo kHlsFourCCBackwardCompatibilityTestVectors[] = { + {false, 2, &kAes128MultiSegmentInfo[0], kHlsPsshAes128LittleEndianFourCC}, + {true, 1, &kSampleAes160ByteSegmentInfo[0], + kHlsPsshSampleAesLittleEndianFourCC}, + {false, 2, &kAes128MultiSegmentInfo[0], kHlsPsshAes128FourCC}, + {true, 1, &kSampleAes160ByteSegmentInfo[0], kHlsPsshSampleAesFourCC}, +}; + } // namespace namespace wvcdm { @@ -3313,6 +3369,60 @@ INSTANTIATE_TEST_CASE_P(Cdm, WvHlsDecryptionTest, ::testing::Range(&kHlsDecryptionTestVectors[0], &kHlsDecryptionTestVectors[11])); +class WvHlsFourCCBackwardCompatibilityTest + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvHlsFourCCBackwardCompatibilityTest, HlsDecryptionTest) { + Provision(kLevel3); + TestWvCdmClientPropertySet client_property_set; + client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + HlsDecryptionInfo* info = GetParam(); + + TestWvCdmHlsEventListener listener; + decryptor_.OpenSession(g_key_system, &client_property_set, EMPTY_ORIGIN, + &listener, &session_id_); + CdmAppParameterMap app_parameters; + GenerateKeyRequest(wvcdm::KEY_MESSAGE, ISO_BMFF_VIDEO_MIME_TYPE, + info->attribute_list, app_parameters, + kLicenseTypeStreaming, NULL); + //TODO(rfrias): Remove once we switch to git-on-borg + std::string license_server = "https://proxy.uat.widevine.com/proxy"; + VerifyKeyRequestResponse(license_server, g_client_auth, false); + CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap(); + EXPECT_EQ(1u, key_status_map.size()); + KeyId key_id = key_status_map.begin()->first; + EXPECT_EQ(KEY_ID_SIZE, key_id.size()); + + for (size_t i = 0; i < info->number_of_segments; ++i) { + HlsSegmentInfo* data = info->segment_info + i; + std::vector output_buffer(data->encrypted_data.size(), 0); + std::vector iv(data->iv.begin(), data->iv.end()); + CdmDecryptionParameters decryption_parameters( + &key_id, reinterpret_cast(data->encrypted_data.c_str()), + data->encrypted_data.size(), &iv, 0, &output_buffer[0]); + decryption_parameters.is_encrypted = true; + decryption_parameters.is_secure = false; + decryption_parameters.cipher_mode = kCipherModeCbc; + if (info->sample_aes) { + decryption_parameters.pattern_descriptor.encrypt_blocks = 1; + decryption_parameters.pattern_descriptor.skip_blocks = 9; + } + EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, false, + decryption_parameters)); + EXPECT_EQ(data->clear_data, + std::string(reinterpret_cast(&output_buffer[0]), + output_buffer.size())); + } + + decryptor_.CloseSession(session_id_); +} + +INSTANTIATE_TEST_CASE_P( + Cdm, WvHlsFourCCBackwardCompatibilityTest, + ::testing::Range(&kHlsFourCCBackwardCompatibilityTestVectors[0], + &kHlsFourCCBackwardCompatibilityTestVectors[4])); + } // namespace wvcdm void show_menu(char* prog_name) {