1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #define LOG_TAG "hidl_JsonWebKey"
17
18 #include <utils/Log.h>
19
20 #include "JsonWebKey.h"
21
22 #include "Base64.h"
23
24 namespace {
25 const std::string kKeysTag("keys");
26 const std::string kKeyTypeTag("kty");
27 const std::string kSymmetricKeyValue("oct");
28 const std::string kKeyTag("k");
29 const std::string kKeyIdTag("kid");
30 const std::string kBase64Padding("=");
31 }
32
33 namespace android {
34 namespace hardware {
35 namespace drm {
36 namespace V1_1 {
37 namespace clearkey {
38
JsonWebKey()39 JsonWebKey::JsonWebKey() {
40 }
41
~JsonWebKey()42 JsonWebKey::~JsonWebKey() {
43 }
44
45 /*
46 * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key
47 * pairs from the JSON Web Key Set. Both key ids and keys are base64url
48 * encoded. The KeyMap contains base64url decoded key id:key pairs.
49 *
50 * @return Returns false for errors, true for success.
51 */
extractKeysFromJsonWebKeySet(const std::string & jsonWebKeySet,KeyMap * keys)52 bool JsonWebKey::extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet,
53 KeyMap* keys) {
54
55 keys->clear();
56
57 if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) {
58 return false;
59 }
60
61 // mJsonObjects[0] contains the entire JSON Web Key Set, including
62 // all the base64 encoded keys. Each key is also stored separately as
63 // a JSON object in mJsonObjects[1..n] where n is the total
64 // number of keys in the set.
65 if (!isJsonWebKeySet(mJsonObjects[0])) {
66 return false;
67 }
68
69 std::string encodedKey, encodedKeyId;
70 std::vector<uint8_t> decodedKey, decodedKeyId;
71
72 // mJsonObjects[1] contains the first JSON Web Key in the set
73 for (size_t i = 1; i < mJsonObjects.size(); ++i) {
74 encodedKeyId.clear();
75 encodedKey.clear();
76
77 if (!parseJsonObject(mJsonObjects[i], &mTokens))
78 return false;
79
80 if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) {
81 if (encodedKeyId.empty() || encodedKey.empty()) {
82 ALOGE("Must have both key id and key in the JsonWebKey set.");
83 continue;
84 }
85
86 if (!decodeBase64String(encodedKeyId, &decodedKeyId)) {
87 ALOGE("Failed to decode key id(%s)", encodedKeyId.c_str());
88 continue;
89 }
90
91 if (!decodeBase64String(encodedKey, &decodedKey)) {
92 ALOGE("Failed to decode key(%s)", encodedKey.c_str());
93 continue;
94 }
95
96 keys->insert(std::pair<std::vector<uint8_t>,
97 std::vector<uint8_t> >(decodedKeyId, decodedKey));
98 }
99 }
100 return true;
101 }
102
decodeBase64String(const std::string & encodedText,std::vector<uint8_t> * decodedText)103 bool JsonWebKey::decodeBase64String(const std::string& encodedText,
104 std::vector<uint8_t>* decodedText) {
105
106 decodedText->clear();
107
108 // encodedText should not contain padding characters as per EME spec.
109 if (encodedText.find(kBase64Padding) != std::string::npos) {
110 return false;
111 }
112
113 // Since decodeBase64() requires padding characters,
114 // add them so length of encodedText is exactly a multiple of 4.
115 int remainder = encodedText.length() % 4;
116 std::string paddedText(encodedText);
117 if (remainder > 0) {
118 for (int i = 0; i < 4 - remainder; ++i) {
119 paddedText.append(kBase64Padding);
120 }
121 }
122
123 sp<Buffer> buffer = decodeBase64(paddedText);
124 if (buffer == nullptr) {
125 ALOGE("Malformed base64 encoded content found.");
126 return false;
127 }
128
129 decodedText->insert(decodedText->end(), buffer->base(), buffer->base() + buffer->size());
130 return true;
131 }
132
findKey(const std::string & jsonObject,std::string * keyId,std::string * encodedKey)133 bool JsonWebKey::findKey(const std::string& jsonObject, std::string* keyId,
134 std::string* encodedKey) {
135
136 std::string key, value;
137
138 // Only allow symmetric key, i.e. "kty":"oct" pair.
139 if (jsonObject.find(kKeyTypeTag) != std::string::npos) {
140 findValue(kKeyTypeTag, &value);
141 if (0 != value.compare(kSymmetricKeyValue))
142 return false;
143 }
144
145 if (jsonObject.find(kKeyIdTag) != std::string::npos) {
146 findValue(kKeyIdTag, keyId);
147 }
148
149 if (jsonObject.find(kKeyTag) != std::string::npos) {
150 findValue(kKeyTag, encodedKey);
151 }
152 return true;
153 }
154
findValue(const std::string & key,std::string * value)155 void JsonWebKey::findValue(const std::string &key, std::string* value) {
156 value->clear();
157 const char* valueToken;
158 for (std::vector<std::string>::const_iterator nextToken = mTokens.begin();
159 nextToken != mTokens.end(); ++nextToken) {
160 if (0 == (*nextToken).compare(key)) {
161 if (nextToken + 1 == mTokens.end())
162 break;
163 valueToken = (*(nextToken + 1)).c_str();
164 value->assign(valueToken);
165 nextToken++;
166 break;
167 }
168 }
169 }
170
isJsonWebKeySet(const std::string & jsonObject) const171 bool JsonWebKey::isJsonWebKeySet(const std::string& jsonObject) const {
172 if (jsonObject.find(kKeysTag) == std::string::npos) {
173 ALOGE("JSON Web Key does not contain keys.");
174 return false;
175 }
176 return true;
177 }
178
179 /*
180 * Parses a JSON objects string and initializes a vector of tokens.
181 *
182 * @return Returns false for errors, true for success.
183 */
parseJsonObject(const std::string & jsonObject,std::vector<std::string> * tokens)184 bool JsonWebKey::parseJsonObject(const std::string& jsonObject,
185 std::vector<std::string>* tokens) {
186 jsmn_parser parser;
187
188 jsmn_init(&parser);
189 int numTokens = jsmn_parse(&parser,
190 jsonObject.c_str(), jsonObject.size(), nullptr, 0);
191 if (numTokens < 0) {
192 ALOGE("Parser returns error code=%d", numTokens);
193 return false;
194 }
195
196 unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
197 mJsmnTokens.clear();
198 mJsmnTokens.resize(jsmnTokensSize);
199
200 jsmn_init(&parser);
201 int status = jsmn_parse(&parser, jsonObject.c_str(),
202 jsonObject.size(), mJsmnTokens.data(), numTokens);
203 if (status < 0) {
204 ALOGE("Parser returns error code=%d", status);
205 return false;
206 }
207
208 tokens->clear();
209 std::string token;
210 const char *pjs;
211 for (int j = 0; j < numTokens; ++j) {
212 pjs = jsonObject.c_str() + mJsmnTokens[j].start;
213 if (mJsmnTokens[j].type == JSMN_STRING ||
214 mJsmnTokens[j].type == JSMN_PRIMITIVE) {
215 token.assign(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start);
216 tokens->push_back(token);
217 }
218 }
219 return true;
220 }
221
222 /*
223 * Parses JSON Web Key Set string and initializes a vector of JSON objects.
224 *
225 * @return Returns false for errors, true for success.
226 */
parseJsonWebKeySet(const std::string & jsonWebKeySet,std::vector<std::string> * jsonObjects)227 bool JsonWebKey::parseJsonWebKeySet(const std::string& jsonWebKeySet,
228 std::vector<std::string>* jsonObjects) {
229 if (jsonWebKeySet.empty()) {
230 ALOGE("Empty JSON Web Key");
231 return false;
232 }
233
234 // The jsmn parser only supports unicode encoding.
235 jsmn_parser parser;
236
237 // Computes number of tokens. A token marks the type, offset in
238 // the original string.
239 jsmn_init(&parser);
240 int numTokens = jsmn_parse(&parser,
241 jsonWebKeySet.c_str(), jsonWebKeySet.size(), nullptr, 0);
242 if (numTokens < 0) {
243 ALOGE("Parser returns error code=%d", numTokens);
244 return false;
245 }
246
247 unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
248 mJsmnTokens.resize(jsmnTokensSize);
249
250 jsmn_init(&parser);
251 int status = jsmn_parse(&parser, jsonWebKeySet.c_str(),
252 jsonWebKeySet.size(), mJsmnTokens.data(), numTokens);
253 if (status < 0) {
254 ALOGE("Parser returns error code=%d", status);
255 return false;
256 }
257
258 std::string token;
259 const char *pjs;
260 for (int i = 0; i < numTokens; ++i) {
261 pjs = jsonWebKeySet.c_str() + mJsmnTokens[i].start;
262 if (mJsmnTokens[i].type == JSMN_OBJECT) {
263 token.assign(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start);
264 jsonObjects->push_back(token);
265 }
266 }
267 return true;
268 }
269
270 } // namespace clearkey
271 } // namespace V1_1
272 } // namespace drm
273 } // namespace hardware
274 } // namespace android
275
276