| // 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 "formats/bmff/hasher.h" | |
| #include <string> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_macros.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "bindings/binding_hasher.h" | |
| #include "bindings/bmff_binding_hasher.h" | |
| #include "crypto/algorithms.h" | |
| #include "proto/bmff_based_hash_assertion.pb.h" | |
| #include "riegeli/bytes/reader.h" | |
| namespace credentio { | |
| BmffBasedHashAssertion GetBmffHashTemplate(HashAlgorithm alg) { | |
| BmffBasedHashAssertion assertion; | |
| assertion.set_name("BMFF file hash"); | |
| assertion.set_alg(absl::StrCat(alg)); | |
| std::string hash(HashSizeBytes(alg), '\0'); | |
| assertion.set_hash(hash); | |
| // Add the required exclusions described in | |
| // https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html#_exclusion_list_requirements. | |
| assertion.add_exclusions()->set_xpath("/ftyp"); | |
| BmffRange* uuid_exclusion = assertion.add_exclusions(); | |
| uuid_exclusion->set_xpath("/uuid"); | |
| BmffData* data = uuid_exclusion->add_data(); | |
| data->set_offset(8); | |
| // The C2PA manifest store extended box type value, which will always | |
| // appear at offset 8 in the C2PA BMFF box | |
| // (https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html#_definition). | |
| data->set_value("\330\376\303\326\033\016H<\222\227X(\207~\304\201"); | |
| assertion.add_exclusions()->set_xpath("/mfra"); | |
| // Exclude 'free' boxes. A 'free' box is often used for a pre-allocated space | |
| // before writing the final C2PA manifest store. | |
| assertion.add_exclusions()->set_xpath("/free"); | |
| return assertion; | |
| } | |
| absl::Status DigestBmff(riegeli::Reader& input, | |
| BmffBasedHashAssertion& assertion_template) { | |
| StatusBindingHasherTracker tracker; | |
| BmffBindingHasher hasher(&assertion_template, &tracker); | |
| auto digest = hasher.Digest(input).value_or(""); | |
| ABSL_RETURN_IF_ERROR(tracker.status()); | |
| if (digest.empty()) { | |
| return absl::InternalError("digest is empty but tracker status is OK"); | |
| } | |
| if (digest.size() != assertion_template.hash().size()) { | |
| return absl::InternalError("digest size mismatch"); | |
| } | |
| assertion_template.set_hash(digest); | |
| return absl::OkStatus(); | |
| } | |
| } // namespace credentio |