• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
JSONWebKeyTest()14   JSONWebKeyTest() {}
15 
16  protected:
ExtractJWKKeysAndExpect(const std::string & jwk,bool expected_result,size_t expected_number_of_keys)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 
ExtractSessionTypeAndExpect(const std::string & jwk,bool expected_result,MediaKeys::SessionType expected_type)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 
CreateLicenseAndExpect(const uint8 * key_id,int key_id_length,MediaKeys::SessionType session_type,const std::string & expected_result)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 
ExtractKeyFromLicenseAndExpect(const std::string & license,bool expected_result,const uint8 * expected_key,int expected_key_length)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 
TEST_F(JSONWebKeyTest,GenerateJWKSet)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 
TEST_F(JSONWebKeyTest,ExtractJWKKeys)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 
TEST_F(JSONWebKeyTest,SessionType)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 
TEST_F(JSONWebKeyTest,CreateLicense)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 
TEST_F(JSONWebKeyTest,ExtractLicense)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