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