• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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