// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine Master // License Agreement. #include #include #include "file_store.h" #include "initialization_data.h" #include "license_protocol.pb.h" #include "platform.h" #include "string_conversions.h" #include "wv_cdm_constants.h" // References: // [1] http://dashif.org/identifiers/content-protection/ // [2] http://www.w3.org/TR/encrypted-media/cenc-format.html#common-system // [3] https://tools.ietf.org/html/draft-pantos-http-live-streaming-18 namespace wvcdm { // Protobuf generated classes. using video_widevine::WidevinePsshData; namespace { // Import names from ::testing for convenience using ::testing::_; // Constants for JSON formatting const std::string kLeftBrace = "{"; const std::string kRightBrace = "}"; const std::string kLeftBracket = "["; const std::string kRightBracket = "]"; const std::string kComma = ","; const std::string kColon = ":"; const std::string kDoubleQuote = "\""; const std::string kNewline = "\n"; const std::string kFourSpaceIndent = " "; const std::string kJsonProvider = "provider"; const std::string kJsonContentId = "content_id"; const std::string kJsonKeyIds = "key_ids"; const uint32_t kFourCcCbc1 = 0x63626331; const uint32_t kFourCcCbcs = 0x63626373; const std::string kWidevinePssh = a2bs_hex( // Widevine PSSH box "00000042" // atom size "70737368" // atom type="pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const std::string kWidevinePsshFirst = a2bs_hex( // first PSSH box, Widevine "00000042" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031" // second PSSH box, Playready [1] "00000028" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "9a04f07998404286ab92e65be0885f95" // system id (PlayReady) "00000008" // data size // arbitrary data: "0102030405060708"); const std::string kWidevinePsshAfterV0Pssh = a2bs_hex( // first PSSH box, Playready [1] "00000028" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "9a04f07998404286ab92e65be0885f95" // system id (PlayReady) "00000008" // data size // arbitrary data: "0102030405060708" // second PSSH box, Widevine "00000042" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const std::string kWidevinePsshAfterNonZeroFlags = a2bs_hex( // first PSSH box, Playready [1] "00000028" // atom size "70737368" // atom type "pssh" "00abcdef" // v0, flags=abcdef "9a04f07998404286ab92e65be0885f95" // system id (PlayReady) "00000008" // data size // arbitrary data: "0102030405060708" // second PSSH box, Widevine "00000042" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const std::string kWidevinePsshAfterV1Pssh = a2bs_hex( // first PSSH box, generic CENC [2] "00000044" // atom size "70737368" // atom type "pssh" "01000000" // v1, flags=0 "1077efecc0b24d02ace33c1e52e2fb4b" // system id (generic CENC) "00000002" // key ID count "30313233343536373839303132333435" // key ID="0123456789012345" "38393031323334354142434445464748" // key ID="ABCDEFGHIJKLMNOP" "00000000" // data size=0 // second PSSH box, Widevine "00000042" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const std::string kWidevineV1Pssh = a2bs_hex( // Widevine PSSH box, v1 format "00000066" // atom size "70737368" // atom type "pssh" "01000000" // v1, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000002" // key ID count "30313233343536373839303132333435" // key ID="0123456789012345" "38393031323334354142434445464748" // key ID="ABCDEFGHIJKLMNOP" "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const std::string kOtherBoxFirst = a2bs_hex( // first box, not a PSSH box "00000018" // atom size "77686174" // atom type "what" "deadbeefdeadbeefdeadbeefdeadbeef" // garbage box data // second box, a Widevine PSSH box "00000042" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const std::string kZeroSizedPsshBox = a2bs_hex( // Widevine PSSH box "00000000" // atom size (whole buffer) "70737368" // atom type="pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const std::string kMultipleWidevinePsshBox = a2bs_hex( // first PSSH box, Widevine with single keys "00000042" // atom size "70737368" // atom type "pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "00000022" // data size // data: "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031" // second PSSH box, Widevine with entitled keys "000001fb" // atom size "70737368" // atom type="pssh" "00000000" // v0, flags=0 "edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine) "000001db" // data size // data: "220b47726f7570563254657374381448" "e3dc959b065002580272580a10668093" "381a8c5be48a0168ce372726ac1210c8" "326486bb5d5c4a958f00b1111afc811a" "20082cd9d3aed3ebe6239d30fbcf0b22" "1d28cbb0360ea1295c2363973346ec00" "512210914781334e864c8eb7f768cf26" "49073872580a10f872d11d5b1052f2bd" "a94e60a0e383021210450897c987a85c" "2e9579f968554a12991a2097e603ceea" "f35ed8cef1029eae7a0a54701e3d6db6" "80e7da1de3b22a8db347fb2210b41c34" "29b7bb96972bbaf6587bc0ddf172580a" "10bac58b9fce9e5929a42a180e529f19" "4712103f11f22988d25659b145ce4854" "3e6b141a20416e22768e5a57b08d155e" "5210d00658056947ff06d626668bceb3" "5eb01c6b57221081fb2ff3fef79d332f" "f98be46233596972580a101261c8036d" "ae5c8caa968858aa0ca9cc12106d583c" "b37c1456519843a81cf49912221a20c2" "1116bb54a226e8d879a4cd41d8879920" "2ae85b80d83b1b4447e5d7fcad6f6a22" "100b27a4c3f44771d2b0c7c34c66af35" "b572580a10ab1c8c259c6b5967991389" "65bff5ac0c1210b5b4473658565d3786" "efaf4b85d8e6e21a203ce6a9085285c2" "ece0b650dc83dd7aa8ac849611a8e3f8" "3c8f389223c0f3621522101946f0c2a3" "d543101cc842bbec2d0b30"); // These are the data payloads of the two PSSH boxes in // kMultipleWidevinePsshBox. const CdmInitData kSingleKeyWidevinePsshBoxData = a2bs_hex( "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"); const CdmInitData kEntitledKeysWidevinePsshBoxData = a2bs_hex( "220b47726f7570563254657374381448" "e3dc959b065002580272580a10668093" "381a8c5be48a0168ce372726ac1210c8" "326486bb5d5c4a958f00b1111afc811a" "20082cd9d3aed3ebe6239d30fbcf0b22" "1d28cbb0360ea1295c2363973346ec00" "512210914781334e864c8eb7f768cf26" "49073872580a10f872d11d5b1052f2bd" "a94e60a0e383021210450897c987a85c" "2e9579f968554a12991a2097e603ceea" "f35ed8cef1029eae7a0a54701e3d6db6" "80e7da1de3b22a8db347fb2210b41c34" "29b7bb96972bbaf6587bc0ddf172580a" "10bac58b9fce9e5929a42a180e529f19" "4712103f11f22988d25659b145ce4854" "3e6b141a20416e22768e5a57b08d155e" "5210d00658056947ff06d626668bceb3" "5eb01c6b57221081fb2ff3fef79d332f" "f98be46233596972580a101261c8036d" "ae5c8caa968858aa0ca9cc12106d583c" "b37c1456519843a81cf49912221a20c2" "1116bb54a226e8d879a4cd41d8879920" "2ae85b80d83b1b4447e5d7fcad6f6a22" "100b27a4c3f44771d2b0c7c34c66af35" "b572580a10ab1c8c259c6b5967991389" "65bff5ac0c1210b5b4473658565d3786" "efaf4b85d8e6e21a203ce6a9085285c2" "ece0b650dc83dd7aa8ac849611a8e3f8" "3c8f389223c0f3621522101946f0c2a3" "d543101cc842bbec2d0b30"); // OEMCrypto Versions known to have and not have entitlement license support. const std::string kOemCryptoWithoutEntitlements = "13"; const std::string kOemCryptoWithEntitlements = "14"; // HLS test attribute key and values const std::string kHlsIvHexValue = "6DF49213A781E338628D0E9C812D328E"; const std::string kHlsIvValue = "0x" + kHlsIvHexValue; const std::string kHlsKeyFormatValue = "com.widevine.alpha"; const std::string kHlsKeyFormatValueOther = "com.example"; const std::string kHlsTestKey1 = "TESTKEY1"; const std::string kHlsTestValue1 = "testvalue1"; const std::string kHlsTestKey2 = "TESTKEY2"; const std::string kHlsTestValue2 = "testvalue2"; const std::string kHlsTestInvalidLowercaseKey = "testkey3"; const std::string kHlsTestKeyWithDash = "TEST-KEY4"; const std::string kHlsTestInvalidNonAlphanumKey = "TEST;KEY4"; const std::string kHlsTestValueWithEmbeddedQuote = "test\"value1"; const std::string kHlsTestEmptyHexValue = ""; const std::string kHlsTestNoHexValue = "0x"; const std::string kHlsTestHexValueWithOddBytes = kHlsIvHexValue + "7"; const std::string kHlsTestInvalidHexValue = kHlsIvHexValue + "g7"; char kHlsTestKeyFormatVersionsSeparator = '/'; const std::string kHlsTestUriDataFormat = "data:text/plain;base64,"; const std::string kHlsTestProvider = "youtube"; const std::string kHlsTestContentId = "MjAxNV9UZWFycw=="; const std::string kHlsTestKeyId1 = "371E135E1A985D75D198A7F41020DC23"; const std::string kHlsTestKeyId2 = "E670D9B60AE61583E01BC9253FA19261"; const std::string kHlsTestKeyId3 = "78094E72165DF39721B8A354D6A71390"; const std::string kHlsTestInvalidKeyId = "B8A354D6A71390"; const std::string kHlsTestKeyFormatVersion1 = "1"; const std::string kHlsTestKeyFormatVersion3 = "3"; const std::string kHlsTestKeyFormatVersion5 = "5"; const std::string kHlsTestKeyFormatVersion13 = "13"; const std::string kHlsTestKeyFormatVersion21 = "21"; const std::string kHlsTestKeyFormatVersion37 = "37"; // HLS attribute helper functions std::string QuoteString(const std::string& value) { return "\"" + value + "\""; } std::string GenerateJsonInitData(const std::string& provider, const std::string& content_id, const std::vector& key_ids) { std::string json = kLeftBrace + kNewline; if (provider.size() > 0) { json += kFourSpaceIndent + kDoubleQuote + kJsonProvider + kDoubleQuote + kColon + kDoubleQuote + provider + kDoubleQuote + kComma + kNewline; } if (content_id.size() > 0) { json += kFourSpaceIndent + kDoubleQuote + kJsonContentId + kDoubleQuote + kColon + kDoubleQuote + content_id + kDoubleQuote + kComma + kNewline; } if (key_ids.size() > 0) { json += kFourSpaceIndent + kDoubleQuote + kJsonKeyIds + kDoubleQuote + kColon + kNewline; json += kFourSpaceIndent + kLeftBracket + kNewline; for (size_t i = 0; i < key_ids.size(); ++i) { json += kFourSpaceIndent + kFourSpaceIndent + kDoubleQuote + key_ids[i] + kDoubleQuote; if (i != key_ids.size() - 1) { json += kComma; } json += kNewline; } json += kFourSpaceIndent + kRightBracket + kNewline; } json += kRightBrace + kNewline; return json; } class VectorOfStrings { public: VectorOfStrings(const std::string& str) { vec_.push_back(str); } VectorOfStrings& Add(const std::string& str) { vec_.push_back(str); return *this; } const std::vector Generate() { return vec_; } private: std::vector vec_; }; std::string GenerateHlsUriData(const std::string& provider, const std::string& content_id, const std::vector& key_ids) { std::string json = GenerateJsonInitData(provider, content_id, key_ids); std::vector json_init_data( reinterpret_cast(json.data()), reinterpret_cast(json.data() + json.size())); return kHlsTestUriDataFormat + Base64Encode(json_init_data); } std::string CreateHlsAttributeList(const std::string& method, const std::string& provider, const std::string& content_id, const std::vector& key_ids, const std::string& iv, const std::string& key_format, const std::string& key_format_version) { return "EXT-X-KEY: " + HLS_METHOD_ATTRIBUTE + "=" + method + "," + HLS_URI_ATTRIBUTE + "=" + QuoteString(GenerateHlsUriData(provider, content_id, key_ids)) + "," + HLS_IV_ATTRIBUTE + "=" + iv + "," + HLS_KEYFORMAT_ATTRIBUTE + "=" + QuoteString(key_format) + "," + HLS_KEYFORMAT_VERSIONS_ATTRIBUTE + "=" + QuoteString(key_format_version); } // HLS attribute list for testing const std::string kHlsAttributeList = CreateHlsAttributeList( HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListKeyFormatUnknown = CreateHlsAttributeList( HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValueOther, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListKeyFormatVersionUnsupported = CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, "2"); const std::string kHlsAttributeListKeyFormatVersionMultiple = CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, "1/2/5"); const std::string kHlsAttributeListMethodAes128 = CreateHlsAttributeList( HLS_METHOD_AES_128, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListMethodNone = CreateHlsAttributeList( HLS_METHOD_NONE, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListMethodInvalid = CreateHlsAttributeList( kHlsTestValue1, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListInvalidUriNoProvider = CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, "", kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListInvalidUriNoContentId = CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, "", VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListInvalidUriNoKeyId = CreateHlsAttributeList( HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings("").Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListValidUriThreeKeyIds = CreateHlsAttributeList( HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1) .Add(kHlsTestKeyId2) .Add(kHlsTestKeyId3) .Generate(), kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListNoIv = CreateHlsAttributeList( HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestNoHexValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); const std::string kHlsAttributeListInvalidIv = CreateHlsAttributeList( HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestHexValueWithOddBytes, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1); std::string InsertHlsAttributeInList(const std::string key, const std::string& value) { return kHlsAttributeList + "," + key + "=" + value + "," + kHlsTestKey2 + "=" + kHlsTestValue2; } } // namespace struct HlsInitDataVariant { HlsInitDataVariant(CdmHlsMethod method, const std::string& provider, const std::string& content_id, const std::string& key_id, bool success) : method_(method), provider_(provider), content_id_(content_id), success_(success) { if (key_id.size() > 0) key_ids_.push_back(key_id); } HlsInitDataVariant& AddKeyId(const std::string& key_id) { key_ids_.push_back(key_id); return *this; } const CdmHlsMethod method_; const std::string provider_; const std::string content_id_; std::vector key_ids_; const bool success_; }; struct HlsAttributeVariant { HlsAttributeVariant(const std::string& attribute_list, const std::string& key, const std::string& value, bool success) : attribute_list_(attribute_list), key_(key), value_(value), success_(success) {} const std::string attribute_list_; const std::string key_; const std::string value_; const bool success_; }; class InitializationDataTest : public ::testing::TestWithParam {}; class HlsAttributeExtractionTest : public ::testing::TestWithParam {}; class HlsHexAttributeExtractionTest : public ::testing::TestWithParam {}; class HlsQuotedAttributeExtractionTest : public ::testing::TestWithParam {}; class HlsKeyFormatVersionsExtractionTest : public ::testing::TestWithParam > {}; class HlsConstructionTest : public ::testing::TestWithParam {}; class HlsInitDataConstructionTest : public ::testing::Test {}; class HlsParseTest : public ::testing::TestWithParam {}; class HlsTest : public ::testing::Test {}; TEST_F(InitializationDataTest, BadType) { InitializationData init_data("bad", kWidevinePssh); EXPECT_TRUE(init_data.IsEmpty()); } TEST_P(InitializationDataTest, Parse) { InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, GetParam()); EXPECT_FALSE(init_data.IsEmpty()); } INSTANTIATE_TEST_CASE_P( ParsePssh, InitializationDataTest, ::testing::Values(kWidevinePssh, kWidevinePsshFirst, kWidevinePsshAfterV0Pssh, kWidevinePsshAfterNonZeroFlags, kWidevinePsshAfterV1Pssh, kWidevineV1Pssh, kOtherBoxFirst, kZeroSizedPsshBox, kMultipleWidevinePsshBox)); TEST_F(InitializationDataTest, HandlesMultipleWidevinePsshs) { InitializationData single_init_data(ISO_BMFF_VIDEO_MIME_TYPE, kMultipleWidevinePsshBox, kOemCryptoWithoutEntitlements); EXPECT_FALSE(single_init_data.IsEmpty()); EXPECT_EQ(kSingleKeyWidevinePsshBoxData, single_init_data.data()); EXPECT_FALSE(single_init_data.contains_entitled_keys()); InitializationData entitled_init_data(ISO_BMFF_VIDEO_MIME_TYPE, kMultipleWidevinePsshBox, kOemCryptoWithEntitlements); EXPECT_FALSE(entitled_init_data.IsEmpty()); EXPECT_EQ(kEntitledKeysWidevinePsshBoxData, entitled_init_data.data()); EXPECT_TRUE(entitled_init_data.contains_entitled_keys()); } TEST_P(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions) { std::vector versions = GetParam(); std::string key_format_versions; for (size_t i = 0; i < versions.size(); ++i) { key_format_versions += versions[i] + kHlsTestKeyFormatVersionsSeparator; } key_format_versions.resize(key_format_versions.size() - sizeof(kHlsTestKeyFormatVersionsSeparator)); std::vector extracted_versions = InitializationData::ExtractKeyFormatVersions(key_format_versions); EXPECT_EQ(versions.size(), extracted_versions.size()); for (size_t i = 0; i < versions.size(); ++i) { bool found = false; for (size_t j = 0; j < extracted_versions.size(); ++j) { if (versions[i] == extracted_versions[j]) { found = true; break; } } EXPECT_TRUE(found); } } INSTANTIATE_TEST_CASE_P( HlsTest, HlsKeyFormatVersionsExtractionTest, ::testing::Values(VectorOfStrings(kHlsTestKeyFormatVersion1).Generate(), VectorOfStrings(kHlsTestKeyFormatVersion21).Generate(), VectorOfStrings(kHlsTestKeyFormatVersion1) .Add(kHlsTestKeyFormatVersion3) .Generate(), VectorOfStrings(kHlsTestKeyFormatVersion1) .Add(kHlsTestKeyFormatVersion3) .Add(kHlsTestKeyFormatVersion13) .Generate(), VectorOfStrings(kHlsTestKeyFormatVersion13) .Add(kHlsTestKeyFormatVersion5) .Add(kHlsTestKeyFormatVersion21) .Add(kHlsTestKeyFormatVersion37) .Generate())); TEST_P(HlsAttributeExtractionTest, ExtractAttribute) { HlsAttributeVariant param = GetParam(); std::string value; if (param.success_) { EXPECT_TRUE(InitializationData::ExtractAttribute(param.attribute_list_, param.key_, &value)); EXPECT_EQ(param.value_, value); } else { EXPECT_FALSE(InitializationData::ExtractAttribute(param.attribute_list_, param.key_, &value)); } } INSTANTIATE_TEST_CASE_P( HlsTest, HlsAttributeExtractionTest, ::testing::Values( HlsAttributeVariant(kHlsAttributeList, HLS_METHOD_ATTRIBUTE, HLS_METHOD_SAMPLE_AES, true), HlsAttributeVariant(kHlsAttributeList, HLS_URI_ATTRIBUTE, QuoteString(GenerateHlsUriData( kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate())), true), HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvValue, true), HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE, QuoteString(kHlsKeyFormatValue), true), HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, QuoteString(HLS_KEYFORMAT_VERSION_VALUE_1), true), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, kHlsTestValue1), kHlsTestKey1, kHlsTestValue1, true), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, kHlsTestValue1), kHlsTestKey2, kHlsTestValue2, true), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "\t", kHlsTestValue1), kHlsTestKey1, kHlsTestValue1, false), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, " " + kHlsTestValue1), kHlsTestKey1, kHlsTestValue1, false), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, kHlsTestValue1 + " "), kHlsTestKey1, kHlsTestValue1, false), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "3", kHlsTestValue1), kHlsTestKey1, kHlsTestValue1, false), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, ""), kHlsTestKey1, "", true), HlsAttributeVariant(InsertHlsAttributeInList( kHlsTestInvalidLowercaseKey, kHlsTestValue1), kHlsTestInvalidLowercaseKey, kHlsTestValue1, false), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKeyWithDash, kHlsTestValue1), kHlsTestKeyWithDash, kHlsTestValue1, true), HlsAttributeVariant(InsertHlsAttributeInList( kHlsTestInvalidNonAlphanumKey, kHlsTestValue1), kHlsTestInvalidNonAlphanumKey, kHlsTestValue1, false), HlsAttributeVariant( InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)), kHlsTestKey1, QuoteString(kHlsTestValue1), true), HlsAttributeVariant( InsertHlsAttributeInList( kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)), kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote), true))); TEST_P(HlsHexAttributeExtractionTest, ExtractHexAttribute) { HlsAttributeVariant param = GetParam(); std::vector value; if (param.success_) { EXPECT_TRUE(InitializationData::ExtractHexAttribute(param.attribute_list_, param.key_, &value)); EXPECT_EQ(param.value_, b2a_hex(value)); } else { EXPECT_FALSE(InitializationData::ExtractHexAttribute(param.attribute_list_, param.key_, &value)); } } INSTANTIATE_TEST_CASE_P( HlsTest, HlsHexAttributeExtractionTest, ::testing::Values( HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvHexValue, true), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, kHlsTestEmptyHexValue), kHlsTestKey1, kHlsTestEmptyHexValue, false), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, kHlsTestNoHexValue), kHlsTestKey1, kHlsTestNoHexValue, false), HlsAttributeVariant(InsertHlsAttributeInList( kHlsTestKey1, kHlsTestHexValueWithOddBytes), kHlsTestKey1, kHlsTestHexValueWithOddBytes, false), HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, kHlsTestInvalidHexValue), kHlsTestKey1, kHlsTestInvalidHexValue, false))); TEST_P(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute) { HlsAttributeVariant param = GetParam(); std::string value; if (param.success_) { EXPECT_TRUE(InitializationData::ExtractQuotedAttribute( param.attribute_list_, param.key_, &value)); EXPECT_EQ(param.value_, value); } else { EXPECT_FALSE(InitializationData::ExtractQuotedAttribute( param.attribute_list_, param.key_, &value)); } } INSTANTIATE_TEST_CASE_P( HlsTest, HlsQuotedAttributeExtractionTest, ::testing::Values( HlsAttributeVariant( kHlsAttributeList, HLS_URI_ATTRIBUTE, GenerateHlsUriData(kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate()), true), HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE, kHlsKeyFormatValue, true), HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, HLS_KEYFORMAT_VERSION_VALUE_1, true), HlsAttributeVariant( InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)), kHlsTestKey1, kHlsTestValue1, true), HlsAttributeVariant( InsertHlsAttributeInList( kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)), kHlsTestKey1, kHlsTestValueWithEmbeddedQuote, false))); TEST_P(HlsConstructionTest, InitData) { HlsInitDataVariant param = GetParam(); std::string uri = GenerateHlsUriData(param.provider_, param.content_id_, param.key_ids_); std::string value; EXPECT_EQ(param.success_, InitializationData::ConstructWidevineInitData( param.method_, uri, &value)); if (param.success_) { WidevinePsshData cenc_header; EXPECT_TRUE(cenc_header.ParseFromString(value)); EXPECT_EQ(video_widevine::WidevinePsshData_Algorithm_AESCTR, cenc_header.algorithm()); for (size_t i = 0; i < param.key_ids_.size(); ++i) { bool key_id_found = false; if (param.key_ids_[i].size() != 32) continue; for (int j = 0; j < cenc_header.key_ids_size(); ++j) { if (param.key_ids_[i] == b2a_hex(cenc_header.key_ids(j))) { key_id_found = true; break; } } EXPECT_TRUE(key_id_found); } EXPECT_EQ(param.provider_, cenc_header.provider()); std::vector param_content_id_vec(Base64Decode(param.content_id_)); EXPECT_EQ( std::string(param_content_id_vec.begin(), param_content_id_vec.end()), cenc_header.content_id()); uint32_t protection_scheme = 0; switch (param.method_) { case kHlsMethodAes128: protection_scheme = kFourCcCbc1; break; case kHlsMethodSampleAes: protection_scheme = kFourCcCbcs; break; default: break; } EXPECT_EQ(protection_scheme, cenc_header.protection_scheme()); } } INSTANTIATE_TEST_CASE_P( HlsTest, HlsConstructionTest, ::testing::Values( HlsInitDataVariant(kHlsMethodAes128, "", kHlsTestContentId, kHlsTestKeyId1, false), HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider, "", kHlsTestKeyId1, false), HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider, kHlsTestContentId, "", false), HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider, kHlsTestContentId, kHlsTestInvalidKeyId, false), HlsInitDataVariant(kHlsMethodNone, kHlsTestProvider, kHlsTestContentId, kHlsTestKeyId1, false), HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider, kHlsTestContentId, kHlsTestKeyId1, true), HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider, kHlsTestContentId, kHlsTestKeyId1, true), HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider, kHlsTestContentId, kHlsTestKeyId1, true) .AddKeyId(kHlsTestKeyId2) .AddKeyId(kHlsTestKeyId3), HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider, kHlsTestContentId, kHlsTestKeyId1, true) .AddKeyId(kHlsTestKeyId2) .AddKeyId(kHlsTestKeyId3), HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider, kHlsTestContentId, kHlsTestInvalidKeyId, true) .AddKeyId(kHlsTestKeyId1), HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider, kHlsTestContentId, kHlsTestInvalidKeyId, true) .AddKeyId(kHlsTestKeyId1))); TEST_F(HlsInitDataConstructionTest, InvalidUriDataFormat) { std::string json = GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate()); std::vector json_init_data( reinterpret_cast(json.data()), reinterpret_cast(json.data() + json.size())); std::string value; EXPECT_FALSE(InitializationData::ConstructWidevineInitData( kHlsMethodAes128, Base64Encode(json_init_data), &value)); } TEST_F(HlsInitDataConstructionTest, InvalidUriBase64Encode) { std::string json = GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId, VectorOfStrings(kHlsTestKeyId1).Generate()); std::string value; EXPECT_FALSE(InitializationData::ConstructWidevineInitData( kHlsMethodSampleAes, kHlsTestUriDataFormat + json, &value)); } TEST_P(HlsParseTest, Parse) { HlsAttributeVariant param = GetParam(); InitializationData init_data(HLS_INIT_DATA_FORMAT, param.attribute_list_); if (param.success_) { EXPECT_TRUE(init_data.is_hls()); EXPECT_FALSE(init_data.IsEmpty()); if (param.key_.compare(HLS_METHOD_ATTRIBUTE) == 0) { if (param.value_.compare(HLS_METHOD_SAMPLE_AES) == 0) { EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method()); } else if (param.value_.compare(HLS_METHOD_AES_128) == 0) { EXPECT_EQ(kHlsMethodAes128, init_data.hls_method()); } else if (param.value_.compare(HLS_METHOD_NONE) == 0) { EXPECT_EQ(kHlsMethodNone, init_data.hls_method()); } } else { EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method()); } WidevinePsshData cenc_header; EXPECT_TRUE(cenc_header.ParseFromString(init_data.data())); EXPECT_EQ(video_widevine::WidevinePsshData_Algorithm_AESCTR, cenc_header.algorithm()); if (param.key_.compare(kJsonProvider) == 0) { EXPECT_EQ(param.value_, cenc_header.provider()); } else if (param.key_.compare(kJsonContentId) == 0) { EXPECT_EQ(param.value_, cenc_header.content_id()); } else if (param.key_.compare(kJsonKeyIds) == 0) { EXPECT_EQ(param.value_, b2a_hex(cenc_header.key_ids(0))); } EXPECT_EQ(kHlsIvHexValue, b2a_hex(init_data.hls_iv())); } else { EXPECT_TRUE(init_data.is_hls()); EXPECT_TRUE(init_data.IsEmpty()); } } INSTANTIATE_TEST_CASE_P( HlsTest, HlsParseTest, ::testing::Values( HlsAttributeVariant(kHlsAttributeList, "", "", true), HlsAttributeVariant(kHlsAttributeListKeyFormatUnknown, HLS_KEYFORMAT_ATTRIBUTE, kHlsKeyFormatValueOther, false), HlsAttributeVariant(kHlsAttributeListKeyFormatVersionUnsupported, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, "2", false), HlsAttributeVariant(kHlsAttributeListMethodAes128, HLS_METHOD_ATTRIBUTE, HLS_METHOD_AES_128, true), HlsAttributeVariant(kHlsAttributeListMethodNone, HLS_METHOD_ATTRIBUTE, HLS_METHOD_NONE, false), HlsAttributeVariant(kHlsAttributeListKeyFormatVersionMultiple, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, HLS_KEYFORMAT_VERSION_VALUE_1, true), HlsAttributeVariant(kHlsAttributeListMethodInvalid, HLS_METHOD_ATTRIBUTE, kHlsTestValue1, false), HlsAttributeVariant(kHlsAttributeListInvalidUriNoProvider, kJsonProvider, kHlsTestProvider, false), HlsAttributeVariant(kHlsAttributeListInvalidUriNoContentId, kJsonContentId, kHlsTestContentId, false), HlsAttributeVariant(kHlsAttributeListInvalidUriNoKeyId, kJsonKeyIds, kHlsTestKeyId1, false), HlsAttributeVariant(kHlsAttributeListValidUriThreeKeyIds, kJsonKeyIds, kHlsTestKeyId1, true), HlsAttributeVariant(kHlsAttributeListNoIv, HLS_IV_ATTRIBUTE, kHlsTestNoHexValue, false), HlsAttributeVariant(kHlsAttributeListInvalidIv, HLS_IV_ATTRIBUTE, kHlsTestHexValueWithOddBytes, false))); } // namespace wvcdm