JsonWebKey.cpp revision 1917fac1e41f57e547df7dcdebf57dd8483e7f61
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 24namespace { 25const std::string kKeysTag("keys"); 26const std::string kKeyTypeTag("kty"); 27const std::string kSymmetricKeyValue("oct"); 28const std::string kKeyTag("k"); 29const std::string kKeyIdTag("kid"); 30const std::string kBase64Padding("="); 31} 32 33namespace android { 34namespace hardware { 35namespace drm { 36namespace V1_1 { 37namespace clearkey { 38 39JsonWebKey::JsonWebKey() { 40} 41 42JsonWebKey::~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 */ 52bool 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 103bool 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 133bool 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 155void 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 171bool 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 */ 184bool 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 */ 227bool 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