blob: 96d31476fa3060563e7b3d2e105380ddf6524643 [file]
// 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