// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/cdm/cenc_utils.h" #include #include #include "base/logging.h" #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { const uint8_t kKey1Data[] = { 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03 }; const uint8_t kKey2Data[] = { 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, }; const uint8_t kKey3Data[] = { 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x05, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x05, }; const uint8_t kKey4Data[] = { 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06, }; const uint8_t kCommonSystemSystemId[] = { 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B }; class CencUtilsTest : public testing::Test { public: CencUtilsTest() : key1_(kKey1Data, kKey1Data + arraysize(kKey1Data)), key2_(kKey2Data, kKey2Data + arraysize(kKey2Data)), key3_(kKey3Data, kKey3Data + arraysize(kKey3Data)), key4_(kKey4Data, kKey4Data + arraysize(kKey4Data)), common_system_system_id_( kCommonSystemSystemId, kCommonSystemSystemId + arraysize(kCommonSystemSystemId)) {} protected: // Initialize the start of the 'pssh' box (up to key_count) void InitializePSSHBox(std::vector* box, uint8_t size, uint8_t version) { DCHECK(box->size() == 0); box->reserve(size); // Add size. box->push_back(0); box->push_back(0); box->push_back(0); box->push_back(size); // Add 'pssh'. box->push_back('p'); box->push_back('s'); box->push_back('s'); box->push_back('h'); // Add version. box->push_back(version); // Add flags. box->push_back(0); box->push_back(0); box->push_back(0); // Add Common Encryption SystemID. box->insert(box->end(), common_system_system_id_.begin(), common_system_system_id_.end()); } std::vector MakePSSHBox(uint8_t version) { std::vector box; uint8_t size = (version == 0) ? 32 : 36; InitializePSSHBox(&box, size, version); if (version > 0) { // Add key_count (= 0). box.push_back(0); box.push_back(0); box.push_back(0); box.push_back(0); } // Add data_size (= 0). box.push_back(0); box.push_back(0); box.push_back(0); box.push_back(0); return box; } std::vector MakePSSHBox(uint8_t version, const std::vector& key1) { DCHECK(version > 0); DCHECK(key1.size() == 16); std::vector box; uint8_t size = 52; InitializePSSHBox(&box, size, version); // Add key_count (= 1). box.push_back(0); box.push_back(0); box.push_back(0); box.push_back(1); // Add key1. for (size_t i = 0; i < key1.size(); ++i) box.push_back(key1[i]); // Add data_size (= 0). box.push_back(0); box.push_back(0); box.push_back(0); box.push_back(0); return box; } std::vector MakePSSHBox(uint8_t version, const std::vector& key1, const std::vector& key2) { DCHECK(version > 0); DCHECK(key1.size() == 16); DCHECK(key2.size() == 16); std::vector box; uint8_t size = 68; InitializePSSHBox(&box, size, version); // Add key_count (= 2). box.push_back(0); box.push_back(0); box.push_back(0); box.push_back(2); // Add key1. for (size_t i = 0; i < key1.size(); ++i) box.push_back(key1[i]); // Add key2. for (size_t i = 0; i < key2.size(); ++i) box.push_back(key2[i]); // Add data_size (= 0). box.push_back(0); box.push_back(0); box.push_back(0); box.push_back(0); return box; } void AppendData(std::vector& pssh_box, const std::vector& data) { // This assumes that |pssh_box| has been created using the routines above, // and simply appends the data to the end of it. It updates the box size // and sets the data size. DCHECK(data.size() < 100); pssh_box[3] += static_cast(data.size()); pssh_box.pop_back(); pssh_box.push_back(static_cast(data.size())); pssh_box.insert(pssh_box.end(), data.begin(), data.end()); } const std::vector& Key1() { return key1_; } const std::vector& Key2() { return key2_; } const std::vector& Key3() { return key3_; } const std::vector& Key4() { return key4_; } const std::vector& CommonSystemSystemId() { return common_system_system_id_; } private: std::vector key1_; std::vector key2_; std::vector key3_; std::vector key4_; std::vector common_system_system_id_; }; TEST_F(CencUtilsTest, EmptyPSSH) { KeyIdList key_ids; EXPECT_TRUE(ValidatePsshInput(std::vector())); EXPECT_FALSE(GetKeyIdsForCommonSystemId(std::vector(), &key_ids)); } TEST_F(CencUtilsTest, PSSHVersion0) { std::vector box = MakePSSHBox(0); KeyIdList key_ids; EXPECT_TRUE(ValidatePsshInput(box)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(box, &key_ids)); } TEST_F(CencUtilsTest, PSSHVersion1WithNoKeys) { std::vector box = MakePSSHBox(1); KeyIdList key_ids; EXPECT_TRUE(ValidatePsshInput(box)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(box, &key_ids)); } TEST_F(CencUtilsTest, PSSHVersion1WithOneKey) { std::vector box = MakePSSHBox(1, Key1()); KeyIdList key_ids; EXPECT_TRUE(ValidatePsshInput(box)); EXPECT_TRUE(GetKeyIdsForCommonSystemId(box, &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); } TEST_F(CencUtilsTest, PSSHVersion1WithTwoKeys) { std::vector box = MakePSSHBox(1, Key1(), Key2()); KeyIdList key_ids; EXPECT_TRUE(ValidatePsshInput(box)); EXPECT_TRUE(GetKeyIdsForCommonSystemId(box, &key_ids)); EXPECT_EQ(2u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); EXPECT_EQ(key_ids[1], Key2()); } TEST_F(CencUtilsTest, PSSHVersion0Plus1) { std::vector box0 = MakePSSHBox(0); std::vector box1 = MakePSSHBox(1, Key1()); // Concatentate box1 onto end of box0. box0.insert(box0.end(), box1.begin(), box1.end()); EXPECT_TRUE(ValidatePsshInput(box0)); // No key IDs returned as only the first 'pssh' box is processed. KeyIdList key_ids; EXPECT_FALSE(GetKeyIdsForCommonSystemId(box0, &key_ids)); } TEST_F(CencUtilsTest, PSSHVersion1Plus0) { std::vector box0 = MakePSSHBox(0); std::vector box1 = MakePSSHBox(1, Key1()); // Concatentate box0 onto end of box1. box1.insert(box1.end(), box0.begin(), box0.end()); KeyIdList key_ids; EXPECT_TRUE(ValidatePsshInput(box1)); EXPECT_TRUE(GetKeyIdsForCommonSystemId(box1, &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); } TEST_F(CencUtilsTest, MultiplePSSHVersion1) { std::vector box = MakePSSHBox(1, Key1(), Key2()); std::vector box1 = MakePSSHBox(1, Key3()); std::vector box2 = MakePSSHBox(1, Key4()); // Concatentate box1 and box2 onto end of box. box.insert(box.end(), box1.begin(), box1.end()); box.insert(box.end(), box2.begin(), box2.end()); KeyIdList key_ids; EXPECT_TRUE(ValidatePsshInput(box)); EXPECT_TRUE(GetKeyIdsForCommonSystemId(box, &key_ids)); EXPECT_EQ(2u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); EXPECT_EQ(key_ids[1], Key2()); } TEST_F(CencUtilsTest, PsshBoxSmallerThanSize) { std::vector box = MakePSSHBox(1, Key1(), Key2()); KeyIdList key_ids; // Tries every buffer size less than the indicated 'pssh' box size. for (size_t i = 1; i < box.size(); ++i) { // Truncate the box to be less than the specified box size. std::vector truncated(&box[0], &box[0] + i); EXPECT_FALSE(ValidatePsshInput(truncated)) << "Failed for length " << i; EXPECT_FALSE(GetKeyIdsForCommonSystemId(truncated, &key_ids)); } } TEST_F(CencUtilsTest, PsshBoxLargerThanSize) { std::vector box = MakePSSHBox(1, Key1(), Key2()); KeyIdList key_ids; // Add 20 additional bytes to |box|. size_t original_size = box.size(); for (size_t i = 0; i < 20; ++i) box.push_back(static_cast(i)); // Tries every size greater than |original_size|. for (size_t i = original_size + 1; i < box.size(); ++i) { // Modify size of box passed to be less than current size. std::vector truncated(&box[0], &box[0] + i); EXPECT_FALSE(ValidatePsshInput(truncated)) << "Failed for length " << i; EXPECT_FALSE(GetKeyIdsForCommonSystemId(truncated, &key_ids)); } } TEST_F(CencUtilsTest, UnrecognizedSystemID) { std::vector box = MakePSSHBox(1, Key1(), Key2()); // Modify the System ID. ++box[20]; KeyIdList key_ids; EXPECT_FALSE(GetKeyIdsForCommonSystemId(box, &key_ids)); } TEST_F(CencUtilsTest, InvalidFlags) { std::vector box = MakePSSHBox(1, Key1(), Key2()); // Modify flags. box[10] = 3; KeyIdList key_ids; EXPECT_FALSE(GetKeyIdsForCommonSystemId(box, &key_ids)); } TEST_F(CencUtilsTest, LongSize) { const uint8_t data[] = { 0x00, 0x00, 0x00, 0x01, // size = 1 0x70, 0x73, 0x73, 0x68, // 'pssh' 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, // longsize 0x01, // version 0x00, 0x00, 0x00, // flags 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // SystemID 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B, 0x00, 0x00, 0x00, 0x02, // key count 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, // key1 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, // key2 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, 0x00, 0x00, 0x00, 0x00 // datasize }; KeyIdList key_ids; EXPECT_TRUE( ValidatePsshInput(std::vector(data, data + arraysize(data)))); EXPECT_TRUE(GetKeyIdsForCommonSystemId( std::vector(data, data + arraysize(data)), &key_ids)); EXPECT_EQ(2u, key_ids.size()); } TEST_F(CencUtilsTest, SizeIsZero) { const uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, // size = 0 0x70, 0x73, 0x73, 0x68, // 'pssh' 0x01, // version 0x00, 0x00, 0x00, // flags 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // SystemID 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B, 0x00, 0x00, 0x00, 0x02, // key count 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, // key1 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, // key2 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, 0x00, 0x00, 0x00, 0x00 // datasize }; KeyIdList key_ids; EXPECT_TRUE( ValidatePsshInput(std::vector(data, data + arraysize(data)))); EXPECT_TRUE(GetKeyIdsForCommonSystemId( std::vector(data, data + arraysize(data)), &key_ids)); EXPECT_EQ(2u, key_ids.size()); } TEST_F(CencUtilsTest, HugeSize) { const uint8_t data[] = { 0x00, 0x00, 0x00, 0x01, // size = 1 0x70, 0x73, 0x73, 0x68, // 'pssh' 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // longsize = big 0x01, // version 0x00, 0x00, 0x00, // flags 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // SystemID 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B, 0x00, 0x00, 0x00, 0x02, // key count 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, // key1 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x03, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, // key2 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x04, 0x00, 0x00, 0x00, 0x00 // datasize }; KeyIdList key_ids; // These calls fail as the box size is huge (0xffffffffffffffff) and there // is not enough bytes in |data|. EXPECT_FALSE( ValidatePsshInput(std::vector(data, data + arraysize(data)))); EXPECT_FALSE(GetKeyIdsForCommonSystemId( std::vector(data, data + arraysize(data)), &key_ids)); } TEST_F(CencUtilsTest, GetPsshData_Version0) { const uint8_t data_bytes[] = {0x01, 0x02, 0x03, 0x04}; std::vector pssh_data; std::vector box = MakePSSHBox(0); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); EXPECT_EQ(0u, pssh_data.size()); std::vector data(data_bytes, data_bytes + arraysize(data_bytes)); AppendData(box, data); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); EXPECT_EQ(data, pssh_data); } TEST_F(CencUtilsTest, GetPsshData_Version1NoKeys) { const uint8_t data_bytes[] = {0x05, 0x06, 0x07, 0x08}; std::vector pssh_data; std::vector box = MakePSSHBox(1); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); EXPECT_EQ(0u, pssh_data.size()); std::vector data(data_bytes, data_bytes + arraysize(data_bytes)); AppendData(box, data); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); EXPECT_EQ(data, pssh_data); } TEST_F(CencUtilsTest, GetPsshData_Version1WithKeys) { const uint8_t data_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; std::vector pssh_data; std::vector box = MakePSSHBox(1, Key1()); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); EXPECT_EQ(0u, pssh_data.size()); std::vector data(data_bytes, data_bytes + arraysize(data_bytes)); AppendData(box, data); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); EXPECT_EQ(data, pssh_data); } TEST_F(CencUtilsTest, GetPsshData_Version2) { std::vector pssh_data; std::vector box = MakePSSHBox(1, Key1()); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); // Change the version manually, since we don't know what v2 will contain. box[8] = 2; EXPECT_FALSE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); } TEST_F(CencUtilsTest, GetPsshData_Version2ThenVersion1) { std::vector pssh_data; std::vector box_v1 = MakePSSHBox(1, Key1()); std::vector box_v2 = MakePSSHBox(2, Key2(), Key3()); // Concatentate the boxes together (v2 first). std::vector boxes; boxes.insert(boxes.end(), box_v2.begin(), box_v2.end()); boxes.insert(boxes.end(), box_v1.begin(), box_v1.end()); EXPECT_TRUE(GetPsshData(boxes, CommonSystemSystemId(), &pssh_data)); // GetKeyIdsForCommonSystemId() should return the single key from the v1 // 'pssh' box. KeyIdList key_ids; EXPECT_TRUE(GetKeyIdsForCommonSystemId(boxes, &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); } TEST_F(CencUtilsTest, GetPsshData_Version1ThenVersion2) { std::vector pssh_data; std::vector box_v1 = MakePSSHBox(1, Key3()); std::vector box_v2 = MakePSSHBox(2, Key4()); // Concatentate the boxes together (v1 first). std::vector boxes; boxes.insert(boxes.end(), box_v1.begin(), box_v1.end()); boxes.insert(boxes.end(), box_v2.begin(), box_v2.end()); EXPECT_TRUE(GetPsshData(boxes, CommonSystemSystemId(), &pssh_data)); // GetKeyIdsForCommonSystemId() should return the single key from the v1 // 'pssh' box. KeyIdList key_ids; EXPECT_TRUE(GetKeyIdsForCommonSystemId(boxes, &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key3()); } TEST_F(CencUtilsTest, GetPsshData_DifferentSystemID) { std::vector unknown_system_id(kKey1Data, kKey1Data + arraysize(kKey1Data)); std::vector pssh_data; std::vector box = MakePSSHBox(1, Key1()); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); EXPECT_FALSE(GetPsshData(box, unknown_system_id, &pssh_data)); } TEST_F(CencUtilsTest, GetPsshData_MissingData) { const uint8_t data_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; std::vector pssh_data; std::vector box = MakePSSHBox(1, Key1()); std::vector data(data_bytes, data_bytes + arraysize(data_bytes)); AppendData(box, data); EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); // Remove some data from the end, so now the size is incorrect. box.pop_back(); box.pop_back(); EXPECT_FALSE(GetPsshData(box, CommonSystemSystemId(), &pssh_data)); } TEST_F(CencUtilsTest, GetPsshData_MultiplePssh) { const uint8_t data1_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; const uint8_t data2_bytes[] = {0xa1, 0xa2, 0xa3, 0xa4}; std::vector pssh_data; std::vector box1 = MakePSSHBox(1, Key1()); std::vector data1(data1_bytes, data1_bytes + arraysize(data1_bytes)); AppendData(box1, data1); std::vector box2 = MakePSSHBox(0); std::vector data2(data2_bytes, data2_bytes + arraysize(data2_bytes)); AppendData(box2, data2); box1.insert(box1.end(), box2.begin(), box2.end()); EXPECT_TRUE(GetPsshData(box1, CommonSystemSystemId(), &pssh_data)); EXPECT_EQ(data1, pssh_data); EXPECT_NE(data2, pssh_data); } TEST_F(CencUtilsTest, NonPsshData) { // Create a non-'pssh' box. const uint8_t data[] = { 0x00, 0x00, 0x00, 0x08, // size = 8 'p', 's', 's', 'g' }; std::vector non_pssh_box(data, data + arraysize(data)); EXPECT_FALSE(ValidatePsshInput(non_pssh_box)); // Make a valid 'pssh' box. std::vector pssh_box = MakePSSHBox(1, Key1()); EXPECT_TRUE(ValidatePsshInput(pssh_box)); // Concatentate the boxes together (|pssh_box| first). std::vector boxes; boxes.insert(boxes.end(), pssh_box.begin(), pssh_box.end()); boxes.insert(boxes.end(), non_pssh_box.begin(), non_pssh_box.end()); EXPECT_FALSE(ValidatePsshInput(boxes)); // Repeat with |non_pssh_box| first. boxes.clear(); boxes.insert(boxes.end(), non_pssh_box.begin(), non_pssh_box.end()); boxes.insert(boxes.end(), pssh_box.begin(), pssh_box.end()); EXPECT_FALSE(ValidatePsshInput(boxes)); } } // namespace media