Backed out 2 changesets (bug 1855992) for causing talos failures @ mozilla::net:...
[gecko.git] / media / psshparser / PsshParser.cpp
blob9a479791640f6edcc48ecb5aa16eda19fe44632e
1 /*
2 * Copyright 2015, Mozilla Foundation and contributors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "PsshParser.h"
19 #include <assert.h>
20 #include <memory.h>
22 #include <algorithm>
23 #include <limits>
24 #include <utility>
26 #include "mozilla/Assertions.h"
27 #include "mozilla/EndianUtils.h"
29 // Stripped down version of mp4_demuxer::ByteReader, stripped down to make it
30 // easier to link into ClearKey DLL and gtest.
31 class ByteReader {
32 public:
33 ByteReader(const uint8_t* aData, size_t aSize)
34 : mPtr(aData), mRemaining(aSize), mLength(aSize) {}
36 size_t Offset() const { return mLength - mRemaining; }
38 size_t Remaining() const { return mRemaining; }
40 size_t Length() const { return mLength; }
42 bool CanRead8() const { return mRemaining >= 1; }
44 uint8_t ReadU8() {
45 auto ptr = Read(1);
46 if (!ptr) {
47 MOZ_ASSERT(false);
48 return 0;
50 return *ptr;
53 bool CanRead32() const { return mRemaining >= 4; }
55 uint32_t ReadU32() {
56 auto ptr = Read(4);
57 if (!ptr) {
58 MOZ_ASSERT(false);
59 return 0;
61 return mozilla::BigEndian::readUint32(ptr);
64 const uint8_t* Read(size_t aCount) {
65 if (aCount > mRemaining) {
66 mRemaining = 0;
67 return nullptr;
69 mRemaining -= aCount;
71 const uint8_t* result = mPtr;
72 mPtr += aCount;
74 return result;
77 const uint8_t* Seek(size_t aOffset) {
78 if (aOffset > mLength) {
79 MOZ_ASSERT(false);
80 return nullptr;
83 mPtr = mPtr - Offset() + aOffset;
84 mRemaining = mLength - aOffset;
85 return mPtr;
88 private:
89 const uint8_t* mPtr;
90 size_t mRemaining;
91 const size_t mLength;
94 #define FOURCC(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d)
96 // System ID identifying the cenc v2 pssh box format; specified at:
97 // https://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/cenc-format.html
98 const uint8_t kSystemID[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
99 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b};
101 bool ParseCENCInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
102 std::vector<std::vector<uint8_t>>& aOutKeyIds) {
103 aOutKeyIds.clear();
104 std::vector<std::vector<uint8_t>> keyIds;
105 ByteReader reader(aInitData, aInitDataSize);
106 while (reader.CanRead32()) {
107 // Box size. For the common system Id, ignore this, as some useragents
108 // handle invalid box sizes.
109 const size_t start = reader.Offset();
110 const size_t size = reader.ReadU32();
111 if (size > std::numeric_limits<size_t>::max() - start) {
112 // Ensure 'start + size' calculation below can't overflow.
113 return false;
115 const size_t end = start + size;
116 if (end > reader.Length()) {
117 // Ridiculous sized box.
118 return false;
121 // PSSH box type.
122 if (!reader.CanRead32()) {
123 return false;
125 uint32_t box = reader.ReadU32();
126 if (box != FOURCC('p', 's', 's', 'h')) {
127 return false;
130 // 1 byte version, 3 bytes flags.
131 if (!reader.CanRead32()) {
132 return false;
134 uint8_t version = reader.ReadU8();
135 if (version != 1) {
136 // Ignore pssh boxes with wrong version.
137 reader.Seek(std::max<size_t>(reader.Offset(), end));
138 continue;
140 reader.Read(3); // skip flags.
142 // SystemID
143 const uint8_t* sid = reader.Read(sizeof(kSystemID));
144 if (!sid) {
145 // Insufficient bytes to read SystemID.
146 return false;
149 if (memcmp(kSystemID, sid, sizeof(kSystemID))) {
150 // Ignore pssh boxes with wrong system ID.
151 reader.Seek(std::max<size_t>(reader.Offset(), end));
152 continue;
155 if (!reader.CanRead32()) {
156 return false;
158 uint32_t kidCount = reader.ReadU32();
160 if (kidCount * CENC_KEY_LEN > reader.Remaining()) {
161 // Not enough bytes remaining to read all keys.
162 return false;
165 for (uint32_t i = 0; i < kidCount; i++) {
166 const uint8_t* kid = reader.Read(CENC_KEY_LEN);
167 keyIds.push_back(std::vector<uint8_t>(kid, kid + CENC_KEY_LEN));
170 // Size of extra data. EME CENC format spec says datasize should
171 // always be 0. We explicitly read the datasize, in case the box
172 // size was 0, so that we get to the end of the box.
173 if (!reader.CanRead32()) {
174 return false;
176 reader.ReadU32();
178 // Jump forwards to the end of the box, skipping any padding.
179 if (size) {
180 reader.Seek(end);
183 aOutKeyIds = std::move(keyIds);
184 return true;