author | Chris Pearce <cpearce@mozilla.com> |
Mon, 01 Aug 2016 16:28:10 +1200 | |
changeset 397704 | 596b66332f97932ebc7cd126f25cf88f60d509b6 |
parent 397689 | 1559c5f5e687784b5a57d921685db0e6ea820640 |
child 527511 | 859748ea688d4e5733824908374087e7f46ec5c0 |
push id | 25360 |
push user | cpearce@mozilla.com |
push date | Mon, 08 Aug 2016 03:49:40 +0000 |
reviewers | gerald |
bugs | 1289968 |
milestone | 51.0a1 |
--- a/dom/media/test/test_eme_initDataTypes.html +++ b/dom/media/test/test_eme_initDataTypes.html @@ -61,24 +61,16 @@ var tests = [ { name: "SessionType in license doesn't match MediaKeySession's sessionType", initDataType: 'keyids', initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}', sessionType: 'persistent-license', expectPass: false, }, { - name: "One valid and one invalid kid", - initDataType: 'keyids', - initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A", "invalid"]}', - expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"],"type":"temporary"}', - sessionType: 'temporary', - expectPass: true, - }, - { name: "Invalid initData", initDataType: 'keyids', initData: 'invalid initData', sessionType: 'temporary', expectPass: false, }, { name: "'webm' initDataType", @@ -87,18 +79,19 @@ var tests = [ expectedRequest: '{"kids":["YAYeAX5Hfod-V9ANHtANHg"],"type":"temporary"}', sessionType: 'temporary', expectPass: true, }, { name: "'webm' initDataType with non 16 byte keyid", initDataType: 'webm', initData: 'YAYeAX5Hfod', + expectedRequest: '{\"kids\":[\"YAYeAX5Hfoc\"],\"type\":\"temporary\"}', sessionType: 'temporary', - expectPass: false, + expectPass: true, }, ]; function PrepareInitData(initDataType, initData) { if (initDataType == "keyids") { return new TextEncoder().encode(initData); } else if (initDataType == "webm") {
--- a/media/gmp-clearkey/0.1/ClearKeyBase64.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyBase64.cpp @@ -54,28 +54,35 @@ Decode6Bit(string& aStr) break; } } return true; } bool -DecodeBase64KeyOrId(const string& aEncoded, vector<uint8_t>& aOutDecoded) +DecodeBase64(const string& aEncoded, vector<uint8_t>& aOutDecoded) { + if (aEncoded.empty()) { + aOutDecoded.clear(); + return true; + } + if (aEncoded.size() == 1) { + // Invalid Base64 encoding. + return false; + } string encoded = aEncoded; - if (!Decode6Bit(encoded) || - encoded.size() != 22) { // Can't decode to 16 byte CENC key or keyId. + if (!Decode6Bit(encoded)) { return false; } // The number of bytes we haven't yet filled in the current byte, mod 8. int shift = 0; - aOutDecoded.resize(16); + aOutDecoded.resize((encoded.size() * 3) / 4); vector<uint8_t>::iterator out = aOutDecoded.begin(); for (size_t i = 0; i < encoded.length(); i++) { if (!shift) { *out = encoded[i] << 2; } else { *out |= encoded[i] >> (6 - shift); out++;
--- a/media/gmp-clearkey/0.1/ClearKeyBase64.h +++ b/media/gmp-clearkey/0.1/ClearKeyBase64.h @@ -16,16 +16,13 @@ #ifndef __ClearKeyBase64_h__ #define __ClearKeyBase64_h__ #include <vector> #include <string> #include <stdint.h> -// Decodes a base64 encoded CENC Key or KeyId into it's raw bytes. Note that -// CENC Keys or KeyIds are 16 bytes long, so encoded they should be 22 bytes -// plus any padding. Fails (returns false) on input that is more than 22 bytes -// long after padding is stripped. Returns true on success. +// Decodes a base64 encoded string. Returns true on success. bool -DecodeBase64KeyOrId(const std::string& aEncoded, std::vector<uint8_t>& aOutDecoded); +DecodeBase64(const std::string& aEncoded, std::vector<uint8_t>& aOutDecoded); #endif
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp +++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp @@ -65,17 +65,17 @@ ClearKeySession::Init(uint32_t aCreateSe } else if (aInitDataType == "keyids") { std::string sessionType; ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds, sessionType); if (sessionType != ClearKeyUtils::SessionTypeToString(mSessionType)) { const char message[] = "Session type specified in keyids init data doesn't match session type."; mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, message, strlen(message)); return; } - } else if (aInitDataType == "webm" && aInitDataSize == 16) { + } else if (aInitDataType == "webm") { // "webm" initData format is simply the raw bytes of the keyId. vector<uint8_t> keyId; keyId.assign(aInitData, aInitData+aInitDataSize); mKeyIds.push_back(keyId); } if (!mKeyIds.size()) { const char message[] = "Couldn't parse init data";
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp @@ -305,25 +305,16 @@ GetNextLabel(ParserContext& aCtx, string return true; } } return false; } static bool -DecodeKey(string& aEncoded, Key& aOutDecoded) -{ - return - DecodeBase64KeyOrId(aEncoded, aOutDecoded) && - // Key should be 128 bits long. - aOutDecoded.size() == CLEARKEY_KEY_LEN; -} - -static bool ParseKeyObject(ParserContext& aCtx, KeyIdPair& aOutKey) { EXPECT_SYMBOL(aCtx, '{'); // Reject empty objects as invalid licenses. if (PeekSymbol(aCtx) == '}') { GetNextSymbol(aCtx); return false; @@ -358,18 +349,18 @@ ParseKeyObject(ParserContext& aCtx, KeyI if (!sym || sym == '}') { break; } EXPECT_SYMBOL(aCtx, ','); } return !key.empty() && !keyId.empty() && - DecodeBase64KeyOrId(keyId, aOutKey.mKeyId) && - DecodeKey(key, aOutKey.mKey) && + DecodeBase64(keyId, aOutKey.mKeyId) && + DecodeBase64(key, aOutKey.mKey) && GetNextSymbol(aCtx) == '}'; } static bool ParseKeys(ParserContext& aCtx, vector<KeyIdPair>& aOutKeys) { // Consume start of array. EXPECT_SYMBOL(aCtx, '['); @@ -446,22 +437,22 @@ static bool ParseKeyIds(ParserContext& aCtx, vector<KeyId>& aOutKeyIds) { // Consume start of array. EXPECT_SYMBOL(aCtx, '['); while (true) { string label; vector<uint8_t> keyId; - if (!GetNextLabel(aCtx, label) || - !DecodeBase64KeyOrId(label, keyId)) { + if (!GetNextLabel(aCtx, label) || !DecodeBase64(label, keyId)) { return false; } - assert(!keyId.empty()); - aOutKeyIds.push_back(keyId); + if (!keyId.empty()) { + aOutKeyIds.push_back(keyId); + } uint8_t sym = PeekSymbol(aCtx); if (!sym || sym == ']') { break; } EXPECT_SYMBOL(aCtx, ','); } @@ -488,17 +479,20 @@ ClearKeyUtils::ParseKeyIdsInitData(const while (true) { string label; // Consume member kids. if (!GetNextLabel(ctx, label)) return false; EXPECT_SYMBOL(ctx, ':'); if (label == "kids") { // Parse "kids" array. - if (!ParseKeyIds(ctx, aOutKeyIds)) return false; + if (!ParseKeyIds(ctx, aOutKeyIds) || + aOutKeyIds.empty()) { + return false; + } } else if (label == "type") { // Consume type string. if (!GetNextLabel(ctx, aOutSessionType)) return false; } else { SkipToken(ctx); } // Check for end of object.
--- a/media/gmp-clearkey/0.1/gtest/TestClearKeyUtils.cpp +++ b/media/gmp-clearkey/0.1/gtest/TestClearKeyUtils.cpp @@ -8,26 +8,25 @@ #include <algorithm> #include <stdint.h> #include <vector> #include "../ClearKeyBase64.cpp" #include "../ClearKeyCencParser.cpp" #include "../ArrayUtils.h" - using namespace std; struct B64Test { - const char* b64; - uint8_t raw[16]; + string b64; + vector<uint8_t> raw; bool shouldPass; }; -B64Test tests[] = { +const B64Test tests[] = { { "AAAAADk4AU4AAAAAAAAAAA", { 0x0, 0x0, 0x0, 0x0, 0x39, 0x38, 0x1, 0x4e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, true }, { "h2mqp1zAJjDIC34YXEXBxA==", { 0x87, 0x69, 0xaa, 0xa7, 0x5c, 0xc0, 0x26, 0x30, 0xc8, 0xb, 0x7e, 0x18, 0x5c, 0x45, 0xc1, 0xc4 }, @@ -36,45 +35,65 @@ B64Test tests[] = { { "flcdA35XHQN-Vx0DflcdAw", { 0x7e, 0x57, 0x1d, 0x3, 0x7e, 0x57, 0x1d, 0x3, 0x7e, 0x57, 0x1d, 0x3, 0x7e, 0x57, 0x1d, 0x3 }, true }, { "flczM35XMzN-VzMzflczMw", { 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33 }, - true + true, }, { "flcdBH5XHQR-Vx0EflcdBA", { 0x7e, 0x57, 0x1d, 0x4, 0x7e, 0x57, 0x1d, 0x4, 0x7e, 0x57, 0x1d, 0x4, 0x7e, 0x57, 0x1d, 0x4 }, true }, { "fldERH5XRER-V0REfldERA", { 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44 }, true }, + { + "fuzzbiz=", + { 0x7e, 0xec, 0xf3, 0x6e, 0x2c }, + true + }, + { + "fuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbiz", + { + 0x7e, 0xec, 0xf3, 0x6e, 0x2c, 0xdf, 0xbb, 0x3c, 0xdb, 0x8b, + 0x37, 0xee, 0xcf, 0x36, 0xe2, 0xcd, 0xfb, 0xb3, 0xcd, 0xb8, + 0xb3, 0x7e, 0xec, 0xf3, 0x6e, 0x2c, 0xdf, 0xbb, 0x3c, 0xdb, + 0x8b, 0x37, 0xee, 0xcf, 0x36, 0xe2, 0xcd, 0xfb, 0xb3, 0xcd, + 0xb8, 0xb3 + }, + true + }, + { "", { }, true }, + { "00", { 0xd3 }, true }, + { "000", { 0xd3, 0x4d }, true }, + + { "invalid", { 0x8a, 0x7b, 0xda, 0x96, 0x27 }, true }, + { "invalic", { 0x8a, 0x7b, 0xda, 0x96, 0x27 }, true }, + // Failure tests - { "", { 0 }, false }, // empty - { "fuzzbiz", { 0 }, false }, // Too short - { "fuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbiz", { 0 }, false }, // too long - + { "A", { }, false }, // 1 character is too few. + { "_", { }, false }, // 1 character is too few. }; -TEST(ClearKey, DecodeBase64KeyOrId) { - for (size_t i = 0; i < MOZ_ARRAY_LENGTH(tests); i++) { +TEST(ClearKey, DecodeBase64) { + for (const B64Test& test : tests) { vector<uint8_t> v; - const B64Test& test = tests[i]; - bool rv = DecodeBase64KeyOrId(string(test.b64), v); - EXPECT_EQ(rv, test.shouldPass); + bool rv = DecodeBase64(string(test.b64), v); + EXPECT_EQ(test.shouldPass, rv); if (test.shouldPass) { - EXPECT_EQ(v.size(), 16u); - for (size_t k = 0; k < 16; k++) { - EXPECT_EQ(v[k], test.raw[k]); + EXPECT_EQ(test.raw.size(), v.size()); + for (size_t k = 0; k < test.raw.size(); k++) { + EXPECT_EQ(test.raw[k], v[k]); } } } } // This is the CENC initData from Google's web-platform tests. // https://github.com/w3c/web-platform-tests/blob/master/encrypted-media/Google/encrypted-media-utils.js#L50 const uint8_t gGoogleWPTCencInitData[] = { @@ -181,17 +200,17 @@ const uint8_t g2xGoogleWPTCencInitData[] 0x70, 0x73, 0x73, 0x68, // 'pssh' 0x01, // version = 1 0x00, 0x00, 0x00, // flags 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02, // Common SystemID 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B, 0x00, 0x00, 0x00, 0x01, // key count 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // key 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x00, 0x00, 0x00, 0x00 // datasize + 0x00, 0x00, 0x00, 0x00 // datasize }; TEST(ClearKey, ParseCencInitData) { std::vector<std::vector<uint8_t>> keyIds; ParseCENCInitData(gGoogleWPTCencInitData, MOZ_ARRAY_LENGTH(gGoogleWPTCencInitData), keyIds); EXPECT_EQ(keyIds.size(), 1u); EXPECT_EQ(keyIds[0].size(), 16u);