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 41 JsonAssetLoader::JsonAssetLoader() { 42 } 43 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 */ 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 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 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 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 */ 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 */ 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