OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cdm/json_web_key.h" | 5 #include "media/cdm/json_web_key.h" |
6 | 6 |
7 #include "base/base64.h" | 7 #include "base/base64.h" |
8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
9 #include "base/json/json_string_value_serializer.h" | 9 #include "base/json/json_string_value_serializer.h" |
10 #include "base/json/string_escape.h" | 10 #include "base/json/string_escape.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
13 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
14 #include "base/values.h" | 14 #include "base/values.h" |
15 | 15 |
16 namespace media { | 16 namespace media { |
17 | 17 |
18 const char kKeysTag[] = "keys"; | 18 const char kKeysTag[] = "keys"; |
19 const char kKeyTypeTag[] = "kty"; | 19 const char kKeyTypeTag[] = "kty"; |
20 const char kKeyTypeOct[] = "oct"; // Octet sequence. | 20 const char kKeyTypeOct[] = "oct"; // Octet sequence. |
21 const char kAlgTag[] = "alg"; | 21 const char kAlgTag[] = "alg"; |
22 const char kAlgA128KW[] = "A128KW"; // AES key wrap using a 128-bit key. | 22 const char kAlgA128KW[] = "A128KW"; // AES key wrap using a 128-bit key. |
23 const char kKeyTag[] = "k"; | 23 const char kKeyTag[] = "k"; |
24 const char kKeyIdTag[] = "kid"; | 24 const char kKeyIdTag[] = "kid"; |
25 const char kKeyIdsTag[] = "kids"; | 25 const char kKeyIdsTag[] = "kids"; |
26 const char kBase64Padding = '='; | 26 const char kBase64Padding = '='; |
| 27 const char kBase64Plus[] = "+"; |
| 28 const char kBase64UrlPlusReplacement[] = "-"; |
| 29 const char kBase64Slash[] = "/"; |
| 30 const char kBase64UrlSlashReplacement[] = "_"; |
27 const char kTypeTag[] = "type"; | 31 const char kTypeTag[] = "type"; |
28 const char kTemporarySession[] = "temporary"; | 32 const char kTemporarySession[] = "temporary"; |
29 const char kPersistentLicenseSession[] = "persistent-license"; | 33 const char kPersistentLicenseSession[] = "persistent-license"; |
30 const char kPersistentReleaseMessageSession[] = "persistent-release-message"; | 34 const char kPersistentReleaseMessageSession[] = "persistent-release-message"; |
31 | 35 |
32 // Encodes |input| into a base64 string without padding. | 36 // Encodes |input| into a base64url string without padding. |
33 static std::string EncodeBase64(const uint8* input, int input_length) { | 37 static std::string EncodeBase64Url(const uint8* input, int input_length) { |
34 std::string encoded_text; | 38 std::string encoded_text; |
35 base::Base64Encode( | 39 base::Base64Encode( |
36 std::string(reinterpret_cast<const char*>(input), input_length), | 40 std::string(reinterpret_cast<const char*>(input), input_length), |
37 &encoded_text); | 41 &encoded_text); |
38 | 42 |
39 // Remove any padding characters added by Base64Encode(). | 43 // Remove any padding characters added by Base64Encode(). |
40 size_t found = encoded_text.find_last_not_of(kBase64Padding); | 44 size_t found = encoded_text.find_last_not_of(kBase64Padding); |
41 if (found != std::string::npos) | 45 if (found != std::string::npos) |
42 encoded_text.erase(found + 1); | 46 encoded_text.erase(found + 1); |
43 | 47 |
| 48 // base64url encoding means the characters '-' and '_' must be used |
| 49 // instead of '+' and '/', respectively. |
| 50 base::ReplaceChars(encoded_text, kBase64Plus, kBase64UrlPlusReplacement, |
| 51 &encoded_text); |
| 52 base::ReplaceChars(encoded_text, kBase64Slash, kBase64UrlSlashReplacement, |
| 53 &encoded_text); |
| 54 |
44 return encoded_text; | 55 return encoded_text; |
45 } | 56 } |
46 | 57 |
47 // Decodes an unpadded base64 string. Returns empty string on error. | 58 // Decodes a base64url string. Returns empty string on error. |
48 static std::string DecodeBase64(const std::string& encoded_text) { | 59 static std::string DecodeBase64Url(const std::string& encoded_text) { |
49 // EME spec doesn't allow padding characters. | 60 // EME spec doesn't allow padding characters. |
50 if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) { | 61 if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) { |
51 DVLOG(1) << "Padding characters not allowed: " << encoded_text; | 62 DVLOG(1) << "Padding characters not allowed: " << encoded_text; |
52 return std::string(); | 63 return std::string(); |
53 } | 64 } |
| 65 // TODO(jrummell): Enable once blink tests updated to use base64url encoding. |
| 66 //if (encoded_text.find(kBase64Plus) != std::string::npos) { |
| 67 // DVLOG(1) << "Base64 '+' characters not allowed: " << encoded_text; |
| 68 // return std::string(); |
| 69 //} |
| 70 //if (encoded_text.find(kBase64Slash) != std::string::npos) { |
| 71 // DVLOG(1) << "Base64 '/' characters not allowed: " << encoded_text; |
| 72 // return std::string(); |
| 73 //} |
54 | 74 |
55 // Since base::Base64Decode() requires padding characters, add them so length | 75 // Since base::Base64Decode() requires padding characters, add them so length |
56 // of |encoded_text| is exactly a multiple of 4. | 76 // of |encoded_text| is exactly a multiple of 4. |
57 size_t num_last_grouping_chars = encoded_text.length() % 4; | 77 size_t num_last_grouping_chars = encoded_text.length() % 4; |
58 std::string modified_text = encoded_text; | 78 std::string modified_text = encoded_text; |
59 if (num_last_grouping_chars > 0) | 79 if (num_last_grouping_chars > 0) |
60 modified_text.append(4 - num_last_grouping_chars, kBase64Padding); | 80 modified_text.append(4 - num_last_grouping_chars, kBase64Padding); |
61 | 81 |
| 82 // base64url encoding means the characters '-' and '_' must be used |
| 83 // instead of '+' and '/', respectively, so replace them before calling |
| 84 // base::Base64Decode(). |
| 85 base::ReplaceChars(modified_text, kBase64UrlPlusReplacement, kBase64Plus, |
| 86 &modified_text); |
| 87 base::ReplaceChars(modified_text, kBase64UrlSlashReplacement, kBase64Slash, |
| 88 &modified_text); |
| 89 |
62 std::string decoded_text; | 90 std::string decoded_text; |
63 if (!base::Base64Decode(modified_text, &decoded_text)) { | 91 if (!base::Base64Decode(modified_text, &decoded_text)) { |
64 DVLOG(1) << "Base64 decoding failed on: " << modified_text; | 92 DVLOG(1) << "Base64 decoding failed on: " << modified_text; |
65 return std::string(); | 93 return std::string(); |
66 } | 94 } |
67 | 95 |
68 return decoded_text; | 96 return decoded_text; |
69 } | 97 } |
70 | 98 |
71 std::string GenerateJWKSet(const uint8* key, int key_length, | 99 std::string GenerateJWKSet(const uint8* key, int key_length, |
72 const uint8* key_id, int key_id_length) { | 100 const uint8* key_id, int key_id_length) { |
73 // Both |key| and |key_id| need to be base64 encoded strings in the JWK. | 101 // Both |key| and |key_id| need to be base64 encoded strings in the JWK. |
74 std::string key_base64 = EncodeBase64(key, key_length); | 102 std::string key_base64 = EncodeBase64Url(key, key_length); |
75 std::string key_id_base64 = EncodeBase64(key_id, key_id_length); | 103 std::string key_id_base64 = EncodeBase64Url(key_id, key_id_length); |
76 | 104 |
77 // Create the JWK, and wrap it into a JWK Set. | 105 // Create the JWK, and wrap it into a JWK Set. |
78 scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); | 106 scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); |
79 jwk->SetString(kKeyTypeTag, kKeyTypeOct); | 107 jwk->SetString(kKeyTypeTag, kKeyTypeOct); |
80 jwk->SetString(kAlgTag, kAlgA128KW); | 108 jwk->SetString(kAlgTag, kAlgA128KW); |
81 jwk->SetString(kKeyTag, key_base64); | 109 jwk->SetString(kKeyTag, key_base64); |
82 jwk->SetString(kKeyIdTag, key_id_base64); | 110 jwk->SetString(kKeyIdTag, key_id_base64); |
83 scoped_ptr<base::ListValue> list(new base::ListValue()); | 111 scoped_ptr<base::ListValue> list(new base::ListValue()); |
84 list->Append(jwk.release()); | 112 list->Append(jwk.release()); |
85 base::DictionaryValue jwk_set; | 113 base::DictionaryValue jwk_set; |
(...skipping 28 matching lines...) Expand all Loading... |
114 if (!jwk.GetString(kKeyIdTag, &encoded_key_id)) { | 142 if (!jwk.GetString(kKeyIdTag, &encoded_key_id)) { |
115 DVLOG(1) << "Missing '" << kKeyIdTag << "' parameter"; | 143 DVLOG(1) << "Missing '" << kKeyIdTag << "' parameter"; |
116 return false; | 144 return false; |
117 } | 145 } |
118 if (!jwk.GetString(kKeyTag, &encoded_key)) { | 146 if (!jwk.GetString(kKeyTag, &encoded_key)) { |
119 DVLOG(1) << "Missing '" << kKeyTag << "' parameter"; | 147 DVLOG(1) << "Missing '" << kKeyTag << "' parameter"; |
120 return false; | 148 return false; |
121 } | 149 } |
122 | 150 |
123 // Key ID and key are base64-encoded strings, so decode them. | 151 // Key ID and key are base64-encoded strings, so decode them. |
124 std::string raw_key_id = DecodeBase64(encoded_key_id); | 152 std::string raw_key_id = DecodeBase64Url(encoded_key_id); |
125 if (raw_key_id.empty()) { | 153 if (raw_key_id.empty()) { |
126 DVLOG(1) << "Invalid '" << kKeyIdTag << "' value: " << encoded_key_id; | 154 DVLOG(1) << "Invalid '" << kKeyIdTag << "' value: " << encoded_key_id; |
127 return false; | 155 return false; |
128 } | 156 } |
129 | 157 |
130 std::string raw_key = DecodeBase64(encoded_key); | 158 std::string raw_key = DecodeBase64Url(encoded_key); |
131 if (raw_key.empty()) { | 159 if (raw_key.empty()) { |
132 DVLOG(1) << "Invalid '" << kKeyTag << "' value: " << encoded_key; | 160 DVLOG(1) << "Invalid '" << kKeyTag << "' value: " << encoded_key; |
133 return false; | 161 return false; |
134 } | 162 } |
135 | 163 |
136 // Add the decoded key ID and the decoded key to the list. | 164 // Add the decoded key ID and the decoded key to the list. |
137 *jwk_key = std::make_pair(raw_key_id, raw_key); | 165 *jwk_key = std::make_pair(raw_key_id, raw_key); |
138 return true; | 166 return true; |
139 } | 167 } |
140 | 168 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
206 return true; | 234 return true; |
207 } | 235 } |
208 | 236 |
209 void CreateLicenseRequest(const uint8* key_id, | 237 void CreateLicenseRequest(const uint8* key_id, |
210 int key_id_length, | 238 int key_id_length, |
211 MediaKeys::SessionType session_type, | 239 MediaKeys::SessionType session_type, |
212 std::vector<uint8>* license) { | 240 std::vector<uint8>* license) { |
213 // Create the license request. | 241 // Create the license request. |
214 scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue()); | 242 scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue()); |
215 scoped_ptr<base::ListValue> list(new base::ListValue()); | 243 scoped_ptr<base::ListValue> list(new base::ListValue()); |
216 list->AppendString(EncodeBase64(key_id, key_id_length)); | 244 list->AppendString(EncodeBase64Url(key_id, key_id_length)); |
217 request->Set(kKeyIdsTag, list.release()); | 245 request->Set(kKeyIdsTag, list.release()); |
218 | 246 |
219 switch (session_type) { | 247 switch (session_type) { |
220 case MediaKeys::TEMPORARY_SESSION: | 248 case MediaKeys::TEMPORARY_SESSION: |
221 request->SetString(kTypeTag, kTemporarySession); | 249 request->SetString(kTypeTag, kTemporarySession); |
222 break; | 250 break; |
223 case MediaKeys::PERSISTENT_LICENSE_SESSION: | 251 case MediaKeys::PERSISTENT_LICENSE_SESSION: |
224 request->SetString(kTypeTag, kPersistentLicenseSession); | 252 request->SetString(kTypeTag, kPersistentLicenseSession); |
225 break; | 253 break; |
226 case MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION: | 254 case MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION: |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 DVLOG(1) << "Empty '" << kKeyIdsTag << "' list"; | 296 DVLOG(1) << "Empty '" << kKeyIdsTag << "' list"; |
269 return false; | 297 return false; |
270 } | 298 } |
271 | 299 |
272 std::string encoded_key; | 300 std::string encoded_key; |
273 if (!list_val->GetString(0, &encoded_key)) { | 301 if (!list_val->GetString(0, &encoded_key)) { |
274 DVLOG(1) << "First entry in '" << kKeyIdsTag << "' not a string"; | 302 DVLOG(1) << "First entry in '" << kKeyIdsTag << "' not a string"; |
275 return false; | 303 return false; |
276 } | 304 } |
277 | 305 |
278 std::string decoded_string = DecodeBase64(encoded_key); | 306 std::string decoded_string = DecodeBase64Url(encoded_key); |
279 if (decoded_string.empty()) { | 307 if (decoded_string.empty()) { |
280 DVLOG(1) << "Invalid '" << kKeyIdsTag << "' value: " << encoded_key; | 308 DVLOG(1) << "Invalid '" << kKeyIdsTag << "' value: " << encoded_key; |
281 return false; | 309 return false; |
282 } | 310 } |
283 | 311 |
284 std::vector<uint8> result(decoded_string.begin(), decoded_string.end()); | 312 std::vector<uint8> result(decoded_string.begin(), decoded_string.end()); |
285 first_key->swap(result); | 313 first_key->swap(result); |
286 return true; | 314 return true; |
287 } | 315 } |
288 | 316 |
289 } // namespace media | 317 } // namespace media |
OLD | NEW |