Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/media/psshparser/PsshParser.cpp
Line
Count
Source (jump to first uncovered line)
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
  ByteReader(const uint8_t* aData, size_t aSize)
33
    : mPtr(aData), mRemaining(aSize), mLength(aSize)
34
0
  {
35
0
  }
36
37
  size_t Offset() const
38
0
  {
39
0
    return mLength - mRemaining;
40
0
  }
41
42
0
  size_t Remaining() const { return mRemaining; }
43
44
0
  size_t Length() const { return mLength; }
45
46
0
  bool CanRead8() const { return mRemaining >= 1; }
47
48
  uint8_t ReadU8()
49
0
  {
50
0
    auto ptr = Read(1);
51
0
    if (!ptr) {
52
0
      MOZ_ASSERT(false);
53
0
      return 0;
54
0
    }
55
0
    return *ptr;
56
0
  }
57
58
0
  bool CanRead32() const { return mRemaining >= 4; }
59
60
  uint32_t ReadU32()
61
0
  {
62
0
    auto ptr = Read(4);
63
0
    if (!ptr) {
64
0
      MOZ_ASSERT(false);
65
0
      return 0;
66
0
    }
67
0
    return mozilla::BigEndian::readUint32(ptr);
68
0
  }
69
70
  const uint8_t* Read(size_t aCount)
71
0
  {
72
0
    if (aCount > mRemaining) {
73
0
      mRemaining = 0;
74
0
      return nullptr;
75
0
    }
76
0
    mRemaining -= aCount;
77
0
78
0
    const uint8_t* result = mPtr;
79
0
    mPtr += aCount;
80
0
81
0
    return result;
82
0
  }
83
84
  const uint8_t* Seek(size_t aOffset)
85
0
  {
86
0
    if (aOffset > mLength) {
87
0
      MOZ_ASSERT(false);
88
0
      return nullptr;
89
0
    }
90
0
91
0
    mPtr = mPtr - Offset() + aOffset;
92
0
    mRemaining = mLength - aOffset;
93
0
    return mPtr;
94
0
  }
95
96
private:
97
  const uint8_t* mPtr;
98
  size_t mRemaining;
99
  const size_t mLength;
100
};
101
102
0
#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
ParseCENCInitData(const uint8_t* aInitData,
113
                  uint32_t aInitDataSize,
114
                  std::vector<std::vector<uint8_t>>& aOutKeyIds)
115
0
{
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
0
    // Box size. For the common system Id, ignore this, as some useragents
121
0
    // 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
0
      // Ensure 'start + size' calculation below can't overflow.
126
0
      return false;
127
0
    }
128
0
    const size_t end = start + size;
129
0
    if (end > reader.Length()) {
130
0
      // Ridiculous sized box.
131
0
      return false;
132
0
    }
133
0
134
0
    // PSSH box type.
135
0
    if (!reader.CanRead32()) {
136
0
      return false;
137
0
    }
138
0
    uint32_t box = reader.ReadU32();
139
0
    if (box != FOURCC('p','s','s','h')) {
140
0
      return false;
141
0
    }
142
0
143
0
    // 1 byte version, 3 bytes flags.
144
0
    if (!reader.CanRead32()) {
145
0
      return false;
146
0
    }
147
0
    uint8_t version = reader.ReadU8();
148
0
    if (version != 1) {
149
0
      // Ignore pssh boxes with wrong version.
150
0
      reader.Seek(std::max<size_t>(reader.Offset(), end));
151
0
      continue;
152
0
    }
153
0
    reader.Read(3); // skip flags.
154
0
155
0
    // SystemID
156
0
    const uint8_t* sid = reader.Read(sizeof(kSystemID));
157
0
    if (!sid) {
158
0
      // Insufficient bytes to read SystemID.
159
0
      return false;
160
0
    }
161
0
162
0
    if (memcmp(kSystemID, sid, sizeof(kSystemID))) {
163
0
      // Ignore pssh boxes with wrong system ID.
164
0
      reader.Seek(std::max<size_t>(reader.Offset(), end));
165
0
      continue;
166
0
    }
167
0
168
0
    if (!reader.CanRead32()) {
169
0
      return false;
170
0
    }
171
0
    uint32_t kidCount = reader.ReadU32();
172
0
173
0
    if (kidCount * CENC_KEY_LEN > reader.Remaining()) {
174
0
      // Not enough bytes remaining to read all keys.
175
0
      return false;
176
0
    }
177
0
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
0
    }
182
0
183
0
    // Size of extra data. EME CENC format spec says datasize should
184
0
    // always be 0. We explicitly read the datasize, in case the box
185
0
    // size was 0, so that we get to the end of the box.
186
0
    if (!reader.CanRead32()) {
187
0
      return false;
188
0
    }
189
0
    reader.ReadU32();
190
0
191
0
    // Jump forwards to the end of the box, skipping any padding.
192
0
    if (size) {
193
0
      reader.Seek(end);
194
0
    }
195
0
  }
196
0
  aOutKeyIds = std::move(keyIds);
197
0
  return true;
198
0
}