gecko-dev/media/psshparser/PsshParser.cpp

209 строки
5.2 KiB
C++

/*
* Copyright 2015, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PsshParser.h"
#include "mozilla/Assertions.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Move.h"
#include <memory.h>
#include <algorithm>
#include <assert.h>
#include <limits>
// Stripped down version of mp4_demuxer::ByteReader, stripped down to make it
// easier to link into ClearKey DLL and gtest.
class ByteReader
{
public:
ByteReader(const uint8_t* aData, size_t aSize)
: mPtr(aData), mRemaining(aSize), mLength(aSize)
{
}
size_t Offset() const
{
return mLength - mRemaining;
}
size_t Remaining() const { return mRemaining; }
size_t Length() const { return mLength; }
bool CanRead8() const { return mRemaining >= 1; }
uint8_t ReadU8()
{
auto ptr = Read(1);
if (!ptr) {
MOZ_ASSERT(false);
return 0;
}
return *ptr;
}
bool CanRead32() const { return mRemaining >= 4; }
uint32_t ReadU32()
{
auto ptr = Read(4);
if (!ptr) {
MOZ_ASSERT(false);
return 0;
}
return mozilla::BigEndian::readUint32(ptr);
}
const uint8_t* Read(size_t aCount)
{
if (aCount > mRemaining) {
mRemaining = 0;
return nullptr;
}
mRemaining -= aCount;
const uint8_t* result = mPtr;
mPtr += aCount;
return result;
}
const uint8_t* Seek(size_t aOffset)
{
if (aOffset > mLength) {
MOZ_ASSERT(false);
return nullptr;
}
mPtr = mPtr - Offset() + aOffset;
mRemaining = mLength - aOffset;
return mPtr;
}
private:
const uint8_t* mPtr;
size_t mRemaining;
const size_t mLength;
};
#define FOURCC(a,b,c,d) ((a << 24) + (b << 16) + (c << 8) + d)
// System ID identifying the cenc v2 pssh box format; specified at:
// https://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/cenc-format.html
const uint8_t kSystemID[] = {
0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
};
const uint8_t kPrimetimeID[] = {
0xf2, 0x39, 0xe7, 0x69, 0xef, 0xa3, 0x48, 0x50,
0x9c, 0x16, 0xa9, 0x03, 0xc6, 0x93, 0x2e, 0xfb
};
bool
ParseCENCInitData(const uint8_t* aInitData,
uint32_t aInitDataSize,
std::vector<std::vector<uint8_t>>& aOutKeyIds)
{
aOutKeyIds.clear();
std::vector<std::vector<uint8_t>> keyIds;
ByteReader reader(aInitData, aInitDataSize);
while (reader.CanRead32()) {
// Box size. For the common system Id, ignore this, as some useragents
// handle invalid box sizes.
const size_t start = reader.Offset();
const size_t size = reader.ReadU32();
if (size > std::numeric_limits<size_t>::max() - start) {
// Ensure 'start + size' calculation below can't overflow.
return false;
}
const size_t end = start + size;
if (end > reader.Length()) {
// Ridiculous sized box.
return false;
}
// PSSH box type.
if (!reader.CanRead32()) {
return false;
}
uint32_t box = reader.ReadU32();
if (box != FOURCC('p','s','s','h')) {
return false;
}
// 1 byte version, 3 bytes flags.
if (!reader.CanRead32()) {
return false;
}
uint8_t version = reader.ReadU8();
if (version != 1) {
// Ignore pssh boxes with wrong version.
reader.Seek(std::max<size_t>(reader.Offset(), end));
continue;
}
reader.Read(3); // skip flags.
// SystemID
const uint8_t* sid = reader.Read(sizeof(kSystemID));
if (!sid) {
// Insufficient bytes to read SystemID.
return false;
}
if (!memcmp(kPrimetimeID, sid, sizeof(kSystemID))) {
// Allow legacy Primetime key system PSSH boxes, which
// don't conform to common encryption format.
return true;
}
if (memcmp(kSystemID, sid, sizeof(kSystemID))) {
// Ignore pssh boxes with wrong system ID.
reader.Seek(std::max<size_t>(reader.Offset(), end));
continue;
}
if (!reader.CanRead32()) {
return false;
}
uint32_t kidCount = reader.ReadU32();
if (kidCount * CENC_KEY_LEN > reader.Remaining()) {
// Not enough bytes remaining to read all keys.
return false;
}
for (uint32_t i = 0; i < kidCount; i++) {
const uint8_t* kid = reader.Read(CENC_KEY_LEN);
keyIds.push_back(std::vector<uint8_t>(kid, kid + CENC_KEY_LEN));
}
// Size of extra data. EME CENC format spec says datasize should
// always be 0. We explicitly read the datasize, in case the box
// size was 0, so that we get to the end of the box.
if (!reader.CanRead32()) {
return false;
}
reader.ReadU32();
// Jump forwards to the end of the box, skipping any padding.
if (size) {
reader.Seek(end);
}
}
aOutKeyIds = mozilla::Move(keyIds);
return true;
}