Home | History | Annotate | Download | only in cdm
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/cdm/json_web_key.h"
      6 
      7 #include "base/logging.h"
      8 #include "testing/gtest/include/gtest/gtest.h"
      9 
     10 namespace media {
     11 
     12 class JSONWebKeyTest : public testing::Test {
     13  public:
     14   JSONWebKeyTest() {}
     15 
     16  protected:
     17   void ExtractJWKKeysAndExpect(const std::string& jwk,
     18                                bool expected_result,
     19                                size_t expected_number_of_keys) {
     20     DCHECK(!jwk.empty());
     21     KeyIdAndKeyPairs keys;
     22     MediaKeys::SessionType session_type;
     23     EXPECT_EQ(expected_result,
     24               ExtractKeysFromJWKSet(jwk, &keys, &session_type));
     25     EXPECT_EQ(expected_number_of_keys, keys.size());
     26   }
     27 
     28   void ExtractSessionTypeAndExpect(const std::string& jwk,
     29                                    bool expected_result,
     30                                    MediaKeys::SessionType expected_type) {
     31     DCHECK(!jwk.empty());
     32     KeyIdAndKeyPairs keys;
     33     MediaKeys::SessionType session_type;
     34     EXPECT_EQ(expected_result,
     35               ExtractKeysFromJWKSet(jwk, &keys, &session_type));
     36     if (expected_result) {
     37       // Only check if successful.
     38       EXPECT_EQ(expected_type, session_type);
     39     }
     40   }
     41 
     42   void CreateLicenseAndExpect(const uint8* key_id,
     43                               int key_id_length,
     44                               MediaKeys::SessionType session_type,
     45                               const std::string& expected_result) {
     46     std::vector<uint8> result;
     47     CreateLicenseRequest(key_id, key_id_length, session_type, &result);
     48     std::string s(result.begin(), result.end());
     49     EXPECT_EQ(expected_result, s);
     50   }
     51 
     52   void ExtractKeyFromLicenseAndExpect(const std::string& license,
     53                                       bool expected_result,
     54                                       const uint8* expected_key,
     55                                       int expected_key_length) {
     56     std::vector<uint8> license_vector(license.begin(), license.end());
     57     std::vector<uint8> key;
     58     EXPECT_EQ(expected_result,
     59               ExtractFirstKeyIdFromLicenseRequest(license_vector, &key));
     60     if (expected_result) {
     61       std::vector<uint8> key_result(expected_key,
     62                                     expected_key + expected_key_length);
     63       EXPECT_EQ(key_result, key);
     64     }
     65   }
     66 };
     67 
     68 TEST_F(JSONWebKeyTest, GenerateJWKSet) {
     69   const uint8 data1[] = { 0x01, 0x02 };
     70   const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 };
     71   const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
     72                           0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
     73 
     74   EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
     75             GenerateJWKSet(data1, arraysize(data1), data1, arraysize(data1)));
     76   EXPECT_EQ(
     77       "{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}",
     78       GenerateJWKSet(data2, arraysize(data2), data2, arraysize(data2)));
     79   EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}",
     80             GenerateJWKSet(data1, arraysize(data1), data2, arraysize(data2)));
     81   EXPECT_EQ("{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
     82             GenerateJWKSet(data2, arraysize(data2), data1, arraysize(data1)));
     83   EXPECT_EQ(
     84       "{\"keys\":[{\"k\":\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kid\":"
     85       "\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kty\":\"oct\"}]}",
     86       GenerateJWKSet(data3, arraysize(data3), data3, arraysize(data3)));
     87 }
     88 
     89 TEST_F(JSONWebKeyTest, ExtractJWKKeys) {
     90   // Try a simple JWK key (i.e. not in a set)
     91   const std::string kJwkSimple =
     92       "{"
     93       "  \"kty\": \"oct\","
     94       "  \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
     95       "  \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
     96       "}";
     97   ExtractJWKKeysAndExpect(kJwkSimple, false, 0);
     98 
     99   // Try a key list with multiple entries.
    100   const std::string kJwksMultipleEntries =
    101       "{"
    102       "  \"keys\": ["
    103       "    {"
    104       "      \"kty\": \"oct\","
    105       "      \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
    106       "      \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
    107       "    },"
    108       "    {"
    109       "      \"kty\": \"oct\","
    110       "      \"kid\": \"JCUmJygpKissLS4vMA\","
    111       "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
    112       "    }"
    113       "  ]"
    114       "}";
    115   ExtractJWKKeysAndExpect(kJwksMultipleEntries, true, 2);
    116 
    117   // Try a key with no spaces and some \n plus additional fields.
    118   const std::string kJwksNoSpaces =
    119       "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
    120       "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg"
    121       "\",\"foo\":\"bar\"}]}\n\n";
    122   ExtractJWKKeysAndExpect(kJwksNoSpaces, true, 1);
    123 
    124   // Try some non-ASCII characters.
    125   ExtractJWKKeysAndExpect(
    126       "This is not ASCII due to \xff\xfe\xfd in it.", false, 0);
    127 
    128   // Try some non-ASCII characters in an otherwise valid JWK.
    129   const std::string kJwksInvalidCharacters =
    130       "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
    131       "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"\xff\xfe\xfd"
    132       "\",\"foo\":\"bar\"}]}\n\n";
    133   ExtractJWKKeysAndExpect(kJwksInvalidCharacters, false, 0);
    134 
    135   // Try a badly formatted key. Assume that the JSON parser is fully tested,
    136   // so we won't try a lot of combinations. However, need a test to ensure
    137   // that the code doesn't crash if invalid JSON received.
    138   ExtractJWKKeysAndExpect("This is not a JSON key.", false, 0);
    139 
    140   // Try passing some valid JSON that is not a dictionary at the top level.
    141   ExtractJWKKeysAndExpect("40", false, 0);
    142 
    143   // Try an empty dictionary.
    144   ExtractJWKKeysAndExpect("{ }", false, 0);
    145 
    146   // Try an empty 'keys' dictionary.
    147   ExtractJWKKeysAndExpect("{ \"keys\": [] }", true, 0);
    148 
    149   // Try with 'keys' not a dictionary.
    150   ExtractJWKKeysAndExpect("{ \"keys\":\"1\" }", false, 0);
    151 
    152   // Try with 'keys' a list of integers.
    153   ExtractJWKKeysAndExpect("{ \"keys\": [ 1, 2, 3 ] }", false, 0);
    154 
    155   // Try padding(=) at end of 'k' base64 string.
    156   const std::string kJwksWithPaddedKey =
    157       "{"
    158       "  \"keys\": ["
    159       "    {"
    160       "      \"kty\": \"oct\","
    161       "      \"kid\": \"AAECAw\","
    162       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\""
    163       "    }"
    164       "  ]"
    165       "}";
    166   ExtractJWKKeysAndExpect(kJwksWithPaddedKey, false, 0);
    167 
    168   // Try padding(=) at end of 'kid' base64 string.
    169   const std::string kJwksWithPaddedKeyId =
    170       "{"
    171       "  \"keys\": ["
    172       "    {"
    173       "      \"kty\": \"oct\","
    174       "      \"kid\": \"AAECAw==\","
    175       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
    176       "    }"
    177       "  ]"
    178       "}";
    179   ExtractJWKKeysAndExpect(kJwksWithPaddedKeyId, false, 0);
    180 
    181   // Try a key with invalid base64 encoding.
    182   const std::string kJwksWithInvalidBase64 =
    183       "{"
    184       "  \"keys\": ["
    185       "    {"
    186       "      \"kty\": \"oct\","
    187       "      \"kid\": \"!@#$%^&*()\","
    188       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
    189       "    }"
    190       "  ]"
    191       "}";
    192   ExtractJWKKeysAndExpect(kJwksWithInvalidBase64, false, 0);
    193 
    194   // Empty key id.
    195   const std::string kJwksWithEmptyKeyId =
    196       "{"
    197       "  \"keys\": ["
    198       "    {"
    199       "      \"kty\": \"oct\","
    200       "      \"kid\": \"\","
    201       "      \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
    202       "    }"
    203       "  ]"
    204       "}";
    205   ExtractJWKKeysAndExpect(kJwksWithEmptyKeyId, false, 0);
    206 
    207   // Try a list with multiple keys with the same kid.
    208   const std::string kJwksDuplicateKids =
    209       "{"
    210       "  \"keys\": ["
    211       "    {"
    212       "      \"kty\": \"oct\","
    213       "      \"kid\": \"JCUmJygpKissLS4vMA\","
    214       "      \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
    215       "    },"
    216       "    {"
    217       "      \"kty\": \"oct\","
    218       "      \"kid\": \"JCUmJygpKissLS4vMA\","
    219       "      \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\""
    220       "    }"
    221       "  ]"
    222       "}";
    223   ExtractJWKKeysAndExpect(kJwksDuplicateKids, true, 2);
    224 }
    225 
    226 TEST_F(JSONWebKeyTest, SessionType) {
    227   ExtractSessionTypeAndExpect(
    228       "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
    229       true,
    230       MediaKeys::TEMPORARY_SESSION);
    231   ExtractSessionTypeAndExpect(
    232       "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
    233       "\"temporary\"}",
    234       true,
    235       MediaKeys::TEMPORARY_SESSION);
    236   ExtractSessionTypeAndExpect(
    237       "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
    238       "\"persistent\"}",
    239       true,
    240       MediaKeys::PERSISTENT_SESSION);
    241   ExtractSessionTypeAndExpect(
    242       "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
    243       "\"unknown\"}",
    244       false,
    245       MediaKeys::TEMPORARY_SESSION);
    246   ExtractSessionTypeAndExpect(
    247       "{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":3}",
    248       false,
    249       MediaKeys::TEMPORARY_SESSION);
    250 }
    251 
    252 TEST_F(JSONWebKeyTest, CreateLicense) {
    253   const uint8 data1[] = { 0x01, 0x02 };
    254   const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 };
    255   const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    256                           0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
    257 
    258   CreateLicenseAndExpect(data1,
    259                          arraysize(data1),
    260                          MediaKeys::TEMPORARY_SESSION,
    261                          "{\"kids\":[\"AQI\"],\"type\":\"temporary\"}");
    262   CreateLicenseAndExpect(data1,
    263                          arraysize(data1),
    264                          MediaKeys::PERSISTENT_SESSION,
    265                          "{\"kids\":[\"AQI\"],\"type\":\"persistent\"}");
    266   CreateLicenseAndExpect(data2,
    267                          arraysize(data2),
    268                          MediaKeys::TEMPORARY_SESSION,
    269                          "{\"kids\":[\"AQIDBA\"],\"type\":\"temporary\"}");
    270   CreateLicenseAndExpect(
    271       data3,
    272       arraysize(data3),
    273       MediaKeys::PERSISTENT_SESSION,
    274       "{\"kids\":[\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":\"persistent\"}");
    275 }
    276 
    277 TEST_F(JSONWebKeyTest, ExtractLicense) {
    278   const uint8 data1[] = { 0x01, 0x02 };
    279   const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 };
    280   const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    281                           0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
    282 
    283   ExtractKeyFromLicenseAndExpect(
    284       "{\"kids\":[\"AQI\"],\"type\":\"temporary\"}",
    285       true,
    286       data1,
    287       arraysize(data1));
    288   ExtractKeyFromLicenseAndExpect(
    289       "{\"kids\":[\"AQIDBA\"],\"type\":\"temporary\"}",
    290       true,
    291       data2,
    292       arraysize(data2));
    293   ExtractKeyFromLicenseAndExpect(
    294       "{\"kids\":[\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":\"persistent\"}",
    295       true,
    296       data3,
    297       arraysize(data3));
    298 
    299   // Try some incorrect JSON.
    300   ExtractKeyFromLicenseAndExpect("", false, NULL, 0);
    301   ExtractKeyFromLicenseAndExpect("!@#$%^&*()", false, NULL, 0);
    302 
    303   // Valid JSON, but not a dictionary.
    304   ExtractKeyFromLicenseAndExpect("6", false, NULL, 0);
    305   ExtractKeyFromLicenseAndExpect("[\"AQI\"]", false, NULL, 0);
    306 
    307   // Dictionary, but missing expected tag.
    308   ExtractKeyFromLicenseAndExpect("{\"kid\":[\"AQI\"]}", false, NULL, 0);
    309 
    310   // Correct tag, but empty list.
    311   ExtractKeyFromLicenseAndExpect("{\"kids\":[]}", false, NULL, 0);
    312 
    313   // Correct tag, but list doesn't contain a string.
    314   ExtractKeyFromLicenseAndExpect("{\"kids\":[[\"AQI\"]]}", false, NULL, 0);
    315 
    316   // Correct tag, but invalid base64 encoding.
    317   ExtractKeyFromLicenseAndExpect("{\"kids\":[\"!@#$%^&*()\"]}", false, NULL, 0);
    318 }
    319 
    320 }  // namespace media
    321 
    322