| 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 |