summaryrefslogtreecommitdiffstats
path: root/media/cdm/cenc_utils.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/cdm/cenc_utils.cc')
-rw-r--r--media/cdm/cenc_utils.cc154
1 files changed, 154 insertions, 0 deletions
diff --git a/media/cdm/cenc_utils.cc b/media/cdm/cenc_utils.cc
new file mode 100644
index 0000000..2eeafd7
--- /dev/null
+++ b/media/cdm/cenc_utils.cc
@@ -0,0 +1,154 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/cdm/cenc_utils.h"
+
+#include "media/base/bit_reader.h"
+
+namespace media {
+
+// The initialization data for encrypted media files using the ISO Common
+// Encryption ('cenc') protection scheme may contain one or more protection
+// system specific header ('pssh') boxes.
+// ref: https://w3c.github.io/encrypted-media/cenc-format.html
+//
+// The format of a 'pssh' box is as follows:
+// unsigned int(32) size;
+// unsigned int(32) type = "pssh";
+// if (size==1) {
+// unsigned int(64) largesize;
+// } else if (size==0) {
+// -- box extends to end of file
+// }
+// unsigned int(8) version;
+// bit(24) flags;
+// unsigned int(8)[16] SystemID;
+// if (version > 0)
+// {
+// unsigned int(32) KID_count;
+// {
+// unsigned int(8)[16] KID;
+// } [KID_count]
+// }
+// unsigned int(32) DataSize;
+// unsigned int(8)[DataSize] Data;
+
+// Minimum size of a 'pssh' box includes all the required fields (size, type,
+// version, flags, SystemID, DataSize).
+const int kMinimumBoxSizeInBytes = 32;
+
+// SystemID for the Common System.
+// https://w3c.github.io/encrypted-media/cenc-format.html#common-system
+const uint8 kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec,
+ 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e,
+ 0x52, 0xe2, 0xfb, 0x4b };
+
+#define RCHECK(x) \
+ do { \
+ if (!(x)) \
+ return false; \
+ } while (0)
+
+// Helper function to read up to 32 bits from a bit stream.
+static uint32 ReadBits(BitReader* reader, int num_bits) {
+ DCHECK_GE(reader->bits_available(), num_bits);
+ DCHECK((num_bits > 0) && (num_bits <= 32));
+ uint32 value;
+ reader->ReadBits(num_bits, &value);
+ return value;
+}
+
+// Checks whether the next 16 bytes matches the Common SystemID.
+// Assumes |reader| has enough data.
+static bool IsCommonSystemID(BitReader* reader) {
+ for (uint32 i = 0; i < arraysize(kCommonSystemId); ++i) {
+ if (ReadBits(reader, 8) != kCommonSystemId[i])
+ return false;
+ }
+ return true;
+}
+
+bool GetKeyIdsForCommonSystemId(const uint8* input,
+ int input_length,
+ std::vector<std::vector<uint8>>* key_ids) {
+ int offset = 0;
+ std::vector<std::vector<uint8>> result;
+
+ while (offset < input_length) {
+ BitReader reader(input + offset, input_length - offset);
+
+ // Enough data for a miniumum size 'pssh' box?
+ RCHECK(reader.bits_available() >= kMinimumBoxSizeInBytes * 8);
+
+ uint32 size = ReadBits(&reader, 32);
+
+ // Must be a 'pssh' box or else fail.
+ RCHECK(ReadBits(&reader, 8) == 'p');
+ RCHECK(ReadBits(&reader, 8) == 's');
+ RCHECK(ReadBits(&reader, 8) == 's');
+ RCHECK(ReadBits(&reader, 8) == 'h');
+
+ if (size == 1) {
+ // If largesize > 2**32 it is too big.
+ RCHECK(ReadBits(&reader, 32) == 0);
+ size = ReadBits(&reader, 32);
+ } else if (size == 0) {
+ size = input_length - offset;
+ }
+
+ // Check that the buffer contains at least size bytes.
+ RCHECK(static_cast<uint32>(input_length - offset) >= size);
+
+ // Update offset to point at the next 'pssh' box (may not be one).
+ offset += size;
+
+ // Check the version, as KIDs only available if version > 0.
+ uint8 version = ReadBits(&reader, 8);
+ if (version == 0)
+ continue;
+
+ // flags must be 0. If not, assume incorrect 'pssh' box and move to the
+ // next one.
+ if (ReadBits(&reader, 24) != 0)
+ continue;
+
+ // Validate SystemID
+ RCHECK(static_cast<uint32>(reader.bits_available()) >=
+ arraysize(kCommonSystemId) * 8);
+ if (!IsCommonSystemID(&reader))
+ continue; // Not Common System, so try the next pssh box.
+
+ // Since version > 0, next field is the KID_count.
+ RCHECK(static_cast<uint32>(reader.bits_available()) >= sizeof(uint32) * 8);
+ uint32 count = ReadBits(&reader, 32);
+
+ if (count == 0)
+ continue;
+
+ // Make sure there is enough data for all the KIDs specified, and then
+ // extract them.
+ RCHECK(static_cast<uint32>(reader.bits_available()) > count * 16 * 8);
+ while (count > 0) {
+ std::vector<uint8> key;
+ key.reserve(16);
+ for (int i = 0; i < 16; ++i) {
+ key.push_back(ReadBits(&reader, 8));
+ }
+ result.push_back(key);
+ --count;
+ }
+
+ // Don't bother checking DataSize and Data.
+ }
+
+ key_ids->swap(result);
+
+ // TODO(jrummell): This should return true only if there was at least one
+ // key ID present. However, numerous test files don't contain the 'pssh' box
+ // for Common Format, so no keys are found. http://crbug.com/460308
+ return true;
+}
+
+} // namespace media