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"
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
;
33 std::string(reinterpret_cast<const char*>(input
), input_length
),
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);
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
)
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
))
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.
93 if (!jwk
.GetString(kKeyTypeTag
, &type
) || type
!= kSymmetricKeyValue
) {
94 DVLOG(1) << "JWK is not a symmetric key";
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";
105 if (!jwk
.GetString(kKeyTag
, &encoded_key
)) {
106 DVLOG(1) << "Missing '" << kKeyTag
<< "' parameter";
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
;
117 std::string raw_key
= DecodeBase64(encoded_key
);
118 if (raw_key
.empty()) {
119 DVLOG(1) << "Invalid '" << kKeyTag
<< "' value: " << encoded_key
;
123 // Add the decoded key ID and the decoded key to the list.
124 *jwk_key
= std::make_pair(raw_key_id
, raw_key
);
128 bool ExtractKeysFromJWKSet(const std::string
& jwk_set
,
129 KeyIdAndKeyPairs
* keys
,
130 MediaKeys::SessionType
* session_type
) {
131 if (!base::IsStringASCII(jwk_set
))
134 scoped_ptr
<base::Value
> root(base::JSONReader().ReadToValue(jwk_set
));
135 if (!root
.get() || root
->GetType() != base::Value::TYPE_DICTIONARY
)
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";
148 // Create a local list of keys, so that |jwk_keys| only gets updated on
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
158 KeyIdAndKeyPair key_pair
;
159 if (!ConvertJwkToKeyPair(*jwk
, &key_pair
)) {
160 DVLOG(1) << "Error from '" << kKeysTag
<< "'[" << i
<< "]";
163 local_keys
.push_back(key_pair
);
166 // Successfully processed all JWKs in the set. Now check if "type" is
168 base::Value
* value
= NULL
;
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";
176 } else if (type_id
== kPersistentType
) {
177 *session_type
= MediaKeys::PERSISTENT_SESSION
;
178 } else if (type_id
== kTemporaryType
) {
179 *session_type
= MediaKeys::TEMPORARY_SESSION
;
181 DVLOG(1) << "Invalid '" << kTypeTag
<< "' value: " << type_id
;
186 keys
->swap(local_keys
);
190 void CreateLicenseRequest(const uint8
* key_id
,
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
);
204 case MediaKeys::PERSISTENT_SESSION
:
205 request
->SetString(kTypeTag
, kPersistentType
);
209 // Serialize the license request as a string.
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
),
224 if (!base::IsStringASCII(license_as_str
))
227 scoped_ptr
<base::Value
> root(base::JSONReader().ReadToValue(license_as_str
));
228 if (!root
.get() || root
->GetType() != base::Value::TYPE_DICTIONARY
)
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";
240 // Get the first key.
241 if (list_val
->GetSize() < 1) {
242 DVLOG(1) << "Empty '" << kKeyIdsTag
<< "' list";
246 std::string encoded_key
;
247 if (!list_val
->GetString(0, &encoded_key
)) {
248 DVLOG(1) << "First entry in '" << kKeyIdsTag
<< "' not a string";
252 std::string decoded_string
= DecodeBase64(encoded_key
);
253 if (decoded_string
.empty()) {
254 DVLOG(1) << "Invalid '" << kKeyIdsTag
<< "' value: " << encoded_key
;
258 std::vector
<uint8
> result(decoded_string
.begin(), decoded_string
.end());
259 first_key
->swap(result
);