Chromium Code Reviews| 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[] = "+"; | |
|
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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |