diff options
| author | Edwin Wong <edwinwong@google.com> | 2014-05-15 17:24:57 -0700 | 
|---|---|---|
| committer | Fred Gylys-Colwell <fredgc@google.com> | 2014-07-01 03:27:03 +0000 | 
| commit | 7bdf28d2b83e527f474e96b0984d6a3f5eb457f7 (patch) | |
| tree | e35d68483c98e5b261b14102c4cd38f461441114 /drm/mediadrm | |
| parent | 6b3c1473199927264691bb50445cf0b35c2f8892 (diff) | |
| download | frameworks_av-7bdf28d2b83e527f474e96b0984d6a3f5eb457f7.zip frameworks_av-7bdf28d2b83e527f474e96b0984d6a3f5eb457f7.tar.gz frameworks_av-7bdf28d2b83e527f474e96b0984d6a3f5eb457f7.tar.bz2  | |
JsonWebKey support.
Parses JSON Web Key Set in the response data; extracts and base64 decode
key id(s) and key(s).
bug: 12035506
Change-Id: Ib71bce942d6eca1e569dfad0a9adb6dee1cdf75e
Diffstat (limited to 'drm/mediadrm')
| -rw-r--r-- | drm/mediadrm/plugins/clearkey/JsonWebKey.cpp | 269 | ||||
| -rw-r--r-- | drm/mediadrm/plugins/clearkey/JsonWebKey.h | 62 | ||||
| -rw-r--r-- | drm/mediadrm/plugins/clearkey/tests/Android.mk | 26 | ||||
| -rw-r--r-- | drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp | 321 | ||||
| -rw-r--r-- | drm/mediadrm/plugins/clearkey/tests/unit-test.mk | 51 | 
5 files changed, 729 insertions, 0 deletions
diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp new file mode 100644 index 0000000..53ffae4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "JsonWebKey" + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AString.h> +#include <media/stagefright/foundation/base64.h> +#include <utils/Log.h> + +#include "JsonWebKey.h" + +namespace { +const android::String8 kKeysTag("keys"); +const android::String8 kKeyTypeTag("kty"); +const android::String8 kSymmetricKeyValue("oct"); +const android::String8 kKeyTag("k"); +const android::String8 kKeyIdTag("kid"); +const android::String8 kBase64Padding("="); +} + +namespace clearkeydrm { + +using android::ABuffer; +using android::AString; + +JsonWebKey::JsonWebKey() { +} + +JsonWebKey::~JsonWebKey() { +} + +/* + * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key + * pairs from the JSON Web Key Set. Both key ids and keys are base64url + * encoded. The KeyMap contains base64url decoded key id:key pairs. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet, +        KeyMap* keys) { + +    keys->clear(); +    if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) { +        return false; +    } + +    // mJsonObjects[0] contains the entire JSON Web Key Set, including +    // all the base64 encoded keys. Each key is also stored separately as +    // a JSON object in mJsonObjects[1..n] where n is the total +    // number of keys in the set. +    if (!isJsonWebKeySet(mJsonObjects[0])) { +        return false; +    } + +    String8 encodedKey, encodedKeyId; +    Vector<uint8_t> decodedKey, decodedKeyId; + +    // mJsonObjects[1] contains the first JSON Web Key in the set +    for (size_t i = 1; i < mJsonObjects.size(); ++i) { +        encodedKeyId.clear(); +        encodedKey.clear(); + +        if (!parseJsonObject(mJsonObjects[i], &mTokens)) +            return false; + +        if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) { +            if (encodedKeyId.isEmpty() || encodedKey.isEmpty()) { +                ALOGE("Must have both key id and key in the JsonWebKey set."); +                continue; +            } + +            if (!decodeBase64String(encodedKeyId, &decodedKeyId)) { +                ALOGE("Failed to decode key id(%s)", encodedKeyId.string()); +                continue; +            } + +            if (!decodeBase64String(encodedKey, &decodedKey)) { +                ALOGE("Failed to decode key(%s)", encodedKey.string()); +                continue; +            } + +            keys->add(decodedKeyId, decodedKey); +        } +    } +    return true; +} + +bool JsonWebKey::decodeBase64String(const String8& encodedText, +        Vector<uint8_t>* decodedText) { + +    decodedText->clear(); + +    // encodedText should not contain padding characters as per EME spec. +    if (encodedText.find(kBase64Padding) != -1) { +        return false; +    } + +    // Since android::decodeBase64() requires padding characters, +    // add them so length of encodedText is exactly a multiple of 4. +    int remainder = encodedText.length() % 4; +    String8 paddedText(encodedText); +    if (remainder > 0) { +        for (int i = 0; i < 4 - remainder; ++i) { +            paddedText.append(kBase64Padding); +        } +    } + +    android::sp<ABuffer> buffer = +            android::decodeBase64(AString(paddedText.string())); +    if (buffer == NULL) { +        ALOGE("Malformed base64 encoded content found."); +        return false; +    } + +    decodedText->appendArray(buffer->base(), buffer->size()); +    return true; +} + +bool JsonWebKey::findKey(const String8& jsonObject, String8* keyId, +        String8* encodedKey) { + +    String8 key, value; + +    // Only allow symmetric key, i.e. "kty":"oct" pair. +    if (jsonObject.find(kKeyTypeTag) >= 0) { +        findValue(kKeyTypeTag, &value); +        if (0 != value.compare(kSymmetricKeyValue)) +            return false; +    } + +    if (jsonObject.find(kKeyIdTag) >= 0) { +        findValue(kKeyIdTag, keyId); +    } + +    if (jsonObject.find(kKeyTag) >= 0) { +        findValue(kKeyTag, encodedKey); +    } +    return true; +} + +void JsonWebKey::findValue(const String8 &key, String8* value) { +    value->clear(); +    const char* valueToken; +    for (Vector<String8>::const_iterator nextToken = mTokens.begin(); +        nextToken != mTokens.end(); ++nextToken) { +        if (0 == (*nextToken).compare(key)) { +            if (nextToken + 1 == mTokens.end()) +                break; +            valueToken = (*(nextToken + 1)).string(); +            value->setTo(valueToken); +            nextToken++; +            break; +        } +    } +} + +bool JsonWebKey::isJsonWebKeySet(const String8& jsonObject) const { +    if (jsonObject.find(kKeysTag) == -1) { +        ALOGE("JSON Web Key does not contain keys."); +        return false; +    } +    return true; +} + +/* + * Parses a JSON objects string and initializes a vector of tokens. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonObject(const String8& jsonObject, +        Vector<String8>* tokens) { +    jsmn_parser parser; + +    jsmn_init(&parser); +    int numTokens = jsmn_parse(&parser, +        jsonObject.string(), jsonObject.size(), NULL, 0); +    if (numTokens < 0) { +        ALOGE("Parser returns error code=%d", numTokens); +        return false; +    } + +    unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); +    mJsmnTokens.clear(); +    mJsmnTokens.setCapacity(jsmnTokensSize); + +    jsmn_init(&parser); +    int status = jsmn_parse(&parser, jsonObject.string(), +        jsonObject.size(), mJsmnTokens.editArray(), numTokens); +    if (status < 0) { +        ALOGE("Parser returns error code=%d", status); +        return false; +    } + +    tokens->clear(); +    String8 token; +    const char *pjs; +    for (int j = 0; j < numTokens; ++j) { +        pjs = jsonObject.string() + mJsmnTokens[j].start; +        if (mJsmnTokens[j].type == JSMN_STRING || +                mJsmnTokens[j].type == JSMN_PRIMITIVE) { +            token.setTo(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start); +            tokens->add(token); +        } +    } +    return true; +} + +/* + * Parses JSON Web Key Set string and initializes a vector of JSON objects. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonWebKeySet(const String8& jsonWebKeySet, +        Vector<String8>* jsonObjects) { +    if (jsonWebKeySet.isEmpty()) { +        ALOGE("Empty JSON Web Key"); +        return false; +    } + +    // The jsmn parser only supports unicode encoding. +    jsmn_parser parser; + +    // Computes number of tokens. A token marks the type, offset in +    // the original string. +    jsmn_init(&parser); +    int numTokens = jsmn_parse(&parser, +            jsonWebKeySet.string(), jsonWebKeySet.size(), NULL, 0); +    if (numTokens < 0) { +        ALOGE("Parser returns error code=%d", numTokens); +        return false; +    } + +    unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); +    mJsmnTokens.setCapacity(jsmnTokensSize); + +    jsmn_init(&parser); +    int status = jsmn_parse(&parser, jsonWebKeySet.string(), +            jsonWebKeySet.size(), mJsmnTokens.editArray(), numTokens); +    if (status < 0) { +        ALOGE("Parser returns error code=%d", status); +        return false; +    } + +    String8 token; +    const char *pjs; +    for (int i = 0; i < numTokens; ++i) { +        pjs = jsonWebKeySet.string() + mJsmnTokens[i].start; +        if (mJsmnTokens[i].type == JSMN_OBJECT) { +            token.setTo(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start); +            jsonObjects->add(token); +        } +    } +    return true; +} + +}  // clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/JsonWebKey.h new file mode 100644 index 0000000..6ae50ee --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/JsonWebKey.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CLEARKEY_JSON_WEB_KEY_H_ +#define CLEARKEY_JSON_WEB_KEY_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +#include "jsmn.h" +#include "Utils.h" +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +using android::KeyedVector; +using android::sp; +using android::String8; +using android::Vector; + +class JsonWebKey { + public: +    JsonWebKey(); +    virtual ~JsonWebKey(); + +    bool extractKeysFromJsonWebKeySet(const String8& jsonWebKeySet, +            KeyMap* keys); + + private: +    Vector<jsmntok_t> mJsmnTokens; +    Vector<String8> mJsonObjects; +    Vector<String8> mTokens; + +    bool decodeBase64String(const String8& encodedText, +            Vector<uint8_t>* decodedText); +    bool findKey(const String8& jsonObject, String8* keyId, +            String8* encodedKey); +    void findValue(const String8 &key, String8* value); +    bool isJsonWebKeySet(const String8& jsonObject) const; +    bool parseJsonObject(const String8& jsonObject, Vector<String8>* tokens); +    bool parseJsonWebKeySet(const String8& jsonWebKeySet, Vector<String8>* jsonObjects); + +    DISALLOW_EVIL_CONSTRUCTORS(JsonWebKey); +}; + +}  // namespace clearkeydrm + +#endif  // CLEARKEY_JSON_WEB_KEY_H_ diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.mk b/drm/mediadrm/plugins/clearkey/tests/Android.mk new file mode 100644 index 0000000..c12cd9f --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/tests/Android.mk @@ -0,0 +1,26 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +#      http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ---------------------------------------------------------------- +# Builds ClearKey Drm Tests +# +LOCAL_PATH := $(call my-dir) + +test_name := JsonWebKeyUnittest +test_src_dir := . +include $(LOCAL_PATH)/unit-test.mk + +test_name := +test_src_dir := diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp new file mode 100644 index 0000000..c3b0d84 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <utils/Log.h> + +#include "JsonWebKey.h" + +#include "gtest/gtest.h" +#include "Utils.h" + +namespace clearkeydrm { +using android::String8; +using android::Vector; + +class JsonWebKeyTest : public ::testing::Test { + protected: +    JsonWebKey* jwk; + +    JsonWebKeyTest() { +        jwk = new JsonWebKey; +    } + +    virtual ~JsonWebKeyTest() { +        if (jwk) +            delete jwk; +    } +}; + +void stringFromVector(const Vector<uint8_t>& input, +        String8* converted) { +    converted->clear(); +    if (input.isEmpty()) { +        return; +    } + +    for (size_t i = 0; i < input.size(); ++i) { +        converted->appendFormat("%c", input.itemAt(i)); +    } +} + +void verifyKeys(const KeyMap& keys, const String8* clearKeys) { +    if (keys.isEmpty()) { +        return; +    } + +    String8 keyString; +    for (size_t i = 0; i < keys.size(); ++i) { +        stringFromVector(keys.valueAt(i), &keyString); +        EXPECT_EQ(keyString, clearKeys[i]); +    } +} + +TEST_F(JsonWebKeyTest, NoSymmetricKey) { +    const String8 js( +            "{" +                "[{" +                    "\"kty\":\"rsa\"," +                    "\"alg\":\"A128KW1\"," +                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"," +                    "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\"" +                "}]" +          "}"); + +    KeyMap keys; +    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); +    EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, NoKeysTag) { +    const String8 js( +            "{" +                "[{" +                    "\"kty\":\"oct\"," +                    "\"alg\":\"A128KW1\"," +                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"," +                    "\"k\":\"1-GawgguFyGrWKav7AX4VKUg\"" +                "}," +                "{" +                    "\"kty\":\"oct\"," +                    "\"alg\":\"A128KW2\"," +                    "\"k\":\"R29vZCBkYXkh\"," +                    "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" +                "}]" +            "}"); + +    KeyMap keys; +    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); +    EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, NoKeyId) { +    const String8 js( +            "{" +                "\"keys\":" +                    "[{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        "\"k\":\"SGVsbG8gRnJpZW5kISE=\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW2\"" +                        "\"k\":\"R29vZCBkYXkh\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" +                    "}]" +            "}"); + +    KeyMap keys; +    EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); +    EXPECT_TRUE(keys.size() == 1); + +    const String8 clearKeys("Good day!"); +    verifyKeys(keys, &clearKeys); +} + +TEST_F(JsonWebKeyTest, NoKey) { +    const String8 js( +            "{" +                "\"keys\":" +                    "[{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        "\"kid\":\"`\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW2\"" +                        "\"k\":\"R29vZCBkYXkh\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" +                    "}]" +            "}"); + +    KeyMap keys; +    EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); +    EXPECT_TRUE(keys.size() == 1); + +    const String8 clearKeys("Good day!"); +    verifyKeys(keys, &clearKeys); +} + +TEST_F(JsonWebKeyTest, MalformedKey) { +    const String8 js( +            "{" +                "\"keys\":" +                    "[{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        "\"k\":\"GawgguFyGrWKav7AX4V???\"" +                        "\"kid\":\"67ef0gd8pvfd0=\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        "\"k\":\"GawgguFyGrWKav7AX4V???\"" +                        "\"kid\":" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        ":\"GawgguFyGrWKav7AX4V???\"" +                        "\"kid\":\"67ef0gd8pvfd0=\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW3\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" +                        "\"k\":\"R29vZCBkYXkh\"" +                    "}]" +            "}"); + +    KeyMap keys; +    EXPECT_TRUE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); +    EXPECT_TRUE(keys.size() == 1); + +    const String8 clearKeys("Good day!"); +    verifyKeys(keys, &clearKeys); +} + +TEST_F(JsonWebKeyTest, EmptyJsonWebKey) { +    const String8 js; +    KeyMap keys; +    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); +    EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, MalformedJsonWebKey) { +    // Missing begin array '[' +    const String8 js( +            "{" +                "\"keys\":" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        "\"k\":\"GawgguFyGrWKav7AX4VKUg\"" +                        "\"kid\":\"67ef0gd8pvfd0=\"" +                    "}" +            "]" +            "}"); + +    KeyMap keys; +    EXPECT_FALSE(jwk->extractKeysFromJsonWebKeySet(js, &keys)); +    EXPECT_TRUE(keys.isEmpty()); +} + +TEST_F(JsonWebKeyTest, SameKeyId) { +    const String8 js( +            "{" +                "\"keys\":" +                    "[{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"" +                        "\"k\":\"SGVsbG8gRnJpZW5kISE\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW3\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" +                        "\"k\":\"R29vZCBkYXkh\"" +                    "}]" +            "}"); + +    KeyMap keys; +    jwk->extractKeysFromJsonWebKeySet(js, &keys); +    EXPECT_TRUE(keys.size() == 2); + +    const String8 clearKeys[] = +            { String8("Hello Friend!"), String8("Good day!") }; +    verifyKeys(keys, clearKeys); +} + +TEST_F(JsonWebKeyTest, ExtractWellFormedKeys) { +    const String8 js( +            "{" +                "\"keys\":" +                    "[{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW2\"" +                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW3\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" +                        "\"k\":\"R29vZCBkYXkh\"" +                    "}]" +            "}"); + +    KeyMap keys; +    jwk->extractKeysFromJsonWebKeySet(js, &keys); +    EXPECT_TRUE(keys.size() == 2); + +    const String8 clearKeys[] = +            { String8("Hello Friend!"), String8("Good day!") }; +    verifyKeys(keys, clearKeys); +} + +TEST_F(JsonWebKeyTest, ExtractKeys) { +    const String8 js( +            "{" +                "\"keys\":" +                    "[{" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\"" +                        "\"k\":\"SGVsbG8gRnJpZW5kISE\"" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW1\"" +                    "}" +                    "{" +                        "\"kty\":\"oct\"" +                        "\"alg\":\"A128KW2\"" +                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\"" +                    "}" +                    "{" +                        "\"kty\":\"rsa\"" +                        "\"alg\":\"A128KW-rsa\"" +                        "\"k\":\"R29vZCBkYXkh\"" +                        "\"kid\":\"rsa-67ef0gd8pvfd0=\"" +                    "}" +                    "{" +                        "\"alg\":\"A128KW3\"" +                        "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\"" +                        "\"k\":\"R29vZCBkYXkh\"" +                        "\"kty\":\"oct\"" +                    "}]" +            "}"); + +    KeyMap keys; +    jwk->extractKeysFromJsonWebKeySet(js, &keys); +    EXPECT_TRUE(keys.size() == 3); + +    const String8 clearKeys[] = +            { String8("Hello Friend!!"), String8("Hello Friend!"), +              String8("Good day!") }; +    verifyKeys(keys, clearKeys); +} + +}  // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/tests/unit-test.mk b/drm/mediadrm/plugins/clearkey/tests/unit-test.mk new file mode 100644 index 0000000..6afb5a3 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/tests/unit-test.mk @@ -0,0 +1,51 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +#      http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------- +# Makes a unit or end to end test. +# test_name must be passed in as the base filename(without the .cpp). +# +$(call assert-not-null,test_name) + +include $(CLEAR_VARS) + +LOCAL_MODULE := $(test_name) +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ +    $(test_src_dir)/$(test_name).cpp + +LOCAL_C_INCLUDES := \ +    bionic \ +    external/gtest/include \ +    external/jsmn \ +    external/openssl/include \ +    external/stlport/stlport \ +    frameworks/av/drm/mediadrm/plugins/clearkey \ +    frameworks/av/include \ +    frameworks/native/include \ + +LOCAL_STATIC_LIBRARIES := \ +    libgtest \ +    libgtest_main \ + +LOCAL_SHARED_LIBRARIES := \ +    libcrypto \ +    libdrmclearkeyplugin \ +    liblog \ +    libstlport \ +    libutils \ + +include $(BUILD_EXECUTABLE)  | 
