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
10namespace media {
11
12class 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
68TEST_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
89TEST_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
226TEST_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
252TEST_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
277TEST_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