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) {