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 JsonAssetLoader::JsonAssetLoader() { 40 } 41 42 JsonAssetLoader::~JsonAssetLoader() { 43 } 44 45 /* 46 * Extract a clear key asset from a JSON string. 47 * 48 * Returns OK if a clear key asset is extracted successfully, 49 * or ERROR_CAS_NO_LICENSE if the string doesn't contain a valid 50 * clear key asset. 51 */ 52 status_t JsonAssetLoader::extractAssetFromString( 53 const String8& jsonAssetString, Asset *asset) { 54 if (!parseJsonAssetString(jsonAssetString, &mJsonObjects)) { 55 return ERROR_CAS_NO_LICENSE; 56 } 57 58 if (mJsonObjects.size() < 1) { 59 return ERROR_CAS_NO_LICENSE; 60 } 61 62 if (!parseJsonObject(mJsonObjects[0], &mTokens)) 63 return ERROR_CAS_NO_LICENSE; 64 65 if (!findKey(mJsonObjects[0], asset)) { 66 return ERROR_CAS_NO_LICENSE; 67 } 68 return OK; 69 } 70 71 //static 72 sp<ABuffer> JsonAssetLoader::decodeBase64String(const String8& encodedText) { 73 // Since android::decodeBase64() requires padding characters, 74 // add them so length of encodedText is exactly a multiple of 4. 75 int remainder = encodedText.length() % 4; 76 String8 paddedText(encodedText); 77 if (remainder > 0) { 78 for (int i = 0; i < 4 - remainder; ++i) { 79 paddedText.append(kBase64Padding); 80 } 81 } 82 83 return decodeBase64(AString(paddedText.string())); 84 } 85 86 bool JsonAssetLoader::findKey(const String8& jsonObject, Asset *asset) { 87 88 String8 value; 89 90 if (jsonObject.find(kIdTag) < 0) { 91 return false; 92 } 93 findValue(kIdTag, &value); 94 ALOGV("found %s=%s", kIdTag.string(), value.string()); 95 asset->set_id(atoi(value.string())); 96 97 if (jsonObject.find(kNameTag) < 0) { 98 return false; 99 } 100 findValue(kNameTag, &value); 101 ALOGV("found %s=%s", kNameTag.string(), value.string()); 102 asset->set_name(value.string()); 103 104 if (jsonObject.find(kLowerCaseOgranizationNameTag) < 0) { 105 return false; 106 } 107 findValue(kLowerCaseOgranizationNameTag, &value); 108 ALOGV("found %s=%s", kLowerCaseOgranizationNameTag.string(), value.string()); 109 asset->set_lowercase_organization_name(value.string()); 110 111 if (jsonObject.find(kCasTypeTag) < 0) { 112 return false; 113 } 114 findValue(kCasTypeTag, &value); 115 ALOGV("found %s=%s", kCasTypeTag.string(), value.string()); 116 // Asset_CasType_CLEARKEY_CAS = 1 117 asset->set_cas_type((Asset_CasType)atoi(value.string())); 118 119 return true; 120 } 121 122 void JsonAssetLoader::findValue(const String8 &key, String8* value) { 123 value->clear(); 124 const char* valueToken; 125 for (Vector<String8>::const_iterator nextToken = mTokens.begin(); 126 nextToken != mTokens.end(); ++nextToken) { 127 if (0 == (*nextToken).compare(key)) { 128 if (nextToken + 1 == mTokens.end()) 129 break; 130 valueToken = (*(nextToken + 1)).string(); 131 value->setTo(valueToken); 132 nextToken++; 133 break; 134 } 135 } 136 } 137 138 /* 139 * Parses a JSON objects string and initializes a vector of tokens. 140 * 141 * @return Returns false for errors, true for success. 142 */ 143 bool JsonAssetLoader::parseJsonObject(const String8& jsonObject, 144 Vector<String8>* tokens) { 145 jsmn_parser parser; 146 147 jsmn_init(&parser); 148 int numTokens = jsmn_parse(&parser, 149 jsonObject.string(), jsonObject.size(), NULL, 0); 150 if (numTokens < 0) { 151 ALOGE("Parser returns error code=%d", numTokens); 152 return false; 153 } 154 155 unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); 156 mJsmnTokens.clear(); 157 mJsmnTokens.setCapacity(jsmnTokensSize); 158 159 jsmn_init(&parser); 160 int status = jsmn_parse(&parser, jsonObject.string(), 161 jsonObject.size(), mJsmnTokens.editArray(), numTokens); 162 if (status < 0) { 163 ALOGE("Parser returns error code=%d", status); 164 return false; 165 } 166 167 tokens->clear(); 168 String8 token; 169 const char *pjs; 170 ALOGV("numTokens: %d", numTokens); 171 for (int j = 0; j < numTokens; ++j) { 172 pjs = jsonObject.string() + mJsmnTokens[j].start; 173 if (mJsmnTokens[j].type == JSMN_STRING || 174 mJsmnTokens[j].type == JSMN_PRIMITIVE) { 175 token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start); 176 tokens->add(token); 177 ALOGV("add token: %s", token.string()); 178 } 179 } 180 return true; 181 } 182 183 /* 184 * Parses JSON asset string and initializes a vector of JSON objects. 185 * 186 * @return Returns false for errors, true for success. 187 */ 188 bool JsonAssetLoader::parseJsonAssetString(const String8& jsonAsset, 189 Vector<String8>* jsonObjects) { 190 if (jsonAsset.isEmpty()) { 191 ALOGE("Empty JSON Web Key"); 192 return false; 193 } 194 195 // The jsmn parser only supports unicode encoding. 196 jsmn_parser parser; 197 198 // Computes number of tokens. A token marks the type, offset in 199 // the original string. 200 jsmn_init(&parser); 201 int numTokens = jsmn_parse(&parser, 202 jsonAsset.string(), jsonAsset.size(), NULL, 0); 203 if (numTokens < 0) { 204 ALOGE("Parser returns error code=%d", numTokens); 205 return false; 206 } 207 208 unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); 209 mJsmnTokens.setCapacity(jsmnTokensSize); 210 211 jsmn_init(&parser); 212 int status = jsmn_parse(&parser, jsonAsset.string(), 213 jsonAsset.size(), mJsmnTokens.editArray(), numTokens); 214 if (status < 0) { 215 ALOGE("Parser returns error code=%d", status); 216 return false; 217 } 218 219 String8 token; 220 const char *pjs; 221 for (int i = 0; i < numTokens; ++i) { 222 pjs = jsonAsset.string() + mJsmnTokens[i].start; 223 if (mJsmnTokens[i].type == JSMN_OBJECT) { 224 token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start); 225 jsonObjects->add(token); 226 } 227 } 228 return true; 229 } 230 231 } // namespace clearkeycas 232 } // namespace android 233