json_web_key_unittest.cc revision f2477e01787aa58f445919b809d89e252beef54f
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/cdm/json_web_key.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace media {
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class JSONWebKeyTest : public testing::Test {
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
14ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  JSONWebKeyTest() {}
158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
16ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch protected:
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ExtractJWKKeysAndExpect(const std::string& jwk,
18ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                               bool expected_result,
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               size_t expected_number_of_keys) {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!jwk.empty());
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KeyIdAndKeyPairs keys;
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expected_result, ExtractKeysFromJWKSet(jwk, &keys));
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expected_number_of_keys, keys.size());
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(JSONWebKeyTest, GenerateJWKSet) {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint8 data1[] = { 0x01, 0x02 };
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 };
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GenerateJWKSet(data1, arraysize(data1), data1, arraysize(data1)));
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}",
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GenerateJWKSet(data2, arraysize(data2), data2, arraysize(data2)));
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}",
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GenerateJWKSet(data1, arraysize(data1), data2, arraysize(data2)));
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_EQ("{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GenerateJWKSet(data2, arraysize(data2), data1, arraysize(data1)));
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{\"keys\":[{\"k\":\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kid\":"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kty\":\"oct\"}]}",
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GenerateJWKSet(data3, arraysize(data3), data3, arraysize(data3)));
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(JSONWebKeyTest, ExtractJWKKeys) {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try a simple JWK key (i.e. not in a set)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string kJwkSimple =
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "  \"kty\": \"oct\","
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}";
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractJWKKeysAndExpect(kJwkSimple, false, 0);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try a key list with multiple entries.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string kJwksMultipleEntries =
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"keys\": ["
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    {"
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kty\": \"oct\","
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    },"
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    {"
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kty\": \"oct\","
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kid\": \"JCUmJygpKissLS4vMA\","
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    }"
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  ]"
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}";
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractJWKKeysAndExpect(kJwksMultipleEntries, true, 2);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try a key with no spaces and some \n plus additional fields.
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string kJwksNoSpaces =
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg"
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\",\"foo\":\"bar\"}]}\n\n";
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractJWKKeysAndExpect(kJwksNoSpaces, true, 1);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try some non-ASCII characters.
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtractJWKKeysAndExpect(
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "This is not ASCII due to \xff\xfe\xfd in it.", false, 0);
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try some non-ASCII characters in an otherwise valid JWK.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string kJwksInvalidCharacters =
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"\xff\xfe\xfd"
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "\",\"foo\":\"bar\"}]}\n\n";
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ExtractJWKKeysAndExpect(kJwksInvalidCharacters, false, 0);
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try a badly formatted key. Assume that the JSON parser is fully tested,
956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // so we won't try a lot of combinations. However, need a test to ensure
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // that the code doesn't crash if invalid JSON received.
976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  ExtractJWKKeysAndExpect("This is not a JSON key.", false, 0);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Try passing some valid JSON that is not a dictionary at the top level.
10090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  ExtractJWKKeysAndExpect("40", false, 0);
10190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
10290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Try an empty dictionary.
103ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ExtractJWKKeysAndExpect("{ }", false, 0);
104ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
105ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Try an empty 'keys' dictionary.
106ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ExtractJWKKeysAndExpect("{ \"keys\": [] }", true, 0);
107ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
108ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Try with 'keys' not a dictionary.
109ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ExtractJWKKeysAndExpect("{ \"keys\":\"1\" }", false, 0);
110ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try with 'keys' a list of integers.
1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  ExtractJWKKeysAndExpect("{ \"keys\": [ 1, 2, 3 ] }", false, 0);
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try padding(=) at end of 'k' base64 string.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string kJwksWithPaddedKey =
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"keys\": ["
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    {"
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kty\": \"oct\","
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kid\": \"AAECAw\","
1218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\""
1228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      "    }"
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  ]"
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}";
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractJWKKeysAndExpect(kJwksWithPaddedKey, false, 0);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try padding(=) at end of 'kid' base64 string.
1288bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  const std::string kJwksWithPaddedKeyId =
1298bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      "{"
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"keys\": ["
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    {"
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kty\": \"oct\","
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kid\": \"AAECAw==\","
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    }"
1368bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      "  ]"
1378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      "}";
1388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  ExtractJWKKeysAndExpect(kJwksWithPaddedKeyId, false, 0);
1398bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1408bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Try a key with invalid base64 encoding.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string kJwksWithInvalidBase64 =
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"keys\": ["
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    {"
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kty\": \"oct\","
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kid\": \"!@#$%^&*()\","
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    }"
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  ]"
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}";
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractJWKKeysAndExpect(kJwksWithInvalidBase64, false, 0);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Empty key id.
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string kJwksWithEmptyKeyId =
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"keys\": ["
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    {"
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kty\": \"oct\","
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kid\": \"\","
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    }"
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  ]"
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "}";
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtractJWKKeysAndExpect(kJwksWithEmptyKeyId, false, 0);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try a list with multiple keys with the same kid.
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string kJwksDuplicateKids =
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "{"
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "  \"keys\": ["
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    {"
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kty\": \"oct\","
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"kid\": \"JCUmJygpKissLS4vMA\","
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "      \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "    },"
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "    {"
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "      \"kty\": \"oct\","
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "      \"kid\": \"JCUmJygpKissLS4vMA\","
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
179ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "    }"
180ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "  ]"
181ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      "}";
182ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ExtractJWKKeysAndExpect(kJwksDuplicateKids, true, 2);
183ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
184ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
185ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}  // namespace media
186ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
187ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch