diff options
author | jrummell <jrummell@chromium.org> | 2015-02-20 18:17:30 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-21 02:18:18 +0000 |
commit | 0961c42c5a18859d6b4b5202f816c6a1304b3ac5 (patch) | |
tree | d9c44042cf1b2ed4a8bcc0e1ab08f535b61dfffb /media/cdm/cenc_utils.cc | |
parent | 01f8886bebfb9eab8dfb370db065a81a613feee1 (diff) | |
download | chromium_src-0961c42c5a18859d6b4b5202f816c6a1304b3ac5.zip chromium_src-0961c42c5a18859d6b4b5202f816c6a1304b3ac5.tar.gz chromium_src-0961c42c5a18859d6b4b5202f816c6a1304b3ac5.tar.bz2 |
Handle 'cenc' Initialization Data Type in Clear Key.
Initialization data is provided as concatentated PSSH boxes. Parse the
PSSH boxes and extract the keys, if present. Only process the PSSH
boxes that contain the Clear Key SystemID, all other PSSH boxes are
skipped.
This handles both version 0 and version 1 PSSH boxes.
BUG=459850
TEST=new tests pass
Review URL: https://codereview.chromium.org/936953005
Cr-Commit-Position: refs/heads/master@{#317490}
Diffstat (limited to 'media/cdm/cenc_utils.cc')
-rw-r--r-- | media/cdm/cenc_utils.cc | 154 |
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 |