diff options
author | jrummell <jrummell@chromium.org> | 2015-04-22 11:25:20 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-22 18:26:30 +0000 |
commit | 15426e1f537cb15a28396c6ba70644fd90605218 (patch) | |
tree | 223aa201301f23eece3ad2cec2121ae95201d10c | |
parent | ab42594ec3070953971f43b384f38f3c2b65d4ea (diff) | |
download | chromium_src-15426e1f537cb15a28396c6ba70644fd90605218.zip chromium_src-15426e1f537cb15a28396c6ba70644fd90605218.tar.gz chromium_src-15426e1f537cb15a28396c6ba70644fd90605218.tar.bz2 |
Sanitize init_data before passing it on
BUG=456397
TEST=media_unittests and EME layout tests pass
Review URL: https://codereview.chromium.org/1096203004
Cr-Commit-Position: refs/heads/master@{#326342}
-rw-r--r-- | content/browser/media/cdm/browser_cdm_manager.cc | 13 | ||||
-rw-r--r-- | media/base/limits.h | 3 | ||||
-rw-r--r-- | media/blink/webcontentdecryptionmodulesession_impl.cc | 110 | ||||
-rw-r--r-- | media/blink/webcontentdecryptionmodulesession_impl.h | 6 | ||||
-rw-r--r-- | media/cdm/cenc_utils.cc | 71 | ||||
-rw-r--r-- | media/cdm/cenc_utils.h | 4 | ||||
-rw-r--r-- | media/cdm/cenc_utils_unittest.cc | 13 | ||||
-rw-r--r-- | media/cdm/json_web_key.cc | 19 | ||||
-rw-r--r-- | media/cdm/json_web_key.h | 5 | ||||
-rw-r--r-- | media/cdm/json_web_key_unittest.cc | 29 |
10 files changed, 220 insertions, 53 deletions
diff --git a/content/browser/media/cdm/browser_cdm_manager.cc b/content/browser/media/cdm/browser_cdm_manager.cc index 3eb55f1..88cf526 100644 --- a/content/browser/media/cdm/browser_cdm_manager.cc +++ b/content/browser/media/cdm/browser_cdm_manager.cc @@ -36,13 +36,6 @@ using media::MediaKeys; namespace { -// Maximum lengths for various EME API parameters. These are checks to -// prevent unnecessarily large parameters from being passed around, and the -// lengths are somewhat arbitrary as the EME spec doesn't specify any limits. -const size_t kMaxInitDataLength = 64 * 1024; // 64 KB -const size_t kMaxSessionResponseLength = 64 * 1024; // 64 KB -const size_t kMaxKeySystemLength = 256; - // The ID used in this class is a concatenation of |render_frame_id| and // |cdm_id|, i.e. (render_frame_id << 32) + cdm_id. @@ -300,7 +293,7 @@ void BrowserCdmManager::OnInitializeCdm(int render_frame_id, int cdm_id, const std::string& key_system, const GURL& security_origin) { - if (key_system.size() > kMaxKeySystemLength) { + if (key_system.size() > media::limits::kMaxKeySystemLength) { // This failure will be discovered and reported by OnCreateSession() // as GetCdm() will return null. NOTREACHED() << "Invalid key system: " << key_system; @@ -346,7 +339,7 @@ void BrowserCdmManager::OnCreateSessionAndGenerateRequest( scoped_ptr<NewSessionPromise> promise( new NewSessionPromise(this, render_frame_id, cdm_id, promise_id)); - if (init_data.size() > kMaxInitDataLength) { + if (init_data.size() > media::limits::kMaxInitDataLength) { LOG(WARNING) << "InitData for ID: " << cdm_id << " too long: " << init_data.size(); promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Init data too long."); @@ -400,7 +393,7 @@ void BrowserCdmManager::OnUpdateSession(int render_frame_id, return; } - if (response.size() > kMaxSessionResponseLength) { + if (response.size() > media::limits::kMaxSessionResponseLength) { LOG(WARNING) << "Response for ID " << cdm_id << " is too long: " << response.size(); promise->reject(MediaKeys::INVALID_ACCESS_ERROR, 0, "Response too long."); diff --git a/media/base/limits.h b/media/base/limits.h index 69a5b63..c3cd677 100644 --- a/media/base/limits.h +++ b/media/base/limits.h @@ -52,6 +52,9 @@ enum { kMinKeyIdLength = 1, kMaxKeyIdLength = 512, kMaxKeyIds = 128, + kMaxInitDataLength = 64 * 1024, // 64 KB + kMaxSessionResponseLength = 64 * 1024, // 64 KB + kMaxKeySystemLength = 256, }; } // namespace limits diff --git a/media/blink/webcontentdecryptionmodulesession_impl.cc b/media/blink/webcontentdecryptionmodulesession_impl.cc index 643aa94..94afa0e 100644 --- a/media/blink/webcontentdecryptionmodulesession_impl.cc +++ b/media/blink/webcontentdecryptionmodulesession_impl.cc @@ -13,11 +13,14 @@ #include "media/base/cdm_key_information.h" #include "media/base/cdm_promise.h" #include "media/base/key_systems.h" +#include "media/base/limits.h" #include "media/base/media_keys.h" #include "media/blink/cdm_result_promise.h" #include "media/blink/cdm_session_adapter.h" #include "media/blink/new_session_cdm_result_promise.h" #include "media/blink/webmediaplayer_util.h" +#include "media/cdm/cenc_utils.h" +#include "media/cdm/json_web_key.h" #include "third_party/WebKit/public/platform/WebData.h" #include "third_party/WebKit/public/platform/WebEncryptedMediaKeyInformation.h" #include "third_party/WebKit/public/platform/WebString.h" @@ -91,6 +94,60 @@ static MediaKeys::SessionType convertSessionType( return MediaKeys::TEMPORARY_SESSION; } +static bool SanitizeInitData(EmeInitDataType init_data_type, + const unsigned char* init_data, + size_t init_data_length, + std::vector<uint8>* sanitized_init_data, + std::string* error_message) { + if (init_data_length > limits::kMaxInitDataLength) { + error_message->assign("Initialization data too long."); + return false; + } + + switch (init_data_type) { + case EmeInitDataType::WEBM: + sanitized_init_data->assign(init_data, init_data + init_data_length); + return true; + + case EmeInitDataType::CENC: + if (!ValidatePsshInput(init_data, init_data_length)) { + error_message->assign("Initialization data for CENC is incorrect."); + return false; + } + + sanitized_init_data->assign(init_data, init_data + init_data_length); + return true; + + case EmeInitDataType::KEYIDS: { + // Extract the keys and then rebuild the message. This ensures that any + // extra data in the provided JSON is dropped. + std::string init_data_string(init_data, init_data + init_data_length); + KeyIdList key_ids; + if (!ExtractKeyIdsFromKeyIdsInitData(init_data_string, &key_ids, + error_message)) + return false; + + for (const auto& key_id : key_ids) { + if (key_id.size() < limits::kMinKeyIdLength || + key_id.size() > limits::kMaxKeyIdLength) { + error_message->assign("Incorrect key size."); + return false; + } + } + + CreateKeyIdsInitData(key_ids, sanitized_init_data); + return true; + } + + case EmeInitDataType::UNKNOWN: + break; + } + + NOTREACHED(); + error_message->assign("Initialization data type is not supported."); + return false; +} + WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl( const scoped_refptr<CdmSessionAdapter>& adapter) : adapter_(adapter), is_closed_(false), weak_ptr_factory_(this) { @@ -118,7 +175,7 @@ void WebContentDecryptionModuleSessionImpl::initializeNewSession( blink::WebContentDecryptionModuleResult result) { DCHECK(session_id_.empty()); - // Step 5 from https://w3c.github.io/encrypted-media/#generateRequest. + // From https://w3c.github.io/encrypted-media/#generateRequest. // 5. If the Key System implementation represented by this object's cdm // implementation value does not support initDataType as an Initialization // Data Type, return a promise rejected with a new DOMException whose name @@ -134,9 +191,42 @@ void WebContentDecryptionModuleSessionImpl::initializeNewSession( return; } + // 9.1 If the init data is not valid for initDataType, reject promise with a + // new DOMException whose name is InvalidAccessError. + // 9.2 Let sanitized init data be a validated and sanitized version of init + // data. The user agent must thoroughly validate the Initialization Data + // before passing it to the CDM. This includes verifying that the length + // and values of fields are reasonable, verifying that values are within + // reasonable limits, and stripping irrelevant, unsupported, or unknown + // data or fields. It is recommended that user agents pre-parse, sanitize, + // and/or generate a fully sanitized version of the Initialization Data. + // If the Initialization Data format specified by initDataType support + // multiple entries, the user agent should remove entries that are not + // needed by the CDM. + // 9.3 If the previous step failed, reject promise with a new DOMException + // whose name is InvalidAccessError. + std::vector<uint8> sanitized_init_data; + std::string message; + if (!SanitizeInitData(eme_init_data_type, init_data, init_data_length, + &sanitized_init_data, &message)) { + result.completeWithError( + blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0, + blink::WebString::fromUTF8(message)); + return; + } + + // 9.4 Let session id be the empty string. + // (Done in constructor.) + + // 9.5 Let message be null. + // (Done by CDM.) + + // 9.6 Let cdm be the CDM instance represented by this object's cdm + // instance value. + // 9.7 Use the cdm to execute the following steps: adapter_->InitializeNewSession( - eme_init_data_type, init_data, - base::saturated_cast<int>(init_data_length), + eme_init_data_type, vector_as_array(&sanitized_init_data), + base::saturated_cast<int>(sanitized_init_data.size()), convertSessionType(session_type), scoped_ptr<NewSessionCdmPromise>(new NewSessionCdmResultPromise( result, adapter_->GetKeySystemUMAPrefix() + kGenerateRequestUMAName, @@ -145,16 +235,6 @@ void WebContentDecryptionModuleSessionImpl::initializeNewSession( base::Unretained(this))))); } -// TODO(jrummell): Remove this. http://crbug.com/418239. -void WebContentDecryptionModuleSessionImpl::initializeNewSession( - const blink::WebString& init_data_type, - const uint8* init_data, - size_t init_data_length, - const blink::WebString& session_type, - blink::WebContentDecryptionModuleResult result) { - NOTREACHED(); -} - void WebContentDecryptionModuleSessionImpl::load( const blink::WebString& session_id, blink::WebContentDecryptionModuleResult result) { @@ -207,8 +287,8 @@ void WebContentDecryptionModuleSessionImpl::OnSessionMessage( MediaKeys::MessageType message_type, const std::vector<uint8>& message) { DCHECK(client_) << "Client not set before message event"; - client_->message(convertMessageType(message_type), - message.empty() ? NULL : &message[0], message.size()); + client_->message(convertMessageType(message_type), vector_as_array(&message), + message.size()); } void WebContentDecryptionModuleSessionImpl::OnSessionKeysChange( diff --git a/media/blink/webcontentdecryptionmodulesession_impl.h b/media/blink/webcontentdecryptionmodulesession_impl.h index 881c07e..2f00ff5 100644 --- a/media/blink/webcontentdecryptionmodulesession_impl.h +++ b/media/blink/webcontentdecryptionmodulesession_impl.h @@ -38,12 +38,6 @@ class WebContentDecryptionModuleSessionImpl size_t initDataLength, blink::WebEncryptedMediaSessionType session_type, blink::WebContentDecryptionModuleResult result); - virtual void initializeNewSession( - const blink::WebString& init_data_type, - const uint8* init_data, - size_t init_data_length, - const blink::WebString& session_type, - blink::WebContentDecryptionModuleResult result); virtual void load(const blink::WebString& session_id, blink::WebContentDecryptionModuleResult result); virtual void update(const uint8* response, diff --git a/media/cdm/cenc_utils.cc b/media/cdm/cenc_utils.cc index 2eeafd7..a59c19c 100644 --- a/media/cdm/cenc_utils.cc +++ b/media/cdm/cenc_utils.cc @@ -70,6 +70,52 @@ static bool IsCommonSystemID(BitReader* reader) { return true; } +// Checks that |reader| contains a valid 'ppsh' box header. |reader| is updated +// to point to the content immediately following the box header. Returns true +// if the header looks valid and |reader| contains enough data for the size of +// header. |size| is updated as the computed size of the box header. Otherwise +// false is returned. +static bool ValidBoxHeader(BitReader* reader, uint32* size) { + // Enough data for a miniumum size 'pssh' box? + uint32 available_bytes = reader->bits_available() / 8; + RCHECK(available_bytes >= kMinimumBoxSizeInBytes); + + *size = ReadBits(reader, 32); + + // Must be a 'pssh' box or else fail. + RCHECK(ReadBits(reader, 8) == 'p'); + RCHECK(ReadBits(reader, 8) == 's'); + RCHECK(ReadBits(reader, 8) == 's'); + RCHECK(ReadBits(reader, 8) == 'h'); + + if (*size == 1) { + // If largesize > 2**32 it is too big. + RCHECK(ReadBits(reader, 32) == 0); + *size = ReadBits(reader, 32); + } else if (*size == 0) { + *size = available_bytes; + } + + // Check that the buffer contains at least size bytes. + return available_bytes >= *size; +} + +bool ValidatePsshInput(const uint8* input, size_t input_length) { + size_t offset = 0; + while (offset < input_length) { + // Create a BitReader over the remaining part of the buffer. + BitReader reader(input + offset, input_length - offset); + uint32 size; + RCHECK(ValidBoxHeader(&reader, &size)); + + // Update offset to point at the next 'pssh' box (may not be one). + offset += size; + } + + // Only valid if this contains 0 or more 'pssh' boxes. + return offset == input_length; +} + bool GetKeyIdsForCommonSystemId(const uint8* input, int input_length, std::vector<std::vector<uint8>>* key_ids) { @@ -77,29 +123,10 @@ bool GetKeyIdsForCommonSystemId(const uint8* input, std::vector<std::vector<uint8>> result; while (offset < input_length) { + // Create a BitReader over the remaining part of the buffer. BitReader reader(input + offset, input_length - offset); - - // Enough data for a miniumum size 'pssh' box? - RCHECK(reader.bits_available() >= kMinimumBoxSizeInBytes * 8); - - uint32 size = ReadBits(&reader, 32); - - // Must be a 'pssh' box or else fail. - RCHECK(ReadBits(&reader, 8) == 'p'); - RCHECK(ReadBits(&reader, 8) == 's'); - RCHECK(ReadBits(&reader, 8) == 's'); - RCHECK(ReadBits(&reader, 8) == 'h'); - - if (size == 1) { - // If largesize > 2**32 it is too big. - RCHECK(ReadBits(&reader, 32) == 0); - size = ReadBits(&reader, 32); - } else if (size == 0) { - size = input_length - offset; - } - - // Check that the buffer contains at least size bytes. - RCHECK(static_cast<uint32>(input_length - offset) >= size); + uint32 size; + RCHECK(ValidBoxHeader(&reader, &size)); // Update offset to point at the next 'pssh' box (may not be one). offset += size; diff --git a/media/cdm/cenc_utils.h b/media/cdm/cenc_utils.h index c659742..6fd9952 100644 --- a/media/cdm/cenc_utils.h +++ b/media/cdm/cenc_utils.h @@ -12,6 +12,10 @@ namespace media { +// Validate that |input| is a set of concatenated 'pssh' boxes and the sizes +// match. Returns true if |input| looks valid, false otherwise. +MEDIA_EXPORT bool ValidatePsshInput(const uint8* input, size_t input_length); + // Gets the Key Ids from a 'pssh' box for the Common SystemID among one or // more concatenated 'pssh' boxes. If |input| looks valid, then true is // returned and |key_ids| is updated to contain the values found. Otherwise diff --git a/media/cdm/cenc_utils_unittest.cc b/media/cdm/cenc_utils_unittest.cc index ae75817..fd481da 100644 --- a/media/cdm/cenc_utils_unittest.cc +++ b/media/cdm/cenc_utils_unittest.cc @@ -168,6 +168,7 @@ class CencUtilsTest : public testing::Test { TEST_F(CencUtilsTest, EmptyPSSH) { std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(nullptr, 0)); EXPECT_TRUE(GetKeyIdsForCommonSystemId(nullptr, 0, &key_ids)); EXPECT_EQ(0u, key_ids.size()); } @@ -175,6 +176,7 @@ TEST_F(CencUtilsTest, EmptyPSSH) { TEST_F(CencUtilsTest, PSSHVersion0) { std::vector<uint8> box = MakePSSHBox(0); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(0u, key_ids.size()); } @@ -182,6 +184,7 @@ TEST_F(CencUtilsTest, PSSHVersion0) { TEST_F(CencUtilsTest, PSSHVersion1WithNoKeys) { std::vector<uint8> box = MakePSSHBox(1); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(0u, key_ids.size()); } @@ -189,6 +192,7 @@ TEST_F(CencUtilsTest, PSSHVersion1WithNoKeys) { TEST_F(CencUtilsTest, PSSHVersion1WithOneKey) { std::vector<uint8> box = MakePSSHBox(1, Key1()); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -197,6 +201,7 @@ TEST_F(CencUtilsTest, PSSHVersion1WithOneKey) { TEST_F(CencUtilsTest, PSSHVersion1WithTwoKeys) { std::vector<uint8> box = MakePSSHBox(1, Key1(), Key2()); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(2u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -212,6 +217,7 @@ TEST_F(CencUtilsTest, PSSHVersion0Plus1) { box0.push_back(value); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box0[0], box0.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box0[0], box0.size(), &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -226,6 +232,7 @@ TEST_F(CencUtilsTest, PSSHVersion1Plus0) { box1.push_back(value); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box1[0], box1.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box1[0], box1.size(), &key_ids)); EXPECT_EQ(1u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -244,6 +251,7 @@ TEST_F(CencUtilsTest, MultiplePSSHVersion1) { box.push_back(value); std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(&box[0], box.size())); EXPECT_TRUE(GetKeyIdsForCommonSystemId(&box[0], box.size(), &key_ids)); EXPECT_EQ(4u, key_ids.size()); EXPECT_EQ(key_ids[0], Key1()); @@ -257,8 +265,10 @@ TEST_F(CencUtilsTest, InvalidPSSH) { std::vector<std::vector<uint8>> key_ids; for (uint32 i = 1; i < box.size(); ++i) { // Modify size of data passed to be less than real size. + EXPECT_FALSE(ValidatePsshInput(&box[0], i)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(&box[0], i, &key_ids)); // Modify starting point. + EXPECT_FALSE(ValidatePsshInput(&box[i], box.size() - i)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(&box[i], box.size() - i, &key_ids)); } } @@ -304,6 +314,7 @@ TEST_F(CencUtilsTest, LongSize) { }; std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(data, arraysize(data))); EXPECT_TRUE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids)); EXPECT_EQ(2u, key_ids.size()); } @@ -325,6 +336,7 @@ TEST_F(CencUtilsTest, NoSize) { }; std::vector<std::vector<uint8>> key_ids; + EXPECT_TRUE(ValidatePsshInput(data, arraysize(data))); EXPECT_TRUE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids)); EXPECT_EQ(2u, key_ids.size()); } @@ -347,6 +359,7 @@ TEST_F(CencUtilsTest, HugeSize) { }; std::vector<std::vector<uint8>> key_ids; + EXPECT_FALSE(ValidatePsshInput(data, arraysize(data))); EXPECT_FALSE(GetKeyIdsForCommonSystemId(data, arraysize(data), &key_ids)); } diff --git a/media/cdm/json_web_key.cc b/media/cdm/json_web_key.cc index 43533f0..f60c6f5 100644 --- a/media/cdm/json_web_key.cc +++ b/media/cdm/json_web_key.cc @@ -329,6 +329,25 @@ void CreateLicenseRequest(const KeyIdList& key_ids, license->swap(result); } +void CreateKeyIdsInitData(const KeyIdList& key_ids, + std::vector<uint8>* init_data) { + // Create the init_data. + scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue()); + scoped_ptr<base::ListValue> list(new base::ListValue()); + for (const auto& key_id : key_ids) + list->AppendString(EncodeBase64Url(&key_id[0], key_id.size())); + dictionary->Set(kKeyIdsTag, list.release()); + + // Serialize the dictionary as a string. + std::string json; + JSONStringValueSerializer serializer(&json); + serializer.Serialize(*dictionary); + + // Convert the serialized data into std::vector and return it. + std::vector<uint8> result(json.begin(), json.end()); + init_data->swap(result); +} + bool ExtractFirstKeyIdFromLicenseRequest(const std::vector<uint8>& license, std::vector<uint8>* first_key) { const std::string license_as_str( diff --git a/media/cdm/json_web_key.h b/media/cdm/json_web_key.h index 7be4473..c77a814 100644 --- a/media/cdm/json_web_key.h +++ b/media/cdm/json_web_key.h @@ -81,6 +81,11 @@ MEDIA_EXPORT void CreateLicenseRequest(const KeyIdList& key_ids, MediaKeys::SessionType session_type, std::vector<uint8>* license); +// Creates a keyIDs init_data message for the |key_ids| specified. +// |key_ids_init_data| is updated to contain the resulting JSON string. +MEDIA_EXPORT void CreateKeyIdsInitData(const KeyIdList& key_ids, + std::vector<uint8>* key_ids_init_data); + // Extract the first key from the license request message. Returns true if // |license| is a valid license request and contains at least one key, // otherwise false and |first_key| is not touched. diff --git a/media/cdm/json_web_key_unittest.cc b/media/cdm/json_web_key_unittest.cc index ecd9d2a..82b3bb0 100644 --- a/media/cdm/json_web_key_unittest.cc +++ b/media/cdm/json_web_key_unittest.cc @@ -576,5 +576,34 @@ TEST_F(JSONWebKeyTest, ExtractKeyIds) { "'kids'[1] is not valid base64url encoded. Value: AQI/"); } +TEST_F(JSONWebKeyTest, CreateInitData) { + const uint8 data1[] = { 0x01, 0x02 }; + const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 }; + const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }; + + KeyIdList key_ids; + std::string error_message; + + key_ids.push_back(std::vector<uint8>(data1, data1 + arraysize(data1))); + std::vector<uint8> init_data1; + CreateKeyIdsInitData(key_ids, &init_data1); + std::string result1(init_data1.begin(), init_data1.end()); + EXPECT_EQ(result1, "{\"kids\":[\"AQI\"]}"); + + key_ids.push_back(std::vector<uint8>(data2, data2 + arraysize(data2))); + std::vector<uint8> init_data2; + CreateKeyIdsInitData(key_ids, &init_data2); + std::string result2(init_data2.begin(), init_data2.end()); + EXPECT_EQ(result2, "{\"kids\":[\"AQI\",\"AQIDBA\"]}"); + + key_ids.push_back(std::vector<uint8>(data3, data3 + arraysize(data3))); + std::vector<uint8> init_data3; + CreateKeyIdsInitData(key_ids, &init_data3); + std::string result3(init_data3.begin(), init_data3.end()); + EXPECT_EQ(result3, + "{\"kids\":[\"AQI\",\"AQIDBA\",\"AQIDBAUGBwgJCgsMDQ4PEA\"]}"); +} + } // namespace media |