Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: media/cdm/json_web_key.cc

Issue 934423002: Encode JSON key IDs and keys as base64url (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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[] = "+";
ddorwin 2015/02/19 04:45:56 Do these need to be strings instead of a char? I g
jrummell 2015/02/19 18:20:07 Correct. ReplaceChars needs it.
28 const char kBase64UrlMinus[] = "-";
ddorwin 2015/02/19 04:45:56 s/Minus/PlusReplacement/ We can read the string t
jrummell 2015/02/19 18:20:07 Done.
29 const char kBase64Slash[] = "/";
30 const char kBase64UrlUnderscore[] = "_";
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 also means the characters '-' and '_' must be used
ddorwin 2015/02/19 04:45:56 nit: drop "also "
jrummell 2015/02/19 18:20:06 Done.
49 // instead of '+' and '/', respectively.
50 base::ReplaceChars(encoded_text, kBase64Plus, kBase64UrlMinus, &encoded_text);
51 base::ReplaceChars(encoded_text, kBase64Slash, kBase64UrlUnderscore,
52 &encoded_text);
53
44 return encoded_text; 54 return encoded_text;
45 } 55 }
46 56
47 // Decodes an unpadded base64 string. Returns empty string on error. 57 // Decodes a base64url string. Returns empty string on error.
48 static std::string DecodeBase64(const std::string& encoded_text) { 58 static std::string DecodeBase64Url(const std::string& encoded_text) {
49 // EME spec doesn't allow padding characters. 59 // EME spec doesn't allow padding characters.
50 if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) { 60 if (encoded_text.find_first_of(kBase64Padding) != std::string::npos) {
51 DVLOG(1) << "Padding characters not allowed: " << encoded_text; 61 DVLOG(1) << "Padding characters not allowed: " << encoded_text;
52 return std::string(); 62 return std::string();
53 } 63 }
64 // TODO(jrummell): Enable once blink tests updated to use base64url encoding.
65 //if (encoded_text.find(kBase64Plus) != std::string::npos) {
ddorwin 2015/02/19 04:45:56 You can use find_first_of() to search for both at
jrummell 2015/02/19 18:20:07 Acknowledged.
66 // DVLOG(1) << "Base64 '+' characters not allowed: " << encoded_text;
67 // return std::string();
68 //}
69 //if (encoded_text.find(kBase64Slash) != std::string::npos) {
70 // DVLOG(1) << "Base64 '/' characters not allowed: " << encoded_text;
71 // return std::string();
72 //}
jrummell 2015/02/19 03:59:34 Will enable this once layout tests are fixed.
54 73
55 // Since base::Base64Decode() requires padding characters, add them so length 74 // Since base::Base64Decode() requires padding characters, add them so length
56 // of |encoded_text| is exactly a multiple of 4. 75 // of |encoded_text| is exactly a multiple of 4.
57 size_t num_last_grouping_chars = encoded_text.length() % 4; 76 size_t num_last_grouping_chars = encoded_text.length() % 4;
58 std::string modified_text = encoded_text; 77 std::string modified_text = encoded_text;
59 if (num_last_grouping_chars > 0) 78 if (num_last_grouping_chars > 0)
60 modified_text.append(4 - num_last_grouping_chars, kBase64Padding); 79 modified_text.append(4 - num_last_grouping_chars, kBase64Padding);
61 80
81 // base64url encoding also means the characters '-' and '_' must be used
ddorwin 2015/02/19 04:45:56 ditto
jrummell 2015/02/19 18:20:06 Done.
82 // instead of '+' and '/', respectively, so replace them before calling
83 // base::Base64Decode().
84 base::ReplaceChars(modified_text, kBase64UrlMinus, kBase64Plus,
85 &modified_text);
86 base::ReplaceChars(modified_text, kBase64UrlUnderscore, kBase64Slash,
87 &modified_text);
88
62 std::string decoded_text; 89 std::string decoded_text;
63 if (!base::Base64Decode(modified_text, &decoded_text)) { 90 if (!base::Base64Decode(modified_text, &decoded_text)) {
64 DVLOG(1) << "Base64 decoding failed on: " << modified_text; 91 DVLOG(1) << "Base64 decoding failed on: " << modified_text;
65 return std::string(); 92 return std::string();
66 } 93 }
67 94
68 return decoded_text; 95 return decoded_text;
69 } 96 }
70 97
71 std::string GenerateJWKSet(const uint8* key, int key_length, 98 std::string GenerateJWKSet(const uint8* key, int key_length,
72 const uint8* key_id, int key_id_length) { 99 const uint8* key_id, int key_id_length) {
73 // Both |key| and |key_id| need to be base64 encoded strings in the JWK. 100 // Both |key| and |key_id| need to be base64 encoded strings in the JWK.
74 std::string key_base64 = EncodeBase64(key, key_length); 101 std::string key_base64 = EncodeBase64Url(key, key_length);
75 std::string key_id_base64 = EncodeBase64(key_id, key_id_length); 102 std::string key_id_base64 = EncodeBase64Url(key_id, key_id_length);
76 103
77 // Create the JWK, and wrap it into a JWK Set. 104 // Create the JWK, and wrap it into a JWK Set.
78 scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue()); 105 scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue());
79 jwk->SetString(kKeyTypeTag, kKeyTypeOct); 106 jwk->SetString(kKeyTypeTag, kKeyTypeOct);
80 jwk->SetString(kAlgTag, kAlgA128KW); 107 jwk->SetString(kAlgTag, kAlgA128KW);
81 jwk->SetString(kKeyTag, key_base64); 108 jwk->SetString(kKeyTag, key_base64);
82 jwk->SetString(kKeyIdTag, key_id_base64); 109 jwk->SetString(kKeyIdTag, key_id_base64);
83 scoped_ptr<base::ListValue> list(new base::ListValue()); 110 scoped_ptr<base::ListValue> list(new base::ListValue());
84 list->Append(jwk.release()); 111 list->Append(jwk.release());
85 base::DictionaryValue jwk_set; 112 base::DictionaryValue jwk_set;
(...skipping 28 matching lines...) Expand all
114 if (!jwk.GetString(kKeyIdTag, &encoded_key_id)) { 141 if (!jwk.GetString(kKeyIdTag, &encoded_key_id)) {
115 DVLOG(1) << "Missing '" << kKeyIdTag << "' parameter"; 142 DVLOG(1) << "Missing '" << kKeyIdTag << "' parameter";
116 return false; 143 return false;
117 } 144 }
118 if (!jwk.GetString(kKeyTag, &encoded_key)) { 145 if (!jwk.GetString(kKeyTag, &encoded_key)) {
119 DVLOG(1) << "Missing '" << kKeyTag << "' parameter"; 146 DVLOG(1) << "Missing '" << kKeyTag << "' parameter";
120 return false; 147 return false;
121 } 148 }
122 149
123 // Key ID and key are base64-encoded strings, so decode them. 150 // Key ID and key are base64-encoded strings, so decode them.
124 std::string raw_key_id = DecodeBase64(encoded_key_id); 151 std::string raw_key_id = DecodeBase64Url(encoded_key_id);
125 if (raw_key_id.empty()) { 152 if (raw_key_id.empty()) {
126 DVLOG(1) << "Invalid '" << kKeyIdTag << "' value: " << encoded_key_id; 153 DVLOG(1) << "Invalid '" << kKeyIdTag << "' value: " << encoded_key_id;
127 return false; 154 return false;
128 } 155 }
129 156
130 std::string raw_key = DecodeBase64(encoded_key); 157 std::string raw_key = DecodeBase64Url(encoded_key);
131 if (raw_key.empty()) { 158 if (raw_key.empty()) {
132 DVLOG(1) << "Invalid '" << kKeyTag << "' value: " << encoded_key; 159 DVLOG(1) << "Invalid '" << kKeyTag << "' value: " << encoded_key;
133 return false; 160 return false;
134 } 161 }
135 162
136 // Add the decoded key ID and the decoded key to the list. 163 // Add the decoded key ID and the decoded key to the list.
137 *jwk_key = std::make_pair(raw_key_id, raw_key); 164 *jwk_key = std::make_pair(raw_key_id, raw_key);
138 return true; 165 return true;
139 } 166 }
140 167
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 return true; 233 return true;
207 } 234 }
208 235
209 void CreateLicenseRequest(const uint8* key_id, 236 void CreateLicenseRequest(const uint8* key_id,
210 int key_id_length, 237 int key_id_length,
211 MediaKeys::SessionType session_type, 238 MediaKeys::SessionType session_type,
212 std::vector<uint8>* license) { 239 std::vector<uint8>* license) {
213 // Create the license request. 240 // Create the license request.
214 scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue()); 241 scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue());
215 scoped_ptr<base::ListValue> list(new base::ListValue()); 242 scoped_ptr<base::ListValue> list(new base::ListValue());
216 list->AppendString(EncodeBase64(key_id, key_id_length)); 243 list->AppendString(EncodeBase64Url(key_id, key_id_length));
217 request->Set(kKeyIdsTag, list.release()); 244 request->Set(kKeyIdsTag, list.release());
218 245
219 switch (session_type) { 246 switch (session_type) {
220 case MediaKeys::TEMPORARY_SESSION: 247 case MediaKeys::TEMPORARY_SESSION:
221 request->SetString(kTypeTag, kTemporarySession); 248 request->SetString(kTypeTag, kTemporarySession);
222 break; 249 break;
223 case MediaKeys::PERSISTENT_LICENSE_SESSION: 250 case MediaKeys::PERSISTENT_LICENSE_SESSION:
224 request->SetString(kTypeTag, kPersistentLicenseSession); 251 request->SetString(kTypeTag, kPersistentLicenseSession);
225 break; 252 break;
226 case MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION: 253 case MediaKeys::PERSISTENT_RELEASE_MESSAGE_SESSION:
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 DVLOG(1) << "Empty '" << kKeyIdsTag << "' list"; 295 DVLOG(1) << "Empty '" << kKeyIdsTag << "' list";
269 return false; 296 return false;
270 } 297 }
271 298
272 std::string encoded_key; 299 std::string encoded_key;
273 if (!list_val->GetString(0, &encoded_key)) { 300 if (!list_val->GetString(0, &encoded_key)) {
274 DVLOG(1) << "First entry in '" << kKeyIdsTag << "' not a string"; 301 DVLOG(1) << "First entry in '" << kKeyIdsTag << "' not a string";
275 return false; 302 return false;
276 } 303 }
277 304
278 std::string decoded_string = DecodeBase64(encoded_key); 305 std::string decoded_string = DecodeBase64Url(encoded_key);
279 if (decoded_string.empty()) { 306 if (decoded_string.empty()) {
280 DVLOG(1) << "Invalid '" << kKeyIdsTag << "' value: " << encoded_key; 307 DVLOG(1) << "Invalid '" << kKeyIdsTag << "' value: " << encoded_key;
281 return false; 308 return false;
282 } 309 }
283 310
284 std::vector<uint8> result(decoded_string.begin(), decoded_string.end()); 311 std::vector<uint8> result(decoded_string.begin(), decoded_string.end());
285 first_key->swap(result); 312 first_key->swap(result);
286 return true; 313 return true;
287 } 314 }
288 315
289 } // namespace media 316 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698