Line data Source code
1 : /*
2 : * Copyright 2015, Mozilla Foundation and contributors
3 : *
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
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
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.
15 : */
16 :
17 : #include "PsshParser.h"
18 :
19 : #include "mozilla/Assertions.h"
20 : #include "mozilla/EndianUtils.h"
21 : #include "mozilla/Move.h"
22 : #include <memory.h>
23 : #include <algorithm>
24 : #include <assert.h>
25 : #include <limits>
26 :
27 : // Stripped down version of mp4_demuxer::ByteReader, stripped down to make it
28 : // easier to link into ClearKey DLL and gtest.
29 : class ByteReader
30 : {
31 : public:
32 0 : ByteReader(const uint8_t* aData, size_t aSize)
33 0 : : mPtr(aData), mRemaining(aSize), mLength(aSize)
34 : {
35 0 : }
36 :
37 0 : size_t Offset() const
38 : {
39 0 : return mLength - mRemaining;
40 : }
41 :
42 0 : size_t Remaining() const { return mRemaining; }
43 :
44 0 : size_t Length() const { return mLength; }
45 :
46 : bool CanRead8() const { return mRemaining >= 1; }
47 :
48 0 : uint8_t ReadU8()
49 : {
50 0 : auto ptr = Read(1);
51 0 : if (!ptr) {
52 0 : MOZ_ASSERT(false);
53 : return 0;
54 : }
55 0 : return *ptr;
56 : }
57 :
58 0 : bool CanRead32() const { return mRemaining >= 4; }
59 :
60 0 : uint32_t ReadU32()
61 : {
62 0 : auto ptr = Read(4);
63 0 : if (!ptr) {
64 0 : MOZ_ASSERT(false);
65 : return 0;
66 : }
67 0 : return mozilla::BigEndian::readUint32(ptr);
68 : }
69 :
70 0 : const uint8_t* Read(size_t aCount)
71 : {
72 0 : if (aCount > mRemaining) {
73 0 : mRemaining = 0;
74 0 : return nullptr;
75 : }
76 0 : mRemaining -= aCount;
77 :
78 0 : const uint8_t* result = mPtr;
79 0 : mPtr += aCount;
80 :
81 0 : return result;
82 : }
83 :
84 0 : const uint8_t* Seek(size_t aOffset)
85 : {
86 0 : if (aOffset > mLength) {
87 0 : MOZ_ASSERT(false);
88 : return nullptr;
89 : }
90 :
91 0 : mPtr = mPtr - Offset() + aOffset;
92 0 : mRemaining = mLength - aOffset;
93 0 : return mPtr;
94 : }
95 :
96 : private:
97 : const uint8_t* mPtr;
98 : size_t mRemaining;
99 : const size_t mLength;
100 : };
101 :
102 : #define FOURCC(a,b,c,d) ((a << 24) + (b << 16) + (c << 8) + d)
103 :
104 : // System ID identifying the cenc v2 pssh box format; specified at:
105 : // https://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/cenc-format.html
106 : const uint8_t kSystemID[] = {
107 : 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
108 : 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
109 : };
110 :
111 : bool
112 0 : ParseCENCInitData(const uint8_t* aInitData,
113 : uint32_t aInitDataSize,
114 : std::vector<std::vector<uint8_t>>& aOutKeyIds)
115 : {
116 0 : aOutKeyIds.clear();
117 0 : std::vector<std::vector<uint8_t>> keyIds;
118 0 : ByteReader reader(aInitData, aInitDataSize);
119 0 : while (reader.CanRead32()) {
120 : // Box size. For the common system Id, ignore this, as some useragents
121 : // handle invalid box sizes.
122 0 : const size_t start = reader.Offset();
123 0 : const size_t size = reader.ReadU32();
124 0 : if (size > std::numeric_limits<size_t>::max() - start) {
125 : // Ensure 'start + size' calculation below can't overflow.
126 0 : return false;
127 : }
128 0 : const size_t end = start + size;
129 0 : if (end > reader.Length()) {
130 : // Ridiculous sized box.
131 0 : return false;
132 : }
133 :
134 : // PSSH box type.
135 0 : if (!reader.CanRead32()) {
136 0 : return false;
137 : }
138 0 : uint32_t box = reader.ReadU32();
139 0 : if (box != FOURCC('p','s','s','h')) {
140 0 : return false;
141 : }
142 :
143 : // 1 byte version, 3 bytes flags.
144 0 : if (!reader.CanRead32()) {
145 0 : return false;
146 : }
147 0 : uint8_t version = reader.ReadU8();
148 0 : if (version != 1) {
149 : // Ignore pssh boxes with wrong version.
150 0 : reader.Seek(std::max<size_t>(reader.Offset(), end));
151 0 : continue;
152 : }
153 0 : reader.Read(3); // skip flags.
154 :
155 : // SystemID
156 0 : const uint8_t* sid = reader.Read(sizeof(kSystemID));
157 0 : if (!sid) {
158 : // Insufficient bytes to read SystemID.
159 0 : return false;
160 : }
161 :
162 0 : if (memcmp(kSystemID, sid, sizeof(kSystemID))) {
163 : // Ignore pssh boxes with wrong system ID.
164 0 : reader.Seek(std::max<size_t>(reader.Offset(), end));
165 0 : continue;
166 : }
167 :
168 0 : if (!reader.CanRead32()) {
169 0 : return false;
170 : }
171 0 : uint32_t kidCount = reader.ReadU32();
172 :
173 0 : if (kidCount * CENC_KEY_LEN > reader.Remaining()) {
174 : // Not enough bytes remaining to read all keys.
175 0 : return false;
176 : }
177 :
178 0 : for (uint32_t i = 0; i < kidCount; i++) {
179 0 : const uint8_t* kid = reader.Read(CENC_KEY_LEN);
180 0 : keyIds.push_back(std::vector<uint8_t>(kid, kid + CENC_KEY_LEN));
181 : }
182 :
183 : // Size of extra data. EME CENC format spec says datasize should
184 : // always be 0. We explicitly read the datasize, in case the box
185 : // size was 0, so that we get to the end of the box.
186 0 : if (!reader.CanRead32()) {
187 0 : return false;
188 : }
189 0 : reader.ReadU32();
190 :
191 : // Jump forwards to the end of the box, skipping any padding.
192 0 : if (size) {
193 0 : reader.Seek(end);
194 : }
195 : }
196 0 : aOutKeyIds = mozilla::Move(keyIds);
197 0 : return true;
198 : }
|