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.
5 #include "media/cdm/json_web_key.h"
7 #include "base/base64.h"
8 #include "base/logging.h"
9 #include "testing/gtest/include/gtest/gtest.h"
13 class JSONWebKeyTest
: public testing::Test
{
18 void ExtractJWKKeysAndExpect(const std::string
& jwk
,
20 size_t expected_number_of_keys
) {
22 KeyIdAndKeyPairs keys
;
23 MediaKeys::SessionType session_type
;
24 EXPECT_EQ(expected_result
,
25 ExtractKeysFromJWKSet(jwk
, &keys
, &session_type
));
26 EXPECT_EQ(expected_number_of_keys
, keys
.size());
29 void ExtractSessionTypeAndExpect(const std::string
& jwk
,
31 MediaKeys::SessionType expected_type
) {
33 KeyIdAndKeyPairs keys
;
34 MediaKeys::SessionType session_type
;
35 EXPECT_EQ(expected_result
,
36 ExtractKeysFromJWKSet(jwk
, &keys
, &session_type
));
37 if (expected_result
) {
38 // Only check if successful.
39 EXPECT_EQ(expected_type
, session_type
);
43 void CreateLicenseAndExpect(const uint8
* key_id
,
45 MediaKeys::SessionType session_type
,
46 const std::string
& expected_result
) {
47 std::vector
<uint8
> result
;
49 key_ids
.push_back(std::vector
<uint8
>(key_id
, key_id
+ key_id_length
));
50 CreateLicenseRequest(key_ids
, session_type
, &result
);
51 std::string
s(result
.begin(), result
.end());
52 EXPECT_EQ(expected_result
, s
);
55 void ExtractKeyFromLicenseAndExpect(const std::string
& license
,
57 const uint8
* expected_key
,
58 int expected_key_length
) {
59 std::vector
<uint8
> license_vector(license
.begin(), license
.end());
60 std::vector
<uint8
> key
;
61 EXPECT_EQ(expected_result
,
62 ExtractFirstKeyIdFromLicenseRequest(license_vector
, &key
));
63 if (expected_result
) {
64 std::vector
<uint8
> key_result(expected_key
,
65 expected_key
+ expected_key_length
);
66 EXPECT_EQ(key_result
, key
);
71 TEST_F(JSONWebKeyTest
, GenerateJWKSet
) {
72 const uint8 data1
[] = { 0x01, 0x02 };
73 const uint8 data2
[] = { 0x01, 0x02, 0x03, 0x04 };
74 const uint8 data3
[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
75 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
78 "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":"
80 GenerateJWKSet(data1
, arraysize(data1
), data1
, arraysize(data1
)));
82 "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQIDBA\",\"kid\":\"AQIDBA\","
84 GenerateJWKSet(data2
, arraysize(data2
), data2
, arraysize(data2
)));
86 "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQIDBA\",\"kty\":"
88 GenerateJWKSet(data1
, arraysize(data1
), data2
, arraysize(data2
)));
90 "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQIDBA\",\"kid\":\"AQI\",\"kty\":"
92 GenerateJWKSet(data2
, arraysize(data2
), data1
, arraysize(data1
)));
94 "{\"keys\":[{\"alg\":\"A128KW\",\"k\":\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kid\":"
95 "\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kty\":\"oct\"}]}",
96 GenerateJWKSet(data3
, arraysize(data3
), data3
, arraysize(data3
)));
99 TEST_F(JSONWebKeyTest
, ExtractValidJWKKeys
) {
100 // Try an empty 'keys' dictionary.
101 ExtractJWKKeysAndExpect("{ \"keys\": [] }", true, 0);
103 // Try a key list with one entry.
104 const std::string kJwksOneEntry
=
109 " \"alg\": \"A128KW\","
110 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
111 " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
115 ExtractJWKKeysAndExpect(kJwksOneEntry
, true, 1);
117 // Try a key list with multiple entries.
118 const std::string kJwksMultipleEntries
=
123 " \"alg\": \"A128KW\","
124 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
125 " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
129 " \"alg\": \"A128KW\","
130 " \"kid\": \"JCUmJygpKissLS4vMA\","
131 " \"k\":\"MTIzNDU2Nzg5Ojs8PT4_QA\""
135 ExtractJWKKeysAndExpect(kJwksMultipleEntries
, true, 2);
137 // Try a key with no spaces and some \n plus additional fields.
138 const std::string kJwksNoSpaces
=
139 "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
140 "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg"
141 "\",\"foo\":\"bar\"}]}\n\n";
142 ExtractJWKKeysAndExpect(kJwksNoSpaces
, true, 1);
144 // Try a list with multiple keys with the same kid.
145 const std::string kJwksDuplicateKids
=
150 " \"alg\": \"A128KW\","
151 " \"kid\": \"JCUmJygpKissLS4vMA\","
152 " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
156 " \"alg\": \"A128KW\","
157 " \"kid\": \"JCUmJygpKissLS4vMA\","
158 " \"k\":\"MTIzNDU2Nzg5Ojs8PT4_QA\""
162 ExtractJWKKeysAndExpect(kJwksDuplicateKids
, true, 2);
165 TEST_F(JSONWebKeyTest
, ExtractInvalidJWKKeys
) {
166 // Try a simple JWK key (i.e. not in a set)
167 const std::string kJwkSimple
=
170 " \"alg\": \"A128KW\","
171 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
172 " \"k\": \"FBUWFxgZGhscHR4fICEiIw\""
174 ExtractJWKKeysAndExpect(kJwkSimple
, false, 0);
176 // Try some non-ASCII characters.
177 ExtractJWKKeysAndExpect(
178 "This is not ASCII due to \xff\xfe\xfd in it.", false, 0);
180 // Try some non-ASCII characters in an otherwise valid JWK.
181 const std::string kJwksInvalidCharacters
=
182 "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\","
183 "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"\xff\xfe\xfd"
184 "\",\"foo\":\"bar\"}]}\n\n";
185 ExtractJWKKeysAndExpect(kJwksInvalidCharacters
, false, 0);
187 // Try a badly formatted key. Assume that the JSON parser is fully tested,
188 // so we won't try a lot of combinations. However, need a test to ensure
189 // that the code doesn't crash if invalid JSON received.
190 ExtractJWKKeysAndExpect("This is not a JSON key.", false, 0);
192 // Try passing some valid JSON that is not a dictionary at the top level.
193 ExtractJWKKeysAndExpect("40", false, 0);
195 // Try an empty dictionary.
196 ExtractJWKKeysAndExpect("{ }", false, 0);
198 // Try with 'keys' not a dictionary.
199 ExtractJWKKeysAndExpect("{ \"keys\":\"1\" }", false, 0);
201 // Try with 'keys' a list of integers.
202 ExtractJWKKeysAndExpect("{ \"keys\": [ 1, 2, 3 ] }", false, 0);
204 // Try padding(=) at end of 'k' base64 string.
205 const std::string kJwksWithPaddedKey
=
210 " \"alg\": \"A128KW\","
211 " \"kid\": \"AAECAw\","
212 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\""
216 ExtractJWKKeysAndExpect(kJwksWithPaddedKey
, false, 0);
218 // Try padding(=) at end of 'kid' base64 string.
219 const std::string kJwksWithPaddedKeyId
=
224 " \"alg\": \"A128KW\","
225 " \"kid\": \"AAECAw==\","
226 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
230 ExtractJWKKeysAndExpect(kJwksWithPaddedKeyId
, false, 0);
232 // Try a key with invalid base64 encoding.
233 const std::string kJwksWithInvalidBase64
=
238 " \"alg\": \"A128KW\","
239 " \"kid\": \"!@#$%^&*()\","
240 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
244 ExtractJWKKeysAndExpect(kJwksWithInvalidBase64
, false, 0);
247 const std::string kJwksWithEmptyKeyId
=
252 " \"alg\": \"A128KW\","
254 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
258 ExtractJWKKeysAndExpect(kJwksWithEmptyKeyId
, false, 0);
261 TEST_F(JSONWebKeyTest
, KeyType
) {
263 const std::string kJwksWithValidKeyType
=
268 " \"alg\": \"A128KW\","
269 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
270 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
274 ExtractJWKKeysAndExpect(kJwksWithValidKeyType
, true, 1);
277 const std::string kJwksWithEmptyKeyType
=
282 " \"alg\": \"A128KW\","
283 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
284 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
288 ExtractJWKKeysAndExpect(kJwksWithEmptyKeyType
, false, 0);
290 // Key type is case sensitive.
291 const std::string kJwksWithUppercaseKeyType
=
296 " \"alg\": \"A128KW\","
297 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
298 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
302 ExtractJWKKeysAndExpect(kJwksWithUppercaseKeyType
, false, 0);
305 const std::string kJwksWithWrongKeyType
=
310 " \"alg\": \"A128KW\","
311 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
312 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
316 ExtractJWKKeysAndExpect(kJwksWithWrongKeyType
, false, 0);
319 TEST_F(JSONWebKeyTest
, Alg
) {
321 const std::string kJwksWithValidAlg
=
326 " \"alg\": \"A128KW\","
327 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
328 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
332 ExtractJWKKeysAndExpect(kJwksWithValidAlg
, true, 1);
335 const std::string kJwksWithEmptyAlg
=
341 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
342 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
346 ExtractJWKKeysAndExpect(kJwksWithEmptyAlg
, false, 0);
348 // Alg is case sensitive.
349 const std::string kJwksWithLowercaseAlg
=
354 " \"alg\": \"a128kw\","
355 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
356 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
360 ExtractJWKKeysAndExpect(kJwksWithLowercaseAlg
, false, 0);
363 const std::string kJwksWithWrongAlg
=
368 " \"alg\": \"RS256\","
369 " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\","
370 " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\""
374 ExtractJWKKeysAndExpect(kJwksWithWrongAlg
, false, 0);
377 TEST_F(JSONWebKeyTest
, SessionType
) {
378 ExtractSessionTypeAndExpect(
379 "{\"keys\":[{\"alg\": "
380 "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}",
381 true, MediaKeys::TEMPORARY_SESSION
);
382 ExtractSessionTypeAndExpect(
383 "{\"keys\":[{\"alg\": "
384 "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
386 true, MediaKeys::TEMPORARY_SESSION
);
387 ExtractSessionTypeAndExpect(
388 "{\"keys\":[{\"alg\": "
389 "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
390 "\"persistent-license\"}",
391 true, MediaKeys::PERSISTENT_LICENSE_SESSION
);
392 ExtractSessionTypeAndExpect(
393 "{\"keys\":[{\"alg\": "
394 "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
395 "\"persistent-release-message\"}",
396 true, MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION
);
397 ExtractSessionTypeAndExpect(
398 "{\"keys\":[{\"alg\": "
399 "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":"
401 false, MediaKeys::TEMPORARY_SESSION
);
402 ExtractSessionTypeAndExpect(
403 "{\"keys\":[{\"alg\": "
404 "\"A128KW\",\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}],\"type\":3}",
405 false, MediaKeys::TEMPORARY_SESSION
);
408 TEST_F(JSONWebKeyTest
, CreateLicense
) {
409 const uint8 data1
[] = { 0x01, 0x02 };
410 const uint8 data2
[] = { 0x01, 0x02, 0x03, 0x04 };
411 const uint8 data3
[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
412 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
414 CreateLicenseAndExpect(data1
,
416 MediaKeys::TEMPORARY_SESSION
,
417 "{\"kids\":[\"AQI\"],\"type\":\"temporary\"}");
418 CreateLicenseAndExpect(
419 data1
, arraysize(data1
), MediaKeys::PERSISTENT_LICENSE_SESSION
,
420 "{\"kids\":[\"AQI\"],\"type\":\"persistent-license\"}");
421 CreateLicenseAndExpect(
422 data1
, arraysize(data1
), MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION
,
423 "{\"kids\":[\"AQI\"],\"type\":\"persistent-release-message\"}");
424 CreateLicenseAndExpect(data2
,
426 MediaKeys::TEMPORARY_SESSION
,
427 "{\"kids\":[\"AQIDBA\"],\"type\":\"temporary\"}");
428 CreateLicenseAndExpect(data3
, arraysize(data3
),
429 MediaKeys::PERSISTENT_LICENSE_SESSION
,
430 "{\"kids\":[\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":"
431 "\"persistent-license\"}");
434 TEST_F(JSONWebKeyTest
, ExtractLicense
) {
435 const uint8 data1
[] = { 0x01, 0x02 };
436 const uint8 data2
[] = { 0x01, 0x02, 0x03, 0x04 };
437 const uint8 data3
[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
438 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
440 ExtractKeyFromLicenseAndExpect(
441 "{\"kids\":[\"AQI\"],\"type\":\"temporary\"}",
445 ExtractKeyFromLicenseAndExpect(
446 "{\"kids\":[\"AQIDBA\"],\"type\":\"temporary\"}",
450 ExtractKeyFromLicenseAndExpect(
451 "{\"kids\":[\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":\"persistent\"}",
456 // Try some incorrect JSON.
457 ExtractKeyFromLicenseAndExpect("", false, NULL
, 0);
458 ExtractKeyFromLicenseAndExpect("!@#$%^&*()", false, NULL
, 0);
460 // Valid JSON, but not a dictionary.
461 ExtractKeyFromLicenseAndExpect("6", false, NULL
, 0);
462 ExtractKeyFromLicenseAndExpect("[\"AQI\"]", false, NULL
, 0);
464 // Dictionary, but missing expected tag.
465 ExtractKeyFromLicenseAndExpect("{\"kid\":[\"AQI\"]}", false, NULL
, 0);
467 // Correct tag, but empty list.
468 ExtractKeyFromLicenseAndExpect("{\"kids\":[]}", false, NULL
, 0);
470 // Correct tag, but list doesn't contain a string.
471 ExtractKeyFromLicenseAndExpect("{\"kids\":[[\"AQI\"]]}", false, NULL
, 0);
473 // Correct tag, but invalid base64 encoding.
474 ExtractKeyFromLicenseAndExpect("{\"kids\":[\"!@#$%^&*()\"]}", false, NULL
, 0);
477 TEST_F(JSONWebKeyTest
, Base64UrlEncoding
) {
478 const uint8 data1
[] = { 0xfb, 0xfd, 0xfb, 0xfd, 0xfb, 0xfd, 0xfb };
480 // Verify that |data1| contains invalid base64url characters '+' and '/'
481 // and is padded with = when converted to base64.
482 std::string encoded_text
;
484 std::string(reinterpret_cast<const char*>(&data1
[0]), arraysize(data1
)),
486 EXPECT_EQ(encoded_text
, "+/37/fv9+w==");
487 EXPECT_NE(encoded_text
.find('+'), std::string::npos
);
488 EXPECT_NE(encoded_text
.find('/'), std::string::npos
);
489 EXPECT_NE(encoded_text
.find('='), std::string::npos
);
491 // base64url characters '-' and '_' not in base64 encoding.
492 EXPECT_EQ(encoded_text
.find('-'), std::string::npos
);
493 EXPECT_EQ(encoded_text
.find('_'), std::string::npos
);
495 CreateLicenseAndExpect(data1
, arraysize(data1
), MediaKeys::TEMPORARY_SESSION
,
496 "{\"kids\":[\"-_37_fv9-w\"],\"type\":\"temporary\"}");
498 ExtractKeyFromLicenseAndExpect(
499 "{\"kids\":[\"-_37_fv9-w\"],\"type\":\"temporary\"}", true, data1
,
503 TEST_F(JSONWebKeyTest
, MultipleKeys
) {
504 const uint8 data1
[] = { 0x01, 0x02 };
505 const uint8 data2
[] = { 0x01, 0x02, 0x03, 0x04 };
506 const uint8 data3
[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
507 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
509 std::vector
<uint8
> result
;
511 key_ids
.push_back(std::vector
<uint8
>(data1
, data1
+ arraysize(data1
)));
512 key_ids
.push_back(std::vector
<uint8
>(data2
, data2
+ arraysize(data2
)));
513 key_ids
.push_back(std::vector
<uint8
>(data3
, data3
+ arraysize(data3
)));
514 CreateLicenseRequest(key_ids
, MediaKeys::TEMPORARY_SESSION
, &result
);
515 std::string
s(result
.begin(), result
.end());
517 "{\"kids\":[\"AQI\",\"AQIDBA\",\"AQIDBAUGBwgJCgsMDQ4PEA\"],\"type\":"