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