PsshParser.cpp (4999B)
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 <assert.h> 20 #include <memory.h> 21 22 #include <algorithm> 23 #include <limits> 24 #include <utility> 25 26 #include "mozilla/Assertions.h" 27 #include "mozilla/EndianUtils.h" 28 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) {} 35 36 size_t Offset() const { return mLength - mRemaining; } 37 38 size_t Remaining() const { return mRemaining; } 39 40 size_t Length() const { return mLength; } 41 42 bool CanRead8() const { return mRemaining >= 1; } 43 44 uint8_t ReadU8() { 45 auto ptr = Read(1); 46 if (!ptr) { 47 MOZ_ASSERT(false); 48 return 0; 49 } 50 return *ptr; 51 } 52 53 bool CanRead32() const { return mRemaining >= 4; } 54 55 uint32_t ReadU32() { 56 auto ptr = Read(4); 57 if (!ptr) { 58 MOZ_ASSERT(false); 59 return 0; 60 } 61 return mozilla::BigEndian::readUint32(ptr); 62 } 63 64 const uint8_t* Read(size_t aCount) { 65 if (aCount > mRemaining) { 66 mRemaining = 0; 67 return nullptr; 68 } 69 mRemaining -= aCount; 70 71 const uint8_t* result = mPtr; 72 mPtr += aCount; 73 74 return result; 75 } 76 77 const uint8_t* Seek(size_t aOffset) { 78 if (aOffset > mLength) { 79 MOZ_ASSERT(false); 80 return nullptr; 81 } 82 83 mPtr = mPtr - Offset() + aOffset; 84 mRemaining = mLength - aOffset; 85 return mPtr; 86 } 87 88 private: 89 const uint8_t* mPtr; 90 size_t mRemaining; 91 const size_t mLength; 92 }; 93 94 #define FOURCC(a, b, c, d) ((a << 24) + (b << 16) + (c << 8) + d) 95 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}; 100 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; 114 } 115 const size_t end = start + size; 116 if (end > reader.Length()) { 117 // Ridiculous sized box. 118 return false; 119 } 120 121 // PSSH box type. 122 if (!reader.CanRead32()) { 123 return false; 124 } 125 uint32_t box = reader.ReadU32(); 126 if (box != FOURCC('p', 's', 's', 'h')) { 127 return false; 128 } 129 130 // 1 byte version, 3 bytes flags. 131 if (!reader.CanRead32()) { 132 return false; 133 } 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; 139 } 140 reader.Read(3); // skip flags. 141 142 // SystemID 143 const uint8_t* sid = reader.Read(sizeof(kSystemID)); 144 if (!sid) { 145 // Insufficient bytes to read SystemID. 146 return false; 147 } 148 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; 153 } 154 155 if (!reader.CanRead32()) { 156 return false; 157 } 158 uint32_t kidCount = reader.ReadU32(); 159 160 if (kidCount * CENC_KEY_LEN > reader.Remaining()) { 161 // Not enough bytes remaining to read all keys. 162 return false; 163 } 164 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)); 168 } 169 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; 175 } 176 reader.ReadU32(); 177 178 // Jump forwards to the end of the box, skipping any padding. 179 if (size) { 180 reader.Seek(end); 181 } 182 } 183 aOutKeyIds = std::move(keyIds); 184 return true; 185 }