Merge "Make PSSH parser more robust."

This commit is contained in:
John "Juce" Bruce
2015-03-05 20:04:52 +00:00
committed by Android (Google) Code Review
4 changed files with 259 additions and 47 deletions

View File

@@ -14,12 +14,10 @@ namespace wvcdm {
InitializationData::InitializationData(const std::string& type,
const CdmInitData& data)
: type_(type), is_cenc_(false), is_webm_(false) {
if (type == ISO_BMFF_VIDEO_MIME_TYPE ||
type == ISO_BMFF_AUDIO_MIME_TYPE ||
if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE ||
type == CENC_INIT_DATA_FORMAT) {
is_cenc_ = true;
} else if (type == WEBM_VIDEO_MIME_TYPE ||
type == WEBM_AUDIO_MIME_TYPE ||
} else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE ||
type == WEBM_INIT_DATA_FORMAT) {
is_webm_ = true;
}
@@ -35,83 +33,127 @@ InitializationData::InitializationData(const std::string& type,
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH.
bool InitializationData::ExtractWidevinePssh(
const CdmInitData& init_data, CdmInitData* output) {
BufferReader reader(
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length());
bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
CdmInitData* output) {
BufferReader reader(reinterpret_cast<const uint8_t*>(init_data.data()),
init_data.length());
// TODO(kqyang): Extracted from an actual init_data;
// Need to find out where it comes from.
// Widevine's registered system ID.
static const uint8_t kWidevineSystemId[] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
};
// one PSSH blob consists of:
// 4 byte size of the PSSH atom, inclusive
// "pssh"
// 4 byte flags, value 0
// 16 byte system id
// 4 byte size of PSSH data, exclusive
// one PSSH box consists of:
// 4 byte size of the atom, inclusive. (0 means the rest of the buffer.)
// 4 byte atom type, "pssh".
// (optional, if size == 1) 8 byte size of the atom, inclusive.
// 1 byte version, value 0 or 1. (skip if larger.)
// 3 byte flags, value 0. (ignored.)
// 16 byte system id.
// (optional, if version == 1) 4 byte key ID count. (K)
// (optional, if version == 1) K * 16 byte key ID.
// 4 byte size of PSSH data, exclusive. (N)
// N byte PSSH data.
while (1) {
// size of PSSH atom, used for skipping
uint32_t size;
if (!reader.Read4(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
size_t start_pos = reader.pos();
// atom size, used for skipping.
uint64_t size;
if (!reader.Read4Into8(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
return false;
}
std::vector<uint8_t> atom_type;
if (!reader.ReadVec(&atom_type, 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
return false;
}
if (size == 1) {
if (!reader.Read8(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
"size.");
return false;
}
} else if (size == 0) {
size = reader.size() - start_pos;
}
// "pssh"
std::vector<uint8_t> pssh;
if (!reader.ReadVec(&pssh, 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
return false;
if (memcmp(&atom_type[0], "pssh", 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present.");
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
"atom.");
return false;
}
continue;
}
if (memcmp(&pssh[0], "pssh", 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
// version
uint8_t version;
if (!reader.Read1(&version)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
return false;
}
// flags
uint32_t flags;
if (!reader.Read4(&flags)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
return false;
if (version > 1) {
// unrecognized version - skip.
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
"atom.");
return false;
}
continue;
}
if (flags != 0) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
// flags
if (!reader.SkipBytes(3)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
return false;
}
// system id
std::vector<uint8_t> system_id;
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID.");
return false;
}
if (memcmp(&system_id[0], kWidevineSystemId,
sizeof(kWidevineSystemId))) {
// skip the remaining contents of the atom,
// after size field, atom name, flags and system id
if (!reader.SkipBytes(
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
// skip non-Widevine PSSH boxes.
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
"atom.");
return false;
}
continue;
}
// size of PSSH box
uint32_t pssh_length;
if (!reader.Read4(&pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
if (version == 1) {
// v1 has additional fields for key IDs. We can skip them.
uint32_t num_key_ids;
if (!reader.Read4(&num_key_ids)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read num key IDs.");
return false;
}
if (!reader.SkipBytes(num_key_ids * 16)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip key IDs.");
return false;
}
}
// size of PSSH data
uint32_t data_length;
if (!reader.Read4(&data_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data size.");
return false;
}
output->clear();
if (!reader.ReadString(output, pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
if (!reader.ReadString(output, data_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
return false;
}

View File

@@ -0,0 +1,165 @@
// Copyright 2015 Google Inc. All Rights Reserved.
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "initialization_data.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
// References:
// [1] http://dashif.org/identifiers/content-protection/
// [2] http://www.w3.org/TR/encrypted-media/cenc-format.html#common-system
namespace {
const std::string kWidevinePssh = wvcdm::a2bs_hex(
// Widevine PSSH box
"00000042" // atom size
"70737368" // atom type="pssh"
"00000000" // v0, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
const std::string kWidevinePsshFirst = wvcdm::a2bs_hex(
// first PSSH box, Widevine
"00000042" // atom size
"70737368" // atom type "pssh"
"00000000" // v0, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"
// second PSSH box, Playready [1]
"00000028" // atom size
"70737368" // atom type "pssh"
"00000000" // v0, flags=0
"9a04f07998404286ab92e65be0885f95" // system id (PlayReady)
"00000008" // data size
// arbitrary data:
"0102030405060708");
const std::string kWidevinePsshAfterV0Pssh = wvcdm::a2bs_hex(
// first PSSH box, Playready [1]
"00000028" // atom size
"70737368" // atom type "pssh"
"00000000" // v0, flags=0
"9a04f07998404286ab92e65be0885f95" // system id (PlayReady)
"00000008" // data size
// arbitrary data:
"0102030405060708"
// second PSSH box, Widevine
"00000042" // atom size
"70737368" // atom type "pssh"
"00000000" // v0, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
const std::string kWidevinePsshAfterNonZeroFlags = wvcdm::a2bs_hex(
// first PSSH box, Playready [1]
"00000028" // atom size
"70737368" // atom type "pssh"
"00abcdef" // v0, flags=abcdef
"9a04f07998404286ab92e65be0885f95" // system id (PlayReady)
"00000008" // data size
// arbitrary data:
"0102030405060708"
// second PSSH box, Widevine
"00000042" // atom size
"70737368" // atom type "pssh"
"00000000" // v0, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
const std::string kWidevinePsshAfterV1Pssh = wvcdm::a2bs_hex(
// first PSSH box, generic CENC [2]
"00000044" // atom size
"70737368" // atom type "pssh"
"01000000" // v1, flags=0
"1077efecc0b24d02ace33c1e52e2fb4b" // system id (generic CENC)
"00000002" // key ID count
"30313233343536373839303132333435" // key ID="0123456789012345"
"38393031323334354142434445464748" // key ID="ABCDEFGHIJKLMNOP"
"00000000" // data size=0
// second PSSH box, Widevine
"00000042" // atom size
"70737368" // atom type "pssh"
"00000000" // v0, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
const std::string kWidevineV1Pssh = wvcdm::a2bs_hex(
// Widevine PSSH box, v1 format
"00000044" // atom size
"70737368" // atom type "pssh"
"01000000" // v1, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000002" // key ID count
"30313233343536373839303132333435" // key ID="0123456789012345"
"38393031323334354142434445464748" // key ID="ABCDEFGHIJKLMNOP"
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
const std::string kOtherBoxFirst = wvcdm::a2bs_hex(
// first box, not a PSSH box
"00000018" // atom size
"77686174" // atom type "what"
"deadbeefdeadbeefdeadbeefdeadbeef" // garbage box data
// second box, a Widevine PSSH box
"00000042" // atom size
"70737368" // atom type "pssh"
"00000000" // v0, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
const std::string kZeroSizedPsshBox = wvcdm::a2bs_hex(
// Widevine PSSH box
"00000000" // atom size (whole buffer)
"70737368" // atom type="pssh"
"00000000" // v0, flags=0
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
"00000022" // data size
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
class InitializationDataTest
: public ::testing::TestWithParam<std::string> {};
} // namespace
namespace wvcdm {
TEST_P(InitializationDataTest, Parse) {
InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, GetParam());
EXPECT_FALSE(init_data.IsEmpty());
}
INSTANTIATE_TEST_CASE_P(
ParsePssh, InitializationDataTest,
::testing::Values(
kWidevinePssh,
kWidevinePsshFirst,
kWidevinePsshAfterV0Pssh,
kWidevinePsshAfterNonZeroFlags,
kWidevinePsshAfterV1Pssh,
kWidevineV1Pssh,
kOtherBoxFirst,
kZeroSizedPsshBox
));
} // namespace wvcdm

View File

@@ -31,6 +31,10 @@ test_name := http_socket_test
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk
test_name := initialization_data_unittest
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk
test_name := license_unittest
test_src_dir := ../core/test
include $(LOCAL_PATH)/unit-test.mk