| // Copyright 2026 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "bindings/bmff_binding_hasher.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include "absl/base/nullability.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "bindings/binding_hasher.h" |
| #include "formats/bmff/box_header.h" |
| #include "formats/bmff/box_matcher.h" |
| #include "proto/bmff_based_hash_assertion.pb.h" |
| #include "riegeli/bytes/reader.h" |
| #include "utils/byte_writers.h" |
| |
| namespace credentio { |
| |
| namespace { |
| |
| std::string Uint64ToBytes(uint64_t value) { |
| std::vector<uint8_t> bytes; |
| bytes.reserve(sizeof(uint64_t)); |
| WriteUint64NetworkOrder(value, &bytes); |
| return std::string(reinterpret_cast<const char*>(bytes.data()), bytes.size()); |
| } |
| |
| absl_nullable std::unique_ptr<BindingHasher> BuildBmffV2Hasher( |
| const BmffBasedHashAssertion& bmff_hash, riegeli::Reader& file_contents, |
| BindingHasherTracker& tracker) { |
| // Build box matchers from the assertion. |
| std::vector<BMFFBoxMatcher> matchers; |
| matchers.reserve(bmff_hash.exclusions_size()); |
| for (const auto& bmff_range : bmff_hash.exclusions()) { |
| matchers.emplace_back(bmff_range, &file_contents); |
| } |
| |
| // Match boxes and build exclusion and insertion chunks for the hasher. |
| std::vector<BindingHasher::Chunk> chunks; |
| absl::Status iterator_status = IterateOverBmffBoxes( |
| file_contents, |
| [&chunks, &matchers](const BmffBoxHeader& box) -> absl::StatusOr<bool> { |
| std::vector<BMFFBoxMatcher::Range> exclusions; |
| bool is_entire_box_excluded = false; |
| for (const auto& matcher : matchers) { |
| if (matcher.Matches(box)) { |
| exclusions = matcher.MatchedRanges(box); |
| is_entire_box_excluded = matcher.IsMatchingEntireBox(); |
| break; |
| } |
| } |
| if (!is_entire_box_excluded && box.IsRootBox()) { |
| chunks.push_back({.op = BindingHasher::Chunk::Op::kInsertion, |
| .offset = box.start, |
| .additional_data = Uint64ToBytes(box.start)}); |
| } |
| for (const auto& exclusion : exclusions) { |
| chunks.push_back({.op = BindingHasher::Chunk::Op::kExclusion, |
| .offset = exclusion.offset, |
| .length = exclusion.length}); |
| } |
| return true; |
| }); |
| if (!iterator_status.ok()) { |
| tracker.RecordGeneralError(iterator_status); |
| return nullptr; |
| } |
| return BindingHasher::Create(bmff_hash.alg(), chunks, tracker); |
| } |
| } // namespace |
| |
| std::optional<std::string> BmffBindingHasher::Digest( |
| riegeli::Reader& input) const { |
| auto hasher = BuildBmffV2Hasher(assertion_, input, tracker_); |
| if (hasher == nullptr) { |
| return std::nullopt; |
| } |
| return hasher->Digest(input, tracker_); |
| } |
| |
| } // namespace credentio |