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