// 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 "base/macros.h" #include "media/formats/mp4/box_definitions.h" #include "media/formats/mp4/box_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 // CENC SystemID for the Common System. // https://w3c.github.io/encrypted-media/cenc-format.html#common-system const uint8_t kCencCommonSystemId[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b}; // Returns true if |input| contains only 1 or more valid 'pssh' boxes, false // otherwise. |pssh_boxes| is updated as the set of parsed 'pssh' boxes. // Note: All boxes in |input| must be 'pssh' boxes. However, if they can't be // properly parsed (e.g. unsupported version), then they will be skipped. static bool ReadAllPsshBoxes( const std::vector& input, std::vector* pssh_boxes) { DCHECK(!input.empty()); // Verify that |input| contains only 'pssh' boxes. // ReadAllChildrenAndCheckFourCC() is templated, so it checks that each // box in |input| matches the box type of the parameter (in this case // mp4::ProtectionSystemSpecificHeader is a 'pssh' box). // mp4::ProtectionSystemSpecificHeader doesn't validate the 'pssh' contents, // so this simply verifies that |input| only contains 'pssh' boxes and // nothing else. scoped_ptr input_reader( mp4::BoxReader::ReadConcatentatedBoxes(input.data(), input.size())); std::vector raw_pssh_boxes; if (!input_reader->ReadAllChildrenAndCheckFourCC(&raw_pssh_boxes)) return false; // Now that we have |input| parsed into |raw_pssh_boxes|, reparse each one // into a mp4::FullProtectionSystemSpecificHeader, which extracts all the // relevant fields from the box. Since there may be unparseable 'pssh' boxes // (due to unsupported version, for example), this is done one by one, // ignoring any boxes that can't be parsed. for (const auto& raw_pssh_box : raw_pssh_boxes) { scoped_ptr raw_pssh_reader( mp4::BoxReader::ReadConcatentatedBoxes(raw_pssh_box.raw_box.data(), raw_pssh_box.raw_box.size())); // ReadAllChildren() appends any successfully parsed box onto it's // parameter, so |pssh_boxes| will contain the collection of successfully // parsed 'pssh' boxes. If an error occurs, try the next box. if (!raw_pssh_reader->ReadAllChildrenAndCheckFourCC(pssh_boxes)) continue; } // Must have successfully parsed at least one 'pssh' box. return pssh_boxes->size() > 0; } bool ValidatePsshInput(const std::vector& input) { // No 'pssh' boxes is considered valid. if (input.empty()) return true; std::vector children; return ReadAllPsshBoxes(input, &children); } bool GetKeyIdsForCommonSystemId(const std::vector& pssh_boxes, KeyIdList* key_ids) { // If there are no 'pssh' boxes then no key IDs found. if (pssh_boxes.empty()) return false; std::vector children; if (!ReadAllPsshBoxes(pssh_boxes, &children)) return false; // Check all children for an appropriate 'pssh' box, returning the // key IDs found. KeyIdList result; std::vector common_system_id( kCencCommonSystemId, kCencCommonSystemId + arraysize(kCencCommonSystemId)); for (const auto& child : children) { if (child.system_id == common_system_id) { key_ids->assign(child.key_ids.begin(), child.key_ids.end()); return key_ids->size() > 0; } } // No matching 'pssh' box found. return false; } bool GetPsshData(const std::vector& input, const std::vector& system_id, std::vector* pssh_data) { if (input.empty()) return false; std::vector children; if (!ReadAllPsshBoxes(input, &children)) return false; // Check all children for an appropriate 'pssh' box, returning |data| from // the first one found. for (const auto& child : children) { if (child.system_id == system_id) { pssh_data->assign(child.data.begin(), child.data.end()); return true; } } // No matching 'pssh' box found. return false; } } // namespace media