Bug 1141386 - Test case for gmp-clearkey's base64 key/keyId decoding. r=edwin

This commit is contained in:
Chris Pearce 2015-03-10 16:49:15 +13:00
parent 310f9675e7
commit d32f079d43
6 changed files with 199 additions and 64 deletions

View File

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ClearKeyBase64.h"
#include <algorithm>
#include "mozilla/ArrayUtils.h"
using namespace std;
/**
* Take a base64-encoded string, convert (in-place) each character to its
* corresponding value in the [0x00, 0x3f] range, and truncate any padding.
*/
static bool
Decode6Bit(string& aStr)
{
for (size_t i = 0; i < aStr.length(); i++) {
if (aStr[i] >= 'A' && aStr[i] <= 'Z') {
aStr[i] -= 'A';
}
else if (aStr[i] >= 'a' && aStr[i] <= 'z') {
aStr[i] -= 'a' - 26;
}
else if (aStr[i] >= '0' && aStr[i] <= '9') {
aStr[i] -= '0' - 52;
}
else if (aStr[i] == '-' || aStr[i] == '+') {
aStr[i] = 62;
}
else if (aStr[i] == '_' || aStr[i] == '/') {
aStr[i] = 63;
}
else {
// Truncate '=' padding at the end of the aString.
if (aStr[i] != '=') {
aStr.erase(i, string::npos);
return false;
}
aStr[i] = '\0';
aStr.resize(i);
break;
}
}
return true;
}
bool
DecodeBase64KeyOrId(const string& aEncoded, vector<uint8_t>& aOutDecoded)
{
string encoded = aEncoded;
if (!Decode6Bit(encoded) ||
encoded.size() != 22) { // Can't decode to 16 byte CENC key or keyId.
return false;
}
// The number of bytes we haven't yet filled in the current byte, mod 8.
int shift = 0;
aOutDecoded.resize(16);
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++;
if (out == aOutDecoded.end()) {
// Hit last 6bit octed in encoded, which is padding and can be ignored.
break;
}
*out = encoded[i] << (shift + 2);
}
shift = (shift + 2) % 8;
}
return true;
}

View File

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#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.
bool
DecodeBase64KeyOrId(const std::string& aEncoded, std::vector<uint8_t>& aOutDecoded);
#endif

View File

@ -9,6 +9,7 @@
#include <vector>
#include "ClearKeyUtils.h"
#include "ClearKeyBase64.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Endian.h"
@ -362,70 +363,6 @@ GetNextLabel(ParserContext& aCtx, string& aOutLabel)
return false;
}
/**
* Take a base64-encoded string, convert (in-place) each character to its
* corresponding value in the [0x00, 0x3f] range, and truncate any padding.
*/
static bool
Decode6Bit(string& aStr)
{
for (size_t i = 0; i < aStr.length(); i++) {
if (aStr[i] >= 'A' && aStr[i] <= 'Z') {
aStr[i] -= 'A';
} else if (aStr[i] >= 'a' && aStr[i] <= 'z') {
aStr[i] -= 'a' - 26;
} else if (aStr[i] >= '0' && aStr[i] <= '9') {
aStr[i] -= '0' - 52;
} else if (aStr[i] == '-' || aStr[i] == '+') {
aStr[i] = 62;
} else if (aStr[i] == '_' || aStr[i] == '/') {
aStr[i] = 63;
} else {
// Truncate '=' padding at the end of the aString.
if (aStr[i] != '=') {
aStr.erase(i, string::npos);
return false;
}
aStr[i] = '\0';
aStr.resize(i);
break;
}
}
return true;
}
static bool
DecodeBase64KeyOrId(string& aEncoded, vector<uint8_t>& aOutDecoded)
{
if (!Decode6Bit(aEncoded) ||
aEncoded.size() != 22) { // Can't decode to 16 byte CENC key or keyId.
return false;
}
// The number of bytes we haven't yet filled in the current byte, mod 8.
int shift = 0;
aOutDecoded.resize(16);
vector<uint8_t>::iterator out = aOutDecoded.begin();
for (size_t i = 0; i < aEncoded.length(); i++) {
if (!shift) {
*out = aEncoded[i] << 2;
} else {
*out |= aEncoded[i] >> (6 - shift);
out++;
if (out == aOutDecoded.end()) {
// Hit last 6bit octed in encoded, which is padding and can be ignored.
break;
}
*out = aEncoded[i] << (shift + 2);
}
shift = (shift + 2) % 8;
}
return true;
}
static bool
DecodeKey(string& aEncoded, Key& aOutDecoded)
{

View File

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include <algorithm>
#include <stdint.h>
#include <vector>
#include "../ClearKeyBase64.cpp"
using namespace std;
using namespace mozilla;
struct B64Test {
const char* b64;
uint8_t raw[16];
bool shouldPass;
};
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 },
true
},
{
"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
},
{
"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
},
// Failure tests
{ "", { 0 }, false }, // empty
{ "fuzzbiz", { 0 }, false }, // Too short
{ "fuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbizfuzzbiz", { 0 }, false }, // too long
};
TEST(ClearKey, DecodeBase64KeyOrId) {
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(tests); i++) {
vector<uint8_t> v;
const B64Test& test = tests[i];
bool rv = DecodeBase64KeyOrId(string(test.b64), v);
EXPECT_EQ(rv, test.shouldPass);
if (test.shouldPass) {
EXPECT_EQ(v.size(), 16);
for (size_t k = 0; k < 16; k++) {
EXPECT_EQ(v[k], test.raw[k]);
}
}
}
}

View File

@ -0,0 +1,17 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'TestClearKeyUtils.cpp',
]
FINAL_LIBRARY = 'xul-gtest'
FAIL_ON_WARNINGS = True
LOCAL_INCLUDES += [
'..',
]

View File

@ -9,6 +9,7 @@ SharedLibrary('clearkey')
FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
UNIFIED_SOURCES += [
'ClearKeyBase64.cpp',
'ClearKeyDecryptionManager.cpp',
'ClearKeyPersistence.cpp',
'ClearKeySession.cpp',
@ -41,6 +42,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['ENABLE_WMF'] = True
TEST_DIRS += [
'gtest',
]
LOCAL_INCLUDES += [
'/dom/media/gmp',