| // 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/data_hash_hard_binding_validator.h" | |
| #include <cstddef> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <type_traits> | |
| #include <utility> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_matchers.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/escaping.h" | |
| #include "absl/strings/string_view.h" | |
| #include "formats/asset_byte_info.h" | |
| #include "gmock/gmock.h" | |
| #include "google/protobuf/util/message_differencer.h" | |
| #include "gtest/gtest.h" | |
| #include "proto/assertion.pb.h" | |
| #include "proto/bmff_based_hash_assertion.pb.h" | |
| #include "proto/boxes_hash_assertion.pb.h" | |
| #include "proto/data_hash_assertion.pb.h" | |
| #include "proto/ingredient_assertion.pb.h" | |
| #include "proto/manifest.pb.h" | |
| #include "proto/validation_result.pb.h" | |
| #include "proto/validation_status.pb.h" | |
| #include "riegeli/base/object.h" | |
| #include "riegeli/base/types.h" | |
| #include "riegeli/bytes/reader.h" | |
| #include "riegeli/bytes/string_reader.h" | |
| #include "testing/proto_test_utils.h" | |
| #include "utils/dual_status_tracker.h" | |
| #include "utils/status_tracker.h" | |
| namespace credentio { | |
| namespace { | |
| using ::absl_testing::IsOkAndHolds; | |
| using ::absl_testing::StatusIs; | |
| using ::credentio_testing::EqualsProto; | |
| using ::credentio_testing::ParseTextProtoOrDie; | |
| using ::testing::HasSubstr; | |
| using ::testing::Pointee; | |
| using ::testing::TestWithParam; | |
| // Matches a `ValidationResultProto` against `expected` (a proto or a text | |
| // proto), ignoring the assertions of all manifests. | |
| MATCHER_P(EqualsProtoIgnoringAssertions, expected, "") { | |
| using Proto = std::decay_t<decltype(arg)>; | |
| Proto expected_proto; | |
| if constexpr (std::is_convertible_v<decltype(expected), absl::string_view>) { | |
| expected_proto = ParseTextProtoOrDie(expected); | |
| } else { | |
| expected_proto = expected; | |
| } | |
| google::protobuf::util::MessageDifferencer differencer; | |
| differencer.IgnoreField( | |
| Manifest::descriptor()->FindFieldByName("assertions")); | |
| std::string diff; | |
| differencer.ReportDifferencesToString(&diff); | |
| if (!differencer.Compare(expected_proto, arg)) { | |
| *result_listener << "\nDifference:\n" << diff; | |
| return false; | |
| } | |
| return true; | |
| } | |
| // A custom reader that supports size but fails on read. | |
| class FailingReader : public riegeli::Reader { | |
| public: | |
| explicit FailingReader(riegeli::Closed) : Reader(riegeli::kClosed) {} | |
| FailingReader() = default; | |
| bool PullSlow(size_t min_length, size_t recommended_length) override { | |
| return Fail(absl::InternalError("Simulated read error")); | |
| } | |
| bool SupportsSize() override { return true; } | |
| std::optional<riegeli::Position> SizeImpl() override { return 10; } | |
| }; | |
| struct DataHashValidatorTestCase { | |
| std::string test_name; | |
| std::string contents; | |
| DataHashAssertion data_hash_assertion; | |
| std::optional<std::string> hash; | |
| AssetByteInfo asset_byte_info; | |
| ValidationStatusSet expected_validation_result; | |
| }; | |
| using DataHashValidatorTest = TestWithParam<DataHashValidatorTestCase>; | |
| // ------------------------------------------------ | |
| TEST(DataHashHardBindingValidatorTest, MissingAssertion) { | |
| auto input = riegeli::StringReader<std::string>(""); | |
| auto partial_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest: { | |
| label: "urn:uuid:1234" | |
| assertions: { | |
| label: "another_data_hash_assertion" | |
| data_hash: { alg: "sha256" hash: "\x11\x22\x33" pad: "\x00\x00" } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:uuid:1234/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| EXPECT_THAT(DataHashHardBindingValidator().Validate( | |
| input, AssetByteInfo(), std::move(partial_result)), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("missing assertion"))); | |
| } | |
| TEST(DataHashHardBindingValidatorTest, NotDataHashAssertion) { | |
| auto input = riegeli::StringReader<std::string>(""); | |
| auto partial_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest: { | |
| label: "urn:uuid:1234" | |
| assertions: { | |
| label: "bmff_based_hash_assertion" | |
| bmff_based_hash: { | |
| alg: "some alg", | |
| name: "Example `c2pa.hash.bmff.v2` assertion" | |
| hash: "\x11\x22\x33'" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:uuid:1234/c2pa.assertions/bmff_based_hash_assertion" | |
| )pb")); | |
| EXPECT_THAT(DataHashHardBindingValidator().Validate( | |
| input, AssetByteInfo(), std::move(partial_result)), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("not a data hash assertion"))); | |
| } | |
| TEST(DataHashHardBindingValidatorTest, InvalidAlgorithm) { | |
| auto input = riegeli::StringReader<std::string>(""); | |
| auto partial_validation_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| assertions { | |
| label: "data_hash_assertion" | |
| data_hash { hash: "\x11\x22\x33" alg: "some algo" } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| EXPECT_THAT( | |
| DataHashHardBindingValidator().Validate( | |
| input, AssetByteInfo(), std::move(partial_validation_result)), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringAssertions(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| validation { | |
| failures { | |
| code: "algorithm.unsupported" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| } | |
| } | |
| )pb")))); | |
| } | |
| TEST(DataHashHardBindingValidatorTest, ClosedReaderRecordsGeneralError) { | |
| auto input = riegeli::StringReader<std::string>(""); | |
| EXPECT_TRUE(input.Close()); | |
| auto partial_validation_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| assertions { | |
| label: "data_hash_assertion" | |
| data_hash { hash: "123" alg: "sha256" } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| EXPECT_THAT( | |
| DataHashHardBindingValidator().Validate( | |
| input, AssetByteInfo(), std::move(partial_validation_result)), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringAssertions(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| validation { | |
| failures { | |
| code: "com.google.internal.error" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| explanation: "Asset size cannot be determined" | |
| } | |
| } | |
| } | |
| )pb")))); | |
| } | |
| TEST(DataHashHardBindingValidatorTest, FailingReaderRecordsGeneralError) { | |
| FailingReader input; | |
| auto partial_validation_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| assertions { | |
| label: "data_hash_assertion" | |
| data_hash { hash: "123" alg: "sha256" } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| EXPECT_THAT( | |
| DataHashHardBindingValidator().Validate( | |
| input, AssetByteInfo(), std::move(partial_validation_result)), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringAssertions(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| validation { | |
| failures { | |
| code: "general.error" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| explanation: "Simulated read error; at byte 0" | |
| } | |
| } | |
| } | |
| )pb")))); | |
| } | |
| TEST(DataHashHardBindingValidatorTest, NoManifestStoreLocation) { | |
| auto input = | |
| riegeli::StringReader<std::string>("adding_some_data_to_the_input"); | |
| auto partial_validation_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| assertions { | |
| label: "data_hash_assertion" | |
| data_hash { | |
| hash: "\x11\x22\x33" | |
| alg: "sha256" | |
| exclusions: { start: 1, length: 4 }, | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| absl::StatusOr<std::unique_ptr<StatusTracker>> tracker = | |
| DualStatusTracker::FromPartialValidationResult( | |
| partial_validation_result.get()); | |
| ABSL_ASSERT_OK(tracker); | |
| DataHashHardBindingValidator().Validate( | |
| input, std::nullopt, | |
| partial_validation_result->active_manifest().assertions(0).data_hash(), | |
| partial_validation_result->hard_binding_uri(), **tracker, | |
| /*assertion_in_ingredient_manifest=*/false); | |
| EXPECT_THAT( | |
| partial_validation_result->active_manifest().validation(), | |
| EqualsProto(R"pb( | |
| failures { | |
| code: "assertion.dataHash.mismatch" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb")); | |
| } | |
| TEST(DataHashHardBindingValidatorTest, TestTruncatedWindow) { | |
| auto input = riegeli::StringReader<std::string>("---ab___cd----------"); | |
| auto partial_validation_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| assertions { | |
| label: "data_hash_assertion" | |
| data_hash { | |
| hash: "\x11\x22\x33" | |
| alg: "sha256" | |
| exclusions: { start: 2, length: 3 }, | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| absl::Base64Unescape("iNQmb9TmM40TuEX88olXnSCciXgjuSF9o+Fhk28DFYk=", | |
| partial_validation_result->mutable_active_manifest() | |
| ->mutable_assertions(0) | |
| ->mutable_data_hash() | |
| ->mutable_hash()); | |
| absl::Base64Unescape("0000", | |
| partial_validation_result->mutable_active_manifest() | |
| ->mutable_assertions(0) | |
| ->mutable_data_hash() | |
| ->mutable_pad()); | |
| absl::StatusOr<std::unique_ptr<StatusTracker>> tracker = | |
| DualStatusTracker::FromPartialValidationResult( | |
| partial_validation_result.get()); | |
| ABSL_ASSERT_OK(tracker); | |
| DataHashHardBindingValidator().Validate( | |
| input, std::nullopt, | |
| partial_validation_result->active_manifest().assertions(0).data_hash(), | |
| partial_validation_result->hard_binding_uri(), **tracker, | |
| /*assertion_in_ingredient_manifest=*/false, /*start_offset=*/3, | |
| /*end_offset=*/10); | |
| EXPECT_THAT( | |
| partial_validation_result->active_manifest().validation(), | |
| EqualsProto(R"pb( | |
| successes { | |
| code: "assertion.dataHash.match" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb")); | |
| } | |
| TEST(DataHashHardBindingValidatorTest, NegativeOffsetAdjustment) { | |
| auto input = | |
| riegeli::StringReader<std::string>("012345678901234567890123456789"); | |
| auto partial_validation_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| assertions { | |
| label: "data_hash_assertion" | |
| data_hash { | |
| hash: "\x11\x22\x33" | |
| alg: "sha256" | |
| exclusions: { start: 10, | |
| length: 10 } # This will set offset_adjustment | |
| exclusions: { start: 1, length: 1 } # This should trigger the failure | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| absl::StatusOr<std::unique_ptr<StatusTracker>> tracker = | |
| DualStatusTracker::FromPartialValidationResult( | |
| partial_validation_result.get()); | |
| ABSL_ASSERT_OK(tracker); | |
| DataHashHardBindingValidator().Validate( | |
| input, /*manifest_store_location=*/{{.offset = 20, .length = 5}}, | |
| partial_validation_result->active_manifest().assertions(0).data_hash(), | |
| partial_validation_result->hard_binding_uri(), **tracker, | |
| /*assertion_in_ingredient_manifest=*/true, /*start_offset=*/10, | |
| /*end_offset=*/30); | |
| EXPECT_THAT( | |
| partial_validation_result->active_manifest().validation(), | |
| EqualsProto(R"pb( | |
| failures { | |
| code: "assertion.dataHash.malformed" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb")); | |
| } | |
| TEST_P(DataHashValidatorTest, ValidateBindingHashData) { | |
| const DataHashValidatorTestCase& test_case = GetParam(); | |
| auto input = riegeli::StringReader<>(test_case.contents); | |
| auto partial_validation_result = std::make_unique< | |
| PartialValidationResultProto>(ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| assertions { label: "data_hash_assertion" } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| )pb")); | |
| partial_validation_result->mutable_active_manifest() | |
| ->mutable_assertions(0) | |
| ->mutable_data_hash() | |
| ->MergeFrom(test_case.data_hash_assertion); | |
| if (test_case.hash.has_value()) { | |
| absl::Base64Unescape(*test_case.hash, | |
| partial_validation_result->mutable_active_manifest() | |
| ->mutable_assertions(0) | |
| ->mutable_data_hash() | |
| ->mutable_hash()); | |
| absl::Base64Unescape("0000", | |
| partial_validation_result->mutable_active_manifest() | |
| ->mutable_assertions(0) | |
| ->mutable_data_hash() | |
| ->mutable_pad()); | |
| } | |
| AssetByteInfo asset_byte_info = test_case.asset_byte_info; | |
| ValidationResultProto expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| active_manifest { | |
| label: "manifest_label" | |
| validation {} | |
| } | |
| )pb"); | |
| *expected_validation_result.mutable_active_manifest()->mutable_validation() = | |
| test_case.expected_validation_result; | |
| EXPECT_THAT(DataHashHardBindingValidator().Validate( | |
| input, asset_byte_info, std::move(partial_validation_result)), | |
| IsOkAndHolds(Pointee( | |
| EqualsProtoIgnoringAssertions(expected_validation_result)))); | |
| } | |
| INSTANTIATE_TEST_SUITE_P( | |
| DataHashHardBindingDataHashValidatorTest, DataHashValidatorTest, | |
| testing::ValuesIn<DataHashValidatorTestCase>({ | |
| { | |
| .test_name = "Sha256", | |
| .contents = "abcd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33" | |
| alg: "sha256" | |
| )pb"), | |
| .hash = "iNQmb9TmM40TuEX88olXnSCciXgjuSF9o+Fhk28DFYk=", | |
| .asset_byte_info = AssetByteInfo(), | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| successes { | |
| code: "assertion.dataHash.match" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "Sha384", | |
| .contents = "abcd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33" | |
| alg: "sha384" | |
| )pb"), | |
| .hash = "EWWzQG/" | |
| "wtSo9JHIfeFRiyiJ2yfRUoRbCsrogFxp5BepaAmaC62WcTV8RXDY6o8eb", | |
| .asset_byte_info = AssetByteInfo(), | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| successes { | |
| code: "assertion.dataHash.match" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "Sha512", | |
| .contents = "abcd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33" | |
| alg: "sha512" | |
| )pb"), | |
| .hash = "2AIvIGCtbv0perc9zFNVybIUBUsNF3ahNqZp0mp9OxT3OqDQ6/" | |
| "8Z7jMzaPAWS2QZqW2knj5IF1Pn6Wtxa9zLbw==", | |
| .asset_byte_info = AssetByteInfo(), | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| successes { | |
| code: "assertion.dataHash.match" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "NoExclusionRange", | |
| .contents = "", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33" | |
| alg: "sha256" | |
| pad: "\x00\x00" | |
| )pb"), | |
| .hash = std::nullopt, | |
| .asset_byte_info = AssetByteInfo(), | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| failures { | |
| code: "assertion.dataHash.mismatch" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "ExcludesManifestStore", | |
| .contents = "a___bcd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| exclusions: { start: 1, length: 3 }, | |
| )pb"), | |
| .hash = "iNQmb9TmM40TuEX88olXnSCciXgjuSF9o+Fhk28DFYk=", | |
| .asset_byte_info = {.manifest_store_location = {.offset = 1, | |
| .length = 3}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| successes { | |
| code: "assertion.dataHash.match" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "ExcludesManifestStoreAndAdditionalExclusions", | |
| .contents = "a-b______c-d", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| pad: "\x00\x00", | |
| exclusions: { start: 1, length: 1 } | |
| exclusions: { start: 3, length: 6 } | |
| exclusions: { start: 10, length: 1 } | |
| )pb"), | |
| .hash = "iNQmb9TmM40TuEX88olXnSCciXgjuSF9o+Fhk28DFYk=", | |
| .asset_byte_info = {.manifest_store_location = {.offset = 3, | |
| .length = 6}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| successes { | |
| code: "assertion.dataHash.match" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| informationals { | |
| code: "assertion.dataHash.additionalExclusionsPresent" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "NegativeStartOffset", | |
| .contents = "ab______cd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| pad: "\x00\x00", | |
| exclusions: { start: -1, length: 10 } | |
| )pb"), | |
| .hash = std::nullopt, | |
| .asset_byte_info = {.manifest_store_location = {.offset = 2, | |
| .length = 6}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| failures { | |
| code: "assertion.dataHash.malformed" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "NegativeLength", | |
| .contents = "ab______cd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| pad: "\x00\x00", | |
| exclusions: { start: 1, length: -10 } | |
| )pb"), | |
| .hash = std::nullopt, | |
| .asset_byte_info = {.manifest_store_location = {.offset = 2, | |
| .length = 6}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| failures { | |
| code: "assertion.dataHash.malformed" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "EndsPastEndOfAsset", | |
| .contents = "ab______cd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| pad: "\x00\x00", | |
| exclusions: { start: 2, length: 6 } | |
| exclusions: { start: 9, length: 5 } | |
| )pb"), | |
| .hash = std::nullopt, | |
| .asset_byte_info = {.manifest_store_location = {.offset = 2, | |
| .length = 6}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| failures { | |
| code: "assertion.dataHash.mismatch" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "OverlappingExclusions", | |
| .contents = "ab______cd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| pad: "\x00\x00", | |
| exclusions: { start: 1, length: 4 } | |
| exclusions: { start: 2, length: 6 } | |
| )pb"), | |
| .hash = std::nullopt, | |
| .asset_byte_info = {.manifest_store_location = {.offset = 2, | |
| .length = 6}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| failures { | |
| code: "assertion.dataHash.malformed" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "BackToBackExclusions", | |
| .contents = "ab--______cd", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| pad: "\x00\x00", | |
| exclusions: { start: 2, length: 1 } | |
| exclusions: { start: 3, length: 1 } | |
| exclusions: { start: 4, length: 6 } | |
| )pb"), | |
| .hash = "iNQmb9TmM40TuEX88olXnSCciXgjuSF9o+Fhk28DFYk=", | |
| .asset_byte_info = {.manifest_store_location = {.offset = 4, | |
| .length = 6}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| successes { | |
| code: "assertion.dataHash.match" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| informationals { | |
| code: "assertion.dataHash.additionalExclusionsPresent" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| { | |
| .test_name = "ExclusionsNotInOrder", | |
| .contents = "ab-c______d", | |
| .data_hash_assertion = ParseTextProtoOrDie(R"pb( | |
| hash: "\x11\x22\x33", | |
| alg: "sha256", | |
| pad: "\x00\x00", | |
| exclusions: { start: 3, length: 1 } | |
| exclusions: { start: 2, length: 1 } | |
| exclusions: { start: 4, length: 6 } | |
| )pb"), | |
| .hash = "iNQmb9TmM40TuEX88olXnSCciXgjuSF9o+Fhk28DFYk=", | |
| .asset_byte_info = {.manifest_store_location = {.offset = 4, | |
| .length = 6}}, | |
| .expected_validation_result = ParseTextProtoOrDie(R"pb( | |
| failures { | |
| code: "assertion.dataHash.malformed" | |
| url: "self#jumbf=/c2pa/manifest_label/c2pa.assertions/data_hash_assertion" | |
| } | |
| )pb"), | |
| }, | |
| }), | |
| [](const testing::TestParamInfo<DataHashValidatorTest::ParamType>& info) { | |
| return info.param.test_name; | |
| }); | |
| } // namespace | |
| } // namespace credentio |