Cleanup: Make TogglePinnedToStartScreen() Windows only.
[chromium-blink-merge.git] / media / cdm / json_web_key.cc
blob03a8a1a7055beb6bb4b00e8da0a859b155f6a5bc
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.
5 #include "media/cdm/json_web_key.h"
7 #include "base/base64.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_string_value_serializer.h"
10 #include "base/json/string_escape.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
16 namespace media {
18 const char kKeysTag[] = "keys";
19 const char kKeyTypeTag[] = "kty";
20 const char kSymmetricKeyValue[] = "oct";
21 const char kKeyTag[] = "k";
22 const char kKeyIdTag[] = "kid";
23 const char kKeyIdsTag[] = "kids";
24 const char kBase64Padding = '=';
25 const char kTypeTag[] = "type";
26 const char kPersistentType[] = "persistent";
27 const char kTemporaryType[] = "temporary";
29 // Encodes |input| into a base64 string without padding.
30 static std::string EncodeBase64(const uint8* input, int input_length) {
31 std::string encoded_text;
32 base::Base64Encode(
33 std::string(reinterpret_cast<const char*>(input), input_length),
34 &encoded_text);
36 // Remove any padding characters added by Base64Encode().
37 size_t found = encoded_text.find_last_not_of(kBase64Padding);
38 if (found != std::string::npos)
39 encoded_text.erase(found + 1);
41 return encoded_text;
44 // Decodes an unpadded base64 string. Returns empty string on error.
45 static std::string DecodeBase64(const std::string& encoded_text) {
46 // EME spec doesn't allow padding characters.
47 if (encoded_text.find_first_of(kBase64Padding) != std::string::npos)
48 return std::string();
50 // Since base::Base64Decode() requires padding characters, add them so length
51 // of |encoded_text| is exactly a multiple of 4.
52 size_t num_last_grouping_chars = encoded_text.length() % 4;
53 std::string modified_text = encoded_text;
54 if (num_last_grouping_chars > 0)
55 modified_text.append(4 - num_last_grouping_chars, kBase64Padding);
57 std::string decoded_text;
58 if (!base::Base64Decode(modified_text, &decoded_text))
59 return std::string();
61 return decoded_text;
64 std::string GenerateJWKSet(const uint8* key, int key_length,
65 const uint8* key_id, int key_id_length) {
66 // Both |key| and |key_id| need to be base64 encoded strings in the JWK.
67 std::string key_base64 = EncodeBase64(key, key_length);
68 std::string key_id_base64 = EncodeBase64(key_id, key_id_length);
70 // Create the JWK, and wrap it into a JWK Set.
71 scoped_ptr<base::DictionaryValue> jwk(new base::DictionaryValue());
72 jwk->SetString(kKeyTypeTag, kSymmetricKeyValue);
73 jwk->SetString(kKeyTag, key_base64);
74 jwk->SetString(kKeyIdTag, key_id_base64);
75 scoped_ptr<base::ListValue> list(new base::ListValue());
76 list->Append(jwk.release());
77 base::DictionaryValue jwk_set;
78 jwk_set.Set(kKeysTag, list.release());
80 // Finally serialize |jwk_set| into a string and return it.
81 std::string serialized_jwk;
82 JSONStringValueSerializer serializer(&serialized_jwk);
83 serializer.Serialize(jwk_set);
84 return serialized_jwk;
87 // Processes a JSON Web Key to extract the key id and key value. Sets |jwk_key|
88 // to the id/value pair and returns true on success.
89 static bool ConvertJwkToKeyPair(const base::DictionaryValue& jwk,
90 KeyIdAndKeyPair* jwk_key) {
91 // Have found a JWK, start by checking that it is a symmetric key.
92 std::string type;
93 if (!jwk.GetString(kKeyTypeTag, &type) || type != kSymmetricKeyValue) {
94 DVLOG(1) << "JWK is not a symmetric key";
95 return false;
98 // Get the key id and actual key parameters.
99 std::string encoded_key_id;
100 std::string encoded_key;
101 if (!jwk.GetString(kKeyIdTag, &encoded_key_id)) {
102 DVLOG(1) << "Missing '" << kKeyIdTag << "' parameter";
103 return false;
105 if (!jwk.GetString(kKeyTag, &encoded_key)) {
106 DVLOG(1) << "Missing '" << kKeyTag << "' parameter";
107 return false;
110 // Key ID and key are base64-encoded strings, so decode them.
111 std::string raw_key_id = DecodeBase64(encoded_key_id);
112 if (raw_key_id.empty()) {
113 DVLOG(1) << "Invalid '" << kKeyIdTag << "' value: " << encoded_key_id;
114 return false;
117 std::string raw_key = DecodeBase64(encoded_key);
118 if (raw_key.empty()) {
119 DVLOG(1) << "Invalid '" << kKeyTag << "' value: " << encoded_key;
120 return false;
123 // Add the decoded key ID and the decoded key to the list.
124 *jwk_key = std::make_pair(raw_key_id, raw_key);
125 return true;
128 bool ExtractKeysFromJWKSet(const std::string& jwk_set,
129 KeyIdAndKeyPairs* keys,
130 MediaKeys::SessionType* session_type) {
131 if (!base::IsStringASCII(jwk_set))
132 return false;
134 scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(jwk_set));
135 if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY)
136 return false;
138 // Locate the set from the dictionary.
139 base::DictionaryValue* dictionary =
140 static_cast<base::DictionaryValue*>(root.get());
141 base::ListValue* list_val = NULL;
142 if (!dictionary->GetList(kKeysTag, &list_val)) {
143 DVLOG(1) << "Missing '" << kKeysTag
144 << "' parameter or not a list in JWK Set";
145 return false;
148 // Create a local list of keys, so that |jwk_keys| only gets updated on
149 // success.
150 KeyIdAndKeyPairs local_keys;
151 for (size_t i = 0; i < list_val->GetSize(); ++i) {
152 base::DictionaryValue* jwk = NULL;
153 if (!list_val->GetDictionary(i, &jwk)) {
154 DVLOG(1) << "Unable to access '" << kKeysTag << "'[" << i
155 << "] in JWK Set";
156 return false;
158 KeyIdAndKeyPair key_pair;
159 if (!ConvertJwkToKeyPair(*jwk, &key_pair)) {
160 DVLOG(1) << "Error from '" << kKeysTag << "'[" << i << "]";
161 return false;
163 local_keys.push_back(key_pair);
166 // Successfully processed all JWKs in the set. Now check if "type" is
167 // specified.
168 base::Value* value = NULL;
169 std::string type_id;
170 if (!dictionary->Get(kTypeTag, &value)) {
171 // Not specified, so use the default type.
172 *session_type = MediaKeys::TEMPORARY_SESSION;
173 } else if (!value->GetAsString(&type_id)) {
174 DVLOG(1) << "Invalid '" << kTypeTag << "' value";
175 return false;
176 } else if (type_id == kPersistentType) {
177 *session_type = MediaKeys::PERSISTENT_SESSION;
178 } else if (type_id == kTemporaryType) {
179 *session_type = MediaKeys::TEMPORARY_SESSION;
180 } else {
181 DVLOG(1) << "Invalid '" << kTypeTag << "' value: " << type_id;
182 return false;
185 // All done.
186 keys->swap(local_keys);
187 return true;
190 void CreateLicenseRequest(const uint8* key_id,
191 int key_id_length,
192 MediaKeys::SessionType session_type,
193 std::vector<uint8>* license) {
194 // Create the license request.
195 scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue());
196 scoped_ptr<base::ListValue> list(new base::ListValue());
197 list->AppendString(EncodeBase64(key_id, key_id_length));
198 request->Set(kKeyIdsTag, list.release());
200 switch (session_type) {
201 case MediaKeys::TEMPORARY_SESSION:
202 request->SetString(kTypeTag, kTemporaryType);
203 break;
204 case MediaKeys::PERSISTENT_SESSION:
205 request->SetString(kTypeTag, kPersistentType);
206 break;
209 // Serialize the license request as a string.
210 std::string json;
211 JSONStringValueSerializer serializer(&json);
212 serializer.Serialize(*request);
214 // Convert the serialized license request into std::vector and return it.
215 std::vector<uint8> result(json.begin(), json.end());
216 license->swap(result);
219 bool ExtractFirstKeyIdFromLicenseRequest(const std::vector<uint8>& license,
220 std::vector<uint8>* first_key) {
221 const std::string license_as_str(
222 reinterpret_cast<const char*>(!license.empty() ? &license[0] : NULL),
223 license.size());
224 if (!base::IsStringASCII(license_as_str))
225 return false;
227 scoped_ptr<base::Value> root(base::JSONReader().ReadToValue(license_as_str));
228 if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY)
229 return false;
231 // Locate the set from the dictionary.
232 base::DictionaryValue* dictionary =
233 static_cast<base::DictionaryValue*>(root.get());
234 base::ListValue* list_val = NULL;
235 if (!dictionary->GetList(kKeyIdsTag, &list_val)) {
236 DVLOG(1) << "Missing '" << kKeyIdsTag << "' parameter or not a list";
237 return false;
240 // Get the first key.
241 if (list_val->GetSize() < 1) {
242 DVLOG(1) << "Empty '" << kKeyIdsTag << "' list";
243 return false;
246 std::string encoded_key;
247 if (!list_val->GetString(0, &encoded_key)) {
248 DVLOG(1) << "First entry in '" << kKeyIdsTag << "' not a string";
249 return false;
252 std::string decoded_string = DecodeBase64(encoded_key);
253 if (decoded_string.empty()) {
254 DVLOG(1) << "Invalid '" << kKeyIdsTag << "' value: " << encoded_key;
255 return false;
258 std::vector<uint8> result(decoded_string.begin(), decoded_string.end());
259 first_key->swap(result);
260 return true;
263 } // namespace media