OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cdm/cenc_utils.h" | 5 #include "media/cdm/cenc_utils.h" |
6 | 6 |
7 #include "base/stl_util.h" | 7 #include "media/base/bit_reader.h" |
8 #include "media/formats/mp4/box_definitions.h" | |
9 #include "media/formats/mp4/box_reader.h" | |
10 | 8 |
11 namespace media { | 9 namespace media { |
12 | 10 |
13 // The initialization data for encrypted media files using the ISO Common | 11 // The initialization data for encrypted media files using the ISO Common |
14 // Encryption ('cenc') protection scheme may contain one or more protection | 12 // Encryption ('cenc') protection scheme may contain one or more protection |
15 // system specific header ('pssh') boxes. | 13 // system specific header ('pssh') boxes. |
16 // ref: https://w3c.github.io/encrypted-media/cenc-format.html | 14 // ref: https://w3c.github.io/encrypted-media/cenc-format.html |
| 15 // |
| 16 // The format of a 'pssh' box is as follows: |
| 17 // unsigned int(32) size; |
| 18 // unsigned int(32) type = "pssh"; |
| 19 // if (size==1) { |
| 20 // unsigned int(64) largesize; |
| 21 // } else if (size==0) { |
| 22 // -- box extends to end of file |
| 23 // } |
| 24 // unsigned int(8) version; |
| 25 // bit(24) flags; |
| 26 // unsigned int(8)[16] SystemID; |
| 27 // if (version > 0) |
| 28 // { |
| 29 // unsigned int(32) KID_count; |
| 30 // { |
| 31 // unsigned int(8)[16] KID; |
| 32 // } [KID_count] |
| 33 // } |
| 34 // unsigned int(32) DataSize; |
| 35 // unsigned int(8)[DataSize] Data; |
| 36 |
| 37 // Minimum size of a 'pssh' box includes all the required fields (size, type, |
| 38 // version, flags, SystemID, DataSize). |
| 39 const int kMinimumBoxSizeInBytes = 32; |
17 | 40 |
18 // SystemID for the Common System. | 41 // SystemID for the Common System. |
19 // https://w3c.github.io/encrypted-media/cenc-format.html#common-system | 42 // https://w3c.github.io/encrypted-media/cenc-format.html#common-system |
20 const uint8_t kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec, | 43 const uint8_t kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec, |
21 0xc0, 0xb2, 0x4d, 0x02, | 44 0xc0, 0xb2, 0x4d, 0x02, |
22 0xac, 0xe3, 0x3c, 0x1e, | 45 0xac, 0xe3, 0x3c, 0x1e, |
23 0x52, 0xe2, 0xfb, 0x4b }; | 46 0x52, 0xe2, 0xfb, 0x4b }; |
24 | 47 |
25 static bool ReadAllPsshBoxes( | 48 #define RCHECK(x) \ |
26 const std::vector<uint8_t>& input, | 49 do { \ |
27 std::vector<mp4::FullProtectionSystemSpecificHeader>* pssh_boxes) { | 50 if (!(x)) \ |
28 DCHECK(!input.empty()); | 51 return false; \ |
| 52 } while (0) |
29 | 53 |
30 // Verify that |input| contains only 'pssh' boxes. ReadAllChildren() is | 54 // Helper function to read up to 32 bits from a bit stream. |
31 // templated, so it checks that each box in |input| matches the box type of | 55 static uint32_t ReadBits(BitReader* reader, int num_bits) { |
32 // the parameter (in this case mp4::ProtectionSystemSpecificHeader is a | 56 DCHECK_GE(reader->bits_available(), num_bits); |
33 // 'pssh' box). mp4::ProtectionSystemSpecificHeader doesn't validate the | 57 DCHECK((num_bits > 0) && (num_bits <= 32)); |
34 // 'pssh' contents, so this simply verifies that |input| only contains | 58 uint32_t value; |
35 // 'pssh' boxes and nothing else. | 59 reader->ReadBits(num_bits, &value); |
36 scoped_ptr<mp4::BoxReader> input_reader( | 60 return value; |
37 mp4::BoxReader::ReadConcatentatedBoxes( | 61 } |
38 vector_as_array(&input), input.size())); | |
39 std::vector<mp4::ProtectionSystemSpecificHeader> raw_pssh_boxes; | |
40 if (!input_reader->ReadAllChildren(&raw_pssh_boxes)) | |
41 return false; | |
42 | 62 |
43 // Now that we have |input| parsed into |raw_pssh_boxes|, reparse each one | 63 // Checks whether the next 16 bytes matches the Common SystemID. |
44 // into a mp4::FullProtectionSystemSpecificHeader, which extracts all the | 64 // Assumes |reader| has enough data. |
45 // relevant fields from the box. Since there may be unparseable 'pssh' boxes | 65 static bool IsCommonSystemID(BitReader* reader) { |
46 // (due to unsupported version, for example), this is done one by one, | 66 for (uint32_t i = 0; i < arraysize(kCommonSystemId); ++i) { |
47 // ignoring any boxes that can't be parsed. | 67 if (ReadBits(reader, 8) != kCommonSystemId[i]) |
48 for (const auto& raw_pssh_box : raw_pssh_boxes) { | 68 return false; |
49 scoped_ptr<mp4::BoxReader> raw_pssh_reader( | 69 } |
50 mp4::BoxReader::ReadConcatentatedBoxes( | 70 return true; |
51 vector_as_array(&raw_pssh_box.raw_box), | 71 } |
52 raw_pssh_box.raw_box.size())); | 72 |
53 // ReadAllChildren() appends any successfully parsed box onto it's | 73 // Checks that |reader| contains a valid 'ppsh' box header. |reader| is updated |
54 // parameter, so |pssh_boxes| will contain the collection of successfully | 74 // to point to the content immediately following the box header. Returns true |
55 // parsed 'pssh' boxes. If an error occurs, try the next box. | 75 // if the header looks valid and |reader| contains enough data for the size of |
56 if (!raw_pssh_reader->ReadAllChildren(pssh_boxes)) | 76 // header. |size| is updated as the computed size of the box header. Otherwise |
57 continue; | 77 // false is returned. |
| 78 static bool ValidBoxHeader(BitReader* reader, uint32* size) { |
| 79 // Enough data for a miniumum size 'pssh' box? |
| 80 uint32 available_bytes = reader->bits_available() / 8; |
| 81 RCHECK(available_bytes >= kMinimumBoxSizeInBytes); |
| 82 |
| 83 *size = ReadBits(reader, 32); |
| 84 |
| 85 // Must be a 'pssh' box or else fail. |
| 86 RCHECK(ReadBits(reader, 8) == 'p'); |
| 87 RCHECK(ReadBits(reader, 8) == 's'); |
| 88 RCHECK(ReadBits(reader, 8) == 's'); |
| 89 RCHECK(ReadBits(reader, 8) == 'h'); |
| 90 |
| 91 if (*size == 1) { |
| 92 // If largesize > 2**32 it is too big. |
| 93 RCHECK(ReadBits(reader, 32) == 0); |
| 94 *size = ReadBits(reader, 32); |
| 95 } else if (*size == 0) { |
| 96 *size = available_bytes; |
58 } | 97 } |
59 | 98 |
60 // Must have successfully parsed at least one 'pssh' box. | 99 // Check that the buffer contains at least size bytes. |
61 return pssh_boxes->size() > 0; | 100 return available_bytes >= *size; |
62 } | 101 } |
63 | 102 |
64 bool ValidatePsshInput(const std::vector<uint8_t>& input) { | 103 bool ValidatePsshInput(const std::vector<uint8_t>& input) { |
65 // No 'pssh' boxes is considered valid. | 104 size_t offset = 0; |
66 if (input.empty()) | 105 while (offset < input.size()) { |
67 return true; | 106 // Create a BitReader over the remaining part of the buffer. |
| 107 BitReader reader(&input[offset], input.size() - offset); |
| 108 uint32 size; |
| 109 RCHECK(ValidBoxHeader(&reader, &size)); |
68 | 110 |
69 std::vector<mp4::FullProtectionSystemSpecificHeader> children; | 111 // Update offset to point at the next 'pssh' box (may not be one). |
70 return ReadAllPsshBoxes(input, &children); | 112 offset += size; |
| 113 } |
| 114 |
| 115 // Only valid if this contains 0 or more 'pssh' boxes. |
| 116 return offset == input.size(); |
71 } | 117 } |
72 | 118 |
73 bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input, | 119 bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input, |
74 KeyIdList* key_ids) { | 120 KeyIdList* key_ids) { |
| 121 size_t offset = 0; |
75 KeyIdList result; | 122 KeyIdList result; |
76 std::vector<uint8_t> common_system_id( | |
77 kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId)); | |
78 | 123 |
79 if (!input.empty()) { | 124 while (offset < input.size()) { |
80 std::vector<mp4::FullProtectionSystemSpecificHeader> children; | 125 BitReader reader(&input[offset], input.size() - offset); |
81 if (!ReadAllPsshBoxes(input, &children)) | 126 uint32 size; |
82 return false; | 127 RCHECK(ValidBoxHeader(&reader, &size)); |
83 | 128 |
84 // Check all children for an appropriate 'pssh' box, concatenating any | 129 // Update offset to point at the next 'pssh' box (may not be one). |
85 // key IDs found. | 130 offset += size; |
86 for (const auto& child : children) { | 131 |
87 if (child.system_id == common_system_id && child.key_ids.size() > 0) | 132 // Check the version, as KIDs only available if version > 0. |
88 result.insert(result.end(), child.key_ids.begin(), child.key_ids.end()); | 133 uint8_t version = ReadBits(&reader, 8); |
| 134 if (version == 0) |
| 135 continue; |
| 136 |
| 137 // flags must be 0. If not, assume incorrect 'pssh' box and move to the |
| 138 // next one. |
| 139 if (ReadBits(&reader, 24) != 0) |
| 140 continue; |
| 141 |
| 142 // Validate SystemID |
| 143 RCHECK(static_cast<uint32_t>(reader.bits_available()) >= |
| 144 arraysize(kCommonSystemId) * 8); |
| 145 if (!IsCommonSystemID(&reader)) |
| 146 continue; // Not Common System, so try the next pssh box. |
| 147 |
| 148 // Since version > 0, next field is the KID_count. |
| 149 RCHECK(static_cast<uint32_t>(reader.bits_available()) >= |
| 150 sizeof(uint32_t) * 8); |
| 151 uint32_t count = ReadBits(&reader, 32); |
| 152 |
| 153 if (count == 0) |
| 154 continue; |
| 155 |
| 156 // Make sure there is enough data for all the KIDs specified, and then |
| 157 // extract them. |
| 158 RCHECK(static_cast<uint32_t>(reader.bits_available()) > count * 16 * 8); |
| 159 while (count > 0) { |
| 160 std::vector<uint8_t> key; |
| 161 key.reserve(16); |
| 162 for (int i = 0; i < 16; ++i) { |
| 163 key.push_back(ReadBits(&reader, 8)); |
| 164 } |
| 165 result.push_back(key); |
| 166 --count; |
89 } | 167 } |
| 168 |
| 169 // Don't bother checking DataSize and Data. |
90 } | 170 } |
91 | 171 |
92 // No matching 'pssh' box found. | 172 key_ids->swap(result); |
| 173 |
93 // TODO(jrummell): This should return true only if there was at least one | 174 // TODO(jrummell): This should return true only if there was at least one |
94 // key ID present. However, numerous test files don't contain the 'pssh' box | 175 // key ID present. However, numerous test files don't contain the 'pssh' box |
95 // for Common Format, so no keys are found. http://crbug.com/460308 | 176 // for Common Format, so no keys are found. http://crbug.com/460308 |
96 key_ids->swap(result); | |
97 return true; | 177 return true; |
98 } | 178 } |
99 | 179 |
100 bool GetPsshData(const std::vector<uint8_t>& input, | |
101 const std::vector<uint8_t>& system_id, | |
102 std::vector<uint8_t>* pssh_data) { | |
103 if (input.empty()) | |
104 return false; | |
105 | |
106 std::vector<mp4::FullProtectionSystemSpecificHeader> children; | |
107 if (!ReadAllPsshBoxes(input, &children)) | |
108 return false; | |
109 | |
110 // Check all children for an appropriate 'pssh' box, returning |data| from | |
111 // the first one found. | |
112 for (const auto& child : children) { | |
113 if (child.system_id == system_id) { | |
114 pssh_data->assign(child.data.begin(), child.data.end()); | |
115 return true; | |
116 } | |
117 } | |
118 | |
119 // No matching 'pssh' box found. | |
120 return false; | |
121 } | |
122 | |
123 } // namespace media | 180 } // namespace media |
OLD | NEW |