| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/cdm/json_web_key.h" |
| |
| #include "base/logging.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace media { |
| |
| class JSONWebKeyTest : public testing::Test { |
| public: |
| JSONWebKeyTest() {} |
| |
| protected: |
| void ExtractJWKKeysAndExpect(const std::string& jwk, |
| bool expected_result, |
| size_t expected_number_of_keys) { |
| DCHECK(!jwk.empty()); |
| KeyIdAndKeyPairs keys; |
| EXPECT_EQ(expected_result, ExtractKeysFromJWKSet(jwk, &keys)); |
| EXPECT_EQ(expected_number_of_keys, keys.size()); |
| } |
| }; |
| |
| TEST_F(JSONWebKeyTest, GenerateJWKSet) { |
| const uint8 data1[] = { 0x01, 0x02 }; |
| const uint8 data2[] = { 0x01, 0x02, 0x03, 0x04 }; |
| const uint8 data3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }; |
| |
| EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}", |
| GenerateJWKSet(data1, arraysize(data1), data1, arraysize(data1))); |
| EXPECT_EQ( |
| "{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}", |
| GenerateJWKSet(data2, arraysize(data2), data2, arraysize(data2))); |
| EXPECT_EQ("{\"keys\":[{\"k\":\"AQI\",\"kid\":\"AQIDBA\",\"kty\":\"oct\"}]}", |
| GenerateJWKSet(data1, arraysize(data1), data2, arraysize(data2))); |
| EXPECT_EQ("{\"keys\":[{\"k\":\"AQIDBA\",\"kid\":\"AQI\",\"kty\":\"oct\"}]}", |
| GenerateJWKSet(data2, arraysize(data2), data1, arraysize(data1))); |
| EXPECT_EQ( |
| "{\"keys\":[{\"k\":\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kid\":" |
| "\"AQIDBAUGBwgJCgsMDQ4PEA\",\"kty\":\"oct\"}]}", |
| GenerateJWKSet(data3, arraysize(data3), data3, arraysize(data3))); |
| } |
| |
| TEST_F(JSONWebKeyTest, ExtractJWKKeys) { |
| // Try a simple JWK key (i.e. not in a set) |
| const std::string kJwkSimple = |
| "{" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\"," |
| " \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" |
| "}"; |
| ExtractJWKKeysAndExpect(kJwkSimple, false, 0); |
| |
| // Try a key list with multiple entries. |
| const std::string kJwksMultipleEntries = |
| "{" |
| " \"keys\": [" |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"AAECAwQFBgcICQoLDA0ODxAREhM\"," |
| " \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" |
| " }," |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"JCUmJygpKissLS4vMA\"," |
| " \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\"" |
| " }" |
| " ]" |
| "}"; |
| ExtractJWKKeysAndExpect(kJwksMultipleEntries, true, 2); |
| |
| // Try a key with no spaces and some \n plus additional fields. |
| const std::string kJwksNoSpaces = |
| "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\"," |
| "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"GawgguFyGrWKav7AX4VKUg" |
| "\",\"foo\":\"bar\"}]}\n\n"; |
| ExtractJWKKeysAndExpect(kJwksNoSpaces, true, 1); |
| |
| // Try some non-ASCII characters. |
| ExtractJWKKeysAndExpect( |
| "This is not ASCII due to \xff\xfe\xfd in it.", false, 0); |
| |
| // Try some non-ASCII characters in an otherwise valid JWK. |
| const std::string kJwksInvalidCharacters = |
| "\n\n{\"something\":1,\"keys\":[{\n\n\"kty\":\"oct\",\"alg\":\"A128KW\"," |
| "\"kid\":\"AAECAwQFBgcICQoLDA0ODxAREhM\",\"k\":\"\xff\xfe\xfd" |
| "\",\"foo\":\"bar\"}]}\n\n"; |
| ExtractJWKKeysAndExpect(kJwksInvalidCharacters, false, 0); |
| |
| // Try a badly formatted key. Assume that the JSON parser is fully tested, |
| // so we won't try a lot of combinations. However, need a test to ensure |
| // that the code doesn't crash if invalid JSON received. |
| ExtractJWKKeysAndExpect("This is not a JSON key.", false, 0); |
| |
| // Try passing some valid JSON that is not a dictionary at the top level. |
| ExtractJWKKeysAndExpect("40", false, 0); |
| |
| // Try an empty dictionary. |
| ExtractJWKKeysAndExpect("{ }", false, 0); |
| |
| // Try an empty 'keys' dictionary. |
| ExtractJWKKeysAndExpect("{ \"keys\": [] }", true, 0); |
| |
| // Try with 'keys' not a dictionary. |
| ExtractJWKKeysAndExpect("{ \"keys\":\"1\" }", false, 0); |
| |
| // Try with 'keys' a list of integers. |
| ExtractJWKKeysAndExpect("{ \"keys\": [ 1, 2, 3 ] }", false, 0); |
| |
| // Try padding(=) at end of 'k' base64 string. |
| const std::string kJwksWithPaddedKey = |
| "{" |
| " \"keys\": [" |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"AAECAw\"," |
| " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw==\"" |
| " }" |
| " ]" |
| "}"; |
| ExtractJWKKeysAndExpect(kJwksWithPaddedKey, false, 0); |
| |
| // Try padding(=) at end of 'kid' base64 string. |
| const std::string kJwksWithPaddedKeyId = |
| "{" |
| " \"keys\": [" |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"AAECAw==\"," |
| " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" |
| " }" |
| " ]" |
| "}"; |
| ExtractJWKKeysAndExpect(kJwksWithPaddedKeyId, false, 0); |
| |
| // Try a key with invalid base64 encoding. |
| const std::string kJwksWithInvalidBase64 = |
| "{" |
| " \"keys\": [" |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"!@#$%^&*()\"," |
| " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" |
| " }" |
| " ]" |
| "}"; |
| ExtractJWKKeysAndExpect(kJwksWithInvalidBase64, false, 0); |
| |
| // Empty key id. |
| const std::string kJwksWithEmptyKeyId = |
| "{" |
| " \"keys\": [" |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"\"," |
| " \"k\": \"BAUGBwgJCgsMDQ4PEBESEw\"" |
| " }" |
| " ]" |
| "}"; |
| ExtractJWKKeysAndExpect(kJwksWithEmptyKeyId, false, 0); |
| |
| // Try a list with multiple keys with the same kid. |
| const std::string kJwksDuplicateKids = |
| "{" |
| " \"keys\": [" |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"JCUmJygpKissLS4vMA\"," |
| " \"k\": \"FBUWFxgZGhscHR4fICEiIw\"" |
| " }," |
| " {" |
| " \"kty\": \"oct\"," |
| " \"kid\": \"JCUmJygpKissLS4vMA\"," |
| " \"k\":\"MTIzNDU2Nzg5Ojs8PT4/QA\"" |
| " }" |
| " ]" |
| "}"; |
| ExtractJWKKeysAndExpect(kJwksDuplicateKids, true, 2); |
| } |
| |
| } // namespace media |
| |