• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright (C) 2017 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "JsonAssetLoader"
19 
20 #include <media/stagefright/foundation/ABuffer.h>
21 #include <media/stagefright/foundation/AString.h>
22 #include <media/stagefright/foundation/base64.h>
23 #include <media/stagefright/MediaErrors.h>
24 #include <utils/Log.h>
25 
26 #include "JsonAssetLoader.h"
27 #include "protos/license_protos.pb.h"
28 
29 namespace android {
30 namespace clearkeycas {
31 
32 const String8 kIdTag("id");
33 const String8 kNameTag("name");
34 const String8 kLowerCaseOgranizationNameTag("lowercase_organization_name");
35 const String8 kEncryptionKeyTag("encryption_key");
36 const String8 kCasTypeTag("cas_type");
37 const String8 kBase64Padding("=");
38 
39 const uint32_t kKeyLength = 16;
40 
JsonAssetLoader()41 JsonAssetLoader::JsonAssetLoader() {
42 }
43 
~JsonAssetLoader()44 JsonAssetLoader::~JsonAssetLoader() {
45 }
46 
47 /*
48  * Extract a clear key asset from a JSON string.
49  *
50  * Returns OK if a clear key asset is extracted successfully,
51  * or ERROR_CAS_NO_LICENSE if the string doesn't contain a valid
52  * clear key asset.
53  */
extractAssetFromString(const String8 & jsonAssetString,Asset * asset)54 status_t JsonAssetLoader::extractAssetFromString(
55         const String8& jsonAssetString, Asset *asset) {
56     if (!parseJsonAssetString(jsonAssetString, &mJsonObjects)) {
57         return ERROR_CAS_NO_LICENSE;
58     }
59 
60     if (mJsonObjects.size() < 1) {
61         return ERROR_CAS_NO_LICENSE;
62     }
63 
64     if (!parseJsonObject(mJsonObjects[0], &mTokens))
65         return ERROR_CAS_NO_LICENSE;
66 
67     if (!findKey(mJsonObjects[0], asset)) {
68         return ERROR_CAS_NO_LICENSE;
69     }
70     return OK;
71 }
72 
73 //static
decodeBase64String(const String8 & encodedText)74 sp<ABuffer> JsonAssetLoader::decodeBase64String(const String8& encodedText) {
75     // Since android::decodeBase64() requires padding characters,
76     // add them so length of encodedText is exactly a multiple of 4.
77     int remainder = encodedText.length() % 4;
78     String8 paddedText(encodedText);
79     if (remainder > 0) {
80         for (int i = 0; i < 4 - remainder; ++i) {
81             paddedText.append(kBase64Padding);
82         }
83     }
84 
85     return decodeBase64(AString(paddedText.string()));
86 }
87 
findKey(const String8 & jsonObject,Asset * asset)88 bool JsonAssetLoader::findKey(const String8& jsonObject, Asset *asset) {
89 
90     String8 value;
91 
92     if (jsonObject.find(kIdTag) < 0) {
93         return false;
94     }
95     findValue(kIdTag, &value);
96     ALOGV("found %s=%s", kIdTag.string(), value.string());
97     asset->set_id(atoi(value.string()));
98 
99     if (jsonObject.find(kNameTag) < 0) {
100         return false;
101     }
102     findValue(kNameTag, &value);
103     ALOGV("found %s=%s", kNameTag.string(), value.string());
104     asset->set_name(value.string());
105 
106     if (jsonObject.find(kLowerCaseOgranizationNameTag) < 0) {
107         return false;
108     }
109     findValue(kLowerCaseOgranizationNameTag, &value);
110     ALOGV("found %s=%s", kLowerCaseOgranizationNameTag.string(), value.string());
111     asset->set_lowercase_organization_name(value.string());
112 
113     if (jsonObject.find(kCasTypeTag) < 0) {
114         return false;
115     }
116     findValue(kCasTypeTag, &value);
117     ALOGV("found %s=%s", kCasTypeTag.string(), value.string());
118     // Asset_CasType_CLEARKEY_CAS = 1
119     asset->set_cas_type((Asset_CasType)atoi(value.string()));
120 
121     return true;
122 }
123 
findValue(const String8 & key,String8 * value)124 void JsonAssetLoader::findValue(const String8 &key, String8* value) {
125     value->clear();
126     const char* valueToken;
127     for (Vector<String8>::const_iterator nextToken = mTokens.begin();
128         nextToken != mTokens.end(); ++nextToken) {
129         if (0 == (*nextToken).compare(key)) {
130             if (nextToken + 1 == mTokens.end())
131                 break;
132             valueToken = (*(nextToken + 1)).string();
133             value->setTo(valueToken);
134             nextToken++;
135             break;
136         }
137     }
138 }
139 
140 /*
141  * Parses a JSON objects string and initializes a vector of tokens.
142  *
143  * @return Returns false for errors, true for success.
144  */
parseJsonObject(const String8 & jsonObject,Vector<String8> * tokens)145 bool JsonAssetLoader::parseJsonObject(const String8& jsonObject,
146         Vector<String8>* tokens) {
147     jsmn_parser parser;
148 
149     jsmn_init(&parser);
150     int numTokens = jsmn_parse(&parser,
151         jsonObject.string(), jsonObject.size(), NULL, 0);
152     if (numTokens < 0) {
153         ALOGE("Parser returns error code=%d", numTokens);
154         return false;
155     }
156 
157     unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
158     mJsmnTokens.clear();
159     mJsmnTokens.setCapacity(jsmnTokensSize);
160 
161     jsmn_init(&parser);
162     int status = jsmn_parse(&parser, jsonObject.string(),
163         jsonObject.size(), mJsmnTokens.editArray(), numTokens);
164     if (status < 0) {
165         ALOGE("Parser returns error code=%d", status);
166         return false;
167     }
168 
169     tokens->clear();
170     String8 token;
171     const char *pjs;
172     ALOGV("numTokens: %d", numTokens);
173     for (int j = 0; j < numTokens; ++j) {
174         pjs = jsonObject.string() + mJsmnTokens[j].start;
175         if (mJsmnTokens[j].type == JSMN_STRING ||
176                 mJsmnTokens[j].type == JSMN_PRIMITIVE) {
177             token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start);
178             tokens->add(token);
179             ALOGV("add token: %s", token.string());
180         }
181     }
182     return true;
183 }
184 
185 /*
186  * Parses JSON asset string and initializes a vector of JSON objects.
187  *
188  * @return Returns false for errors, true for success.
189  */
parseJsonAssetString(const String8 & jsonAsset,Vector<String8> * jsonObjects)190 bool JsonAssetLoader::parseJsonAssetString(const String8& jsonAsset,
191         Vector<String8>* jsonObjects) {
192     if (jsonAsset.isEmpty()) {
193         ALOGE("Empty JSON Web Key");
194         return false;
195     }
196 
197     // The jsmn parser only supports unicode encoding.
198     jsmn_parser parser;
199 
200     // Computes number of tokens. A token marks the type, offset in
201     // the original string.
202     jsmn_init(&parser);
203     int numTokens = jsmn_parse(&parser,
204             jsonAsset.string(), jsonAsset.size(), NULL, 0);
205     if (numTokens < 0) {
206         ALOGE("Parser returns error code=%d", numTokens);
207         return false;
208     }
209 
210     unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t);
211     mJsmnTokens.setCapacity(jsmnTokensSize);
212 
213     jsmn_init(&parser);
214     int status = jsmn_parse(&parser, jsonAsset.string(),
215             jsonAsset.size(), mJsmnTokens.editArray(), numTokens);
216     if (status < 0) {
217         ALOGE("Parser returns error code=%d", status);
218         return false;
219     }
220 
221     String8 token;
222     const char *pjs;
223     for (int i = 0; i < numTokens; ++i) {
224         pjs = jsonAsset.string() + mJsmnTokens[i].start;
225         if (mJsmnTokens[i].type == JSMN_OBJECT) {
226             token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start);
227             jsonObjects->add(token);
228         }
229     }
230     return true;
231 }
232 
233 }  // namespace clearkeycas
234 }  // namespace android
235