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/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
17 const char kKeysTag
[] = "keys";
18 const char kKeyTypeTag
[] = "kty";
19 const char kSymmetricKeyValue
[] = "oct";
20 const char kKeyTag
[] = "k";
21 const char kKeyIdTag
[] = "kid";
22 const char kBase64Padding
= '=';
24 // Encodes |input| into a base64 string without padding.
25 static std::string
EncodeBase64(const uint8
* input
, int input_length
) {
26 std::string encoded_text
;
28 std::string(reinterpret_cast<const char*>(input
), input_length
),
31 // Remove any padding characters added by Base64Encode().
32 size_t found
= encoded_text
.find_last_not_of(kBase64Padding
);
33 if (found
!= std::string::npos
)
34 encoded_text
.erase(found
+ 1);
39 // Decodes an unpadded base64 string. Returns empty string on error.
40 static std::string
DecodeBase64(const std::string
& encoded_text
) {
41 // EME spec doesn't allow padding characters.
42 if (encoded_text
.find_first_of(kBase64Padding
) != std::string::npos
)
45 // Since base::Base64Decode() requires padding characters, add them so length
46 // of |encoded_text| is exactly a multiple of 4.
47 size_t num_last_grouping_chars
= encoded_text
.length() % 4;
48 std::string modified_text
= encoded_text
;
49 if (num_last_grouping_chars
> 0)
50 modified_text
.append(4 - num_last_grouping_chars
, kBase64Padding
);
52 std::string decoded_text
;
53 if (!base::Base64Decode(modified_text
, &decoded_text
))
59 std::string
GenerateJWKSet(const uint8
* key
, int key_length
,
60 const uint8
* key_id
, int key_id_length
) {
61 // Both |key| and |key_id| need to be base64 encoded strings in the JWK.
62 std::string key_base64
= EncodeBase64(key
, key_length
);
63 std::string key_id_base64
= EncodeBase64(key_id
, key_id_length
);
65 // Create the JWK, and wrap it into a JWK Set.
66 scoped_ptr
<base::DictionaryValue
> jwk(new base::DictionaryValue());
67 jwk
->SetString(kKeyTypeTag
, kSymmetricKeyValue
);
68 jwk
->SetString(kKeyTag
, key_base64
);
69 jwk
->SetString(kKeyIdTag
, key_id_base64
);
70 scoped_ptr
<base::ListValue
> list(new base::ListValue());
71 list
->Append(jwk
.release());
72 base::DictionaryValue jwk_set
;
73 jwk_set
.Set(kKeysTag
, list
.release());
75 // Finally serialize |jwk_set| into a string and return it.
76 std::string serialized_jwk
;
77 JSONStringValueSerializer
serializer(&serialized_jwk
);
78 serializer
.Serialize(jwk_set
);
79 return serialized_jwk
;
82 // Processes a JSON Web Key to extract the key id and key value. Sets |jwk_key|
83 // to the id/value pair and returns true on success.
84 static bool ConvertJwkToKeyPair(const base::DictionaryValue
& jwk
,
85 KeyIdAndKeyPair
* jwk_key
) {
86 // Have found a JWK, start by checking that it is a symmetric key.
88 if (!jwk
.GetString(kKeyTypeTag
, &type
) || type
!= kSymmetricKeyValue
) {
89 DVLOG(1) << "JWK is not a symmetric key";
93 // Get the key id and actual key parameters.
94 std::string encoded_key_id
;
95 std::string encoded_key
;
96 if (!jwk
.GetString(kKeyIdTag
, &encoded_key_id
)) {
97 DVLOG(1) << "Missing '" << kKeyIdTag
<< "' parameter";
100 if (!jwk
.GetString(kKeyTag
, &encoded_key
)) {
101 DVLOG(1) << "Missing '" << kKeyTag
<< "' parameter";
105 // Key ID and key are base64-encoded strings, so decode them.
106 std::string raw_key_id
= DecodeBase64(encoded_key_id
);
107 if (raw_key_id
.empty()) {
108 DVLOG(1) << "Invalid '" << kKeyIdTag
<< "' value: " << encoded_key_id
;
112 std::string raw_key
= DecodeBase64(encoded_key
);
113 if (raw_key
.empty()) {
114 DVLOG(1) << "Invalid '" << kKeyTag
<< "' value: " << encoded_key
;
118 // Add the decoded key ID and the decoded key to the list.
119 *jwk_key
= std::make_pair(raw_key_id
, raw_key
);
123 bool ExtractKeysFromJWKSet(const std::string
& jwk_set
, KeyIdAndKeyPairs
* keys
) {
124 if (!IsStringASCII(jwk_set
))
127 scoped_ptr
<base::Value
> root(base::JSONReader().ReadToValue(jwk_set
));
128 if (!root
.get() || root
->GetType() != base::Value::TYPE_DICTIONARY
)
131 // Locate the set from the dictionary.
132 base::DictionaryValue
* dictionary
=
133 static_cast<base::DictionaryValue
*>(root
.get());
134 base::ListValue
* list_val
= NULL
;
135 if (!dictionary
->GetList(kKeysTag
, &list_val
)) {
136 DVLOG(1) << "Missing '" << kKeysTag
137 << "' parameter or not a list in JWK Set";
141 // Create a local list of keys, so that |jwk_keys| only gets updated on
143 KeyIdAndKeyPairs local_keys
;
144 for (size_t i
= 0; i
< list_val
->GetSize(); ++i
) {
145 base::DictionaryValue
* jwk
= NULL
;
146 if (!list_val
->GetDictionary(i
, &jwk
)) {
147 DVLOG(1) << "Unable to access '" << kKeysTag
<< "'[" << i
151 KeyIdAndKeyPair key_pair
;
152 if (!ConvertJwkToKeyPair(*jwk
, &key_pair
)) {
153 DVLOG(1) << "Error from '" << kKeysTag
<< "'[" << i
<< "]";
156 local_keys
.push_back(key_pair
);
159 // Successfully processed all JWKs in the set.
160 keys
->swap(local_keys
);