Bug 1289968 - [Part1] Ensure ClearKey doesn't assume keyIds can only be 16 bytes. r=cpearce

MozReview-Commit-ID: Hf2IQsAHa4r

--HG--
extra : transplant_source : %D1%2C%B2j%1C%F5%A7%A9%D8%0B%EEE%92%B1%DC1%A5A%BC%8B
This commit is contained in:
Chris Pearce 2016-08-01 16:28:10 +12:00
Родитель 108459bd3b
Коммит ee9ca0dd5e
6 изменённых файлов: 63 добавлений и 53 удалений

Просмотреть файл

@ -65,14 +65,6 @@ var tests = [
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',
@ -92,8 +84,9 @@ var tests = [
name: "'webm' initDataType with non 16 byte keyid",
initDataType: 'webm',
initData: 'YAYeAX5Hfod',
expectedRequest: '{\"kids\":[\"YAYeAX5Hfoc\"],\"type\":\"temporary\"}',
sessionType: 'temporary',
expectPass: false,
expectPass: true,
},
];

Просмотреть файл

@ -59,18 +59,25 @@ Decode6Bit(string& aStr)
}
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) {

Просмотреть файл

@ -21,11 +21,8 @@
#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

Просмотреть файл

@ -70,7 +70,7 @@ ClearKeySession::Init(uint32_t aCreateSessionToken,
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);

Просмотреть файл

@ -309,15 +309,6 @@ GetNextLabel(ParserContext& aCtx, string& aOutLabel)
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)
{
@ -363,8 +354,8 @@ ParseKeyObject(ParserContext& aCtx, KeyIdPair& aOutKey)
return !key.empty() &&
!keyId.empty() &&
DecodeBase64KeyOrId(keyId, aOutKey.mKeyId) &&
DecodeKey(key, aOutKey.mKey) &&
DecodeBase64(keyId, aOutKey.mKeyId) &&
DecodeBase64(key, aOutKey.mKey) &&
GetNextSymbol(aCtx) == '}';
}
@ -451,12 +442,12 @@ ParseKeyIds(ParserContext& aCtx, vector<KeyId>& aOutKeyIds)
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 == ']') {
@ -493,7 +484,10 @@ ClearKeyUtils::ParseKeyIdsInitData(const uint8_t* aInitData,
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;

Просмотреть файл

@ -13,16 +13,15 @@
#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 },
@ -41,7 +40,7 @@ B64Test tests[] = {
{
"flczM35XMzN-VzMzflczMw",
{ 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33, 0x7e, 0x57, 0x33, 0x33 },
true
true,
},
{
"flcdBH5XHQR-Vx0EflcdBA",
@ -53,23 +52,43 @@ B64Test tests[] = {
{ 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44, 0x7e, 0x57, 0x44, 0x44 },
true
},
// Failure tests
{ "", { 0 }, false }, // empty
{ "fuzzbiz", { 0 }, false }, // Too short
{ "fuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbiz", { 0 }, false }, // too long
{
"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
{ "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]);
}
}
}
@ -186,7 +205,7 @@ const uint8_t g2xGoogleWPTCencInitData[] = {
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) {