| // 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 "claim/manifest_box_encoder.h" | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/log/absl_check.h" | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_matchers.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/str_join.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/synchronization/notification.h" | |
| #include "assertion/assertion_encoder.h" | |
| #include "assertion/assertion_referencer.h" | |
| #include "async/async_context.h" | |
| #include "claim/claim_cbor_encoder.h" | |
| #include "claim/local_signer.h" | |
| #include "claim/manifest_box.h" | |
| #include "claim/manifest_store_builder.h" | |
| #include "claim/signer.h" | |
| #include "constants/labels.h" | |
| #include "crypto/algorithms.h" | |
| #include "crypto/crypto_read_handler.h" | |
| #include "crypto/crypto_write_handler.h" | |
| #include "crypto/default/default_crypto_read_handler.h" | |
| #include "crypto/default/hasher.h" | |
| #include "crypto/hash.h" | |
| #include "gmock/gmock.h" | |
| #include "google/protobuf/util/message_differencer.h" | |
| #include "gtest/gtest.h" | |
| #include "jumbf/box_builder.h" | |
| #include "proto/actions_assertion.pb.h" | |
| #include "proto/assertion.pb.h" | |
| #include "proto/generator.pb.h" | |
| #include "proto/generator_info.pb.h" | |
| #include "proto/hashed_uri.pb.h" | |
| #include "proto/ingredient_assertion.pb.h" | |
| #include "proto/manifest.pb.h" | |
| #include "proto/signature_info.pb.h" | |
| #include "proto/validation_result.pb.h" | |
| #include "proto/validation_status.pb.h" | |
| #include "testing/create_test_crypto_write_handler.h" | |
| #include "testing/local_key_crypto_write_handler.h" | |
| #include "testing/manifest_store_test_utils.h" | |
| #include "testing/proto_test_utils.h" | |
| #include "uuid/uuid.h" | |
| namespace credentio { | |
| namespace { | |
| using ::absl_testing::IsOk; | |
| using ::absl_testing::IsOkAndHolds; | |
| using ::absl_testing::StatusIs; | |
| using ::credentio_testing::ParseTextProtoOrDie; | |
| using ::testing::HasSubstr; | |
| using ::testing::Pointee; | |
| constexpr auto kUuid = | |
| Uuid::FromStringOrDie("12345678-1234-1234-1234-123456789012"); | |
| MATCHER_P(EqualsProtoIgnoringFieldsAndOrdering, expected, "") { | |
| google::protobuf::util::MessageDifferencer differencer; | |
| differencer.set_repeated_field_comparison( | |
| google::protobuf::util::MessageDifferencer::AS_SET); | |
| differencer.IgnoreField(HashedUri::descriptor()->FindFieldByName("hash")); | |
| differencer.IgnoreField( | |
| SignatureInfo::descriptor()->FindFieldByName("cert_serial_number")); | |
| differencer.IgnoreField( | |
| SignatureInfo::descriptor()->FindFieldByName("certificate_issuer")); | |
| differencer.IgnoreField( | |
| SignatureInfo::descriptor()->FindFieldByName("timestamping_time")); | |
| std::string diff; | |
| differencer.ReportDifferencesToString(&diff); | |
| bool match = differencer.Compare(expected, arg); | |
| if (!match) { | |
| *result_listener << "\nDifference:\n" << diff; | |
| } | |
| return match; | |
| } | |
| ClaimSigner::Options ToClaimSignerOptions( | |
| const GenerateManifestStoreOptions& options, AsyncContext context) { | |
| ClaimSigner::Options result; | |
| result.async_context = std::move(context); | |
| switch (options.time_stamping_mode()) { | |
| case GenerateManifestStoreOptions::TIME_STAMPING_REQUIRED: | |
| result.time_stamping_mode = TimeStampingMode::kRequired; | |
| break; | |
| case GenerateManifestStoreOptions::TIME_STAMPING_OMITTED_WITH_PADDING: | |
| result.time_stamping_mode = TimeStampingMode::kOmittedWithPadding; | |
| break; | |
| case GenerateManifestStoreOptions::TIME_STAMPING_OPTIONAL: | |
| case GenerateManifestStoreOptions::TIME_STAMPING_MODE_UNSPECIFIED: | |
| default: | |
| result.time_stamping_mode = TimeStampingMode::kOptional; | |
| break; | |
| } | |
| return result; | |
| } | |
| absl::StatusOr<jumbf::BuiltSuperBox> Encode( | |
| ManifestBoxEncoder& manifest_box_encoder, | |
| const GenerateManifestStoreOptions& options = {}) { | |
| absl::Notification done; | |
| absl::StatusOr<ManifestEncoderResult> result; | |
| manifest_box_encoder.Encode( | |
| ToClaimSignerOptions(options, AsyncContext()), | |
| [&result, &done](absl::StatusOr<ManifestEncoderResult> res) { | |
| result = std::move(res); | |
| done.Notify(); | |
| }); | |
| done.WaitForNotification(); | |
| if (!result.ok()) { | |
| return result.status(); | |
| } | |
| return std::move(result->box); | |
| } | |
| class FakeUuidGenerator : public UuidGenerator { | |
| public: | |
| FakeUuidGenerator() = default; | |
| Uuid Generate() const override { return kUuid; } | |
| }; | |
| class FakeHasherFactory : public HasherFactory { | |
| public: | |
| absl::StatusOr<std::unique_ptr<Hasher>> Create() const override { | |
| return CreateHasher("sha256"); | |
| } | |
| HashAlgorithm algorithm() const override { return HashAlgorithm::kSha256; } | |
| }; | |
| class ManifestBoxEncoderTest : public testing::Test { | |
| protected: | |
| ManifestBoxEncoderTest() | |
| : assertion_cbor_encoder_(), claim_cbor_encoder_(GetClaimCborEncoder()) { | |
| generator_info_.set_name("Test generator"); | |
| generator_info_.set_spec_version("2.2.0"); | |
| auto crypto_writer = CreateTestCryptoWriteHandler({}); | |
| ABSL_CHECK_OK(crypto_writer); | |
| crypto_writer_ = std::move(*crypto_writer); | |
| } | |
| std::unique_ptr<CryptoWriteHandler> crypto_writer_; | |
| GeneratorInfo generator_info_; | |
| FakeHasherFactory hasher_factory_; | |
| StandardAssertionEncoder assertion_cbor_encoder_; | |
| const ClaimCborEncoder& claim_cbor_encoder_; | |
| ManifestBox CreateMinimalManifestBox() { | |
| return { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "12345678-1234-1234-1234-123456789012", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = {}, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| } | |
| }; | |
| TEST_F(ManifestBoxEncoderTest, GetEncodedManifest) { | |
| ManifestBox manifest_box = CreateMinimalManifestBox(); | |
| PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie( | |
| R"pb( | |
| active_manifest { | |
| label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| claim { | |
| claim_generator_info { | |
| name: "Test generator" | |
| spec_version: "2.2.0" | |
| } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3" | |
| algorithm: "sha256" | |
| hash: "D\242\244\274\235o\350\352\312\307\225|C\033\204\347\311\243\202\'\203c\362\331T1a\377P\271\255\366" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| hash: "\331\271\373\3228\310\335>y\263\334\342\335\367\211Q\360\325\227\r\345$]\214\005\3047[\r#1\312" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "12345678-1234-1234-1234-123456789012" | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| software_agent { name: "test generator" } | |
| } | |
| } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| )pb"); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| EXPECT_THAT(ValidateClaim(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result.active_manifest().claim()))); | |
| EXPECT_THAT(ValidateManifestStore(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result)))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, TimeStampingModeUnspecified) { | |
| ManifestBox manifest_box = CreateMinimalManifestBox(); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| auto crypto_writer = | |
| CreateTestCryptoWriteHandler({.disable_timestamping = true}); | |
| ABSL_ASSERT_OK(crypto_writer); | |
| LocalClaimSigner claim_signer(crypto_writer->get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| GenerateManifestStoreOptions options; | |
| options.set_time_stamping_mode( | |
| GenerateManifestStoreOptions::TIME_STAMPING_MODE_UNSPECIFIED); | |
| // Should default to OPTIONAL, so even if timestamping fails, encode succeeds. | |
| EXPECT_THAT(Encode(manifest_box_encoder, options), IsOk()); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, TimeStampingModeOptional) { | |
| ManifestBox manifest_box = CreateMinimalManifestBox(); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| auto crypto_writer = | |
| CreateTestCryptoWriteHandler({.disable_timestamping = true}); | |
| ABSL_ASSERT_OK(crypto_writer); | |
| LocalClaimSigner claim_signer(crypto_writer->get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| GenerateManifestStoreOptions options; | |
| options.set_time_stamping_mode( | |
| GenerateManifestStoreOptions::TIME_STAMPING_OPTIONAL); | |
| // Timestamping fails, but mode is OPTIONAL, so encode succeeds. | |
| EXPECT_THAT(Encode(manifest_box_encoder, options), IsOk()); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, TimeStampingModeRequiredSuccess) { | |
| ManifestBox manifest_box = CreateMinimalManifestBox(); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| auto crypto_writer = | |
| CreateTestCryptoWriteHandler({.disable_timestamping = false}); | |
| ABSL_ASSERT_OK(crypto_writer); | |
| LocalClaimSigner claim_signer(crypto_writer->get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| GenerateManifestStoreOptions options; | |
| options.set_time_stamping_mode( | |
| GenerateManifestStoreOptions::TIME_STAMPING_REQUIRED); | |
| EXPECT_THAT(Encode(manifest_box_encoder, options), IsOk()); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, TimeStampingModeRequiredFailure) { | |
| ManifestBox manifest_box = CreateMinimalManifestBox(); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| auto crypto_writer = | |
| CreateTestCryptoWriteHandler({.disable_timestamping = true}); | |
| ABSL_ASSERT_OK(crypto_writer); | |
| LocalClaimSigner claim_signer(crypto_writer->get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| GenerateManifestStoreOptions options; | |
| options.set_time_stamping_mode( | |
| GenerateManifestStoreOptions::TIME_STAMPING_REQUIRED); | |
| // Timestamping fails, and mode is REQUIRED, so encode fails. | |
| EXPECT_THAT( | |
| Encode(manifest_box_encoder, options), | |
| StatusIs(absl::StatusCode::kUnavailable, HasSubstr("TSA is disabled"))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, TimeStampingModeOmittedWithPadding) { | |
| ManifestBox manifest_box = CreateMinimalManifestBox(); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| // This should not cause a failure because timestamping should be skipped. | |
| auto crypto_writer = | |
| CreateTestCryptoWriteHandler({.disable_timestamping = true}); | |
| ABSL_ASSERT_OK(crypto_writer); | |
| LocalClaimSigner claim_signer(crypto_writer->get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| GenerateManifestStoreOptions options; | |
| options.set_time_stamping_mode( | |
| GenerateManifestStoreOptions::TIME_STAMPING_OMITTED_WITH_PADDING); | |
| EXPECT_THAT(Encode(manifest_box_encoder, options), IsOk()); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, | |
| EncodeManifestWithInvalidOpenedActionNoIngredients) { | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "12345678-1234-1234-1234-123456789012", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = {}, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| EXPECT_THAT(Encode(manifest_box_encoder), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("Opened action must have exactly one " | |
| "ingredient."))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, | |
| EncodeManifestWithInvalidOpenedActionMultipleIngredients) { | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "12345678-1234-1234-1234-123456789012", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| } | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = | |
| { | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3__1" | |
| ingredient_v3 { | |
| title: "test ingredient 2" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| }, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| EXPECT_THAT(Encode(manifest_box_encoder), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("Opened action must have exactly one " | |
| "ingredient."))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, EncodedManifestWithCreatedActionIngredient) { | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "12345678-1234-1234-1234-123456789012", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| } | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = | |
| {ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient 1" | |
| media_type: "video/mp4" | |
| relationship: "inputTo" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3__1" | |
| ingredient_v3 { | |
| title: "test ingredient 2" | |
| media_type: "video/mp4" | |
| relationship: "inputTo" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb")}, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie( | |
| R"pb( | |
| active_manifest { | |
| label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| claim { | |
| claim_generator_info { | |
| name: "Test generator" | |
| spec_version: "2.2.0" | |
| } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3" | |
| algorithm: "sha256" | |
| hash: "D\242\244\274\235o\350\352\312\307\225|C\033\204\347\311\243\202\'\203c\362\331T1a\377P\271\255\366" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__1" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| hash: "\331\271\373\3228\310\335>y\263\334\342\335\367\211Q\360\325\227\r\345$]\214\005\3047[\r#1\312" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "12345678-1234-1234-1234-123456789012" | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| software_agent { name: "test generator" } | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient 1" | |
| media_type: "video/mp4" | |
| relationship: "inputTo" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3__1" | |
| ingredient_v3 { | |
| title: "test ingredient 2" | |
| media_type: "video/mp4" | |
| relationship: "inputTo" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| )pb"); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| EXPECT_THAT(ValidateClaim(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result.active_manifest().claim()))); | |
| EXPECT_THAT(ValidateManifestStore(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result)))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, EncodeManifestWithPlacedActionIngredients) { | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "12345678-1234-1234-1234-123456789012", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| actions { | |
| action: "c2pa.placed" | |
| software_agent { name: "test generator" } | |
| description: "placed for testing" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__2" | |
| } | |
| } | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = | |
| { | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3__1" | |
| ingredient_v3 { | |
| title: "test ingredient 2" | |
| media_type: "video/mp4" | |
| relationship: "componentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3__2" | |
| ingredient_v3 { | |
| title: "test ingredient 3" | |
| media_type: "video/mp4" | |
| relationship: "componentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| }, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie( | |
| R"pb( | |
| active_manifest { | |
| label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| claim { | |
| claim_generator_info { | |
| name: "Test generator" | |
| spec_version: "2.2.0" | |
| } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__1" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__2" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3" | |
| algorithm: "sha256" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "12345678-1234-1234-1234-123456789012" | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| description: "created for testing" | |
| software_agent { name: "test generator" } | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| actions { | |
| action: "c2pa.placed" | |
| description: "placed for testing" | |
| software_agent { name: "test generator" } | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3__2" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3__1" | |
| ingredient_v3 { | |
| title: "test ingredient 2" | |
| media_type: "video/mp4" | |
| relationship: "componentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3__2" | |
| ingredient_v3 { | |
| title: "test ingredient 3" | |
| media_type: "video/mp4" | |
| relationship: "componentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3__2" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| informationals { | |
| code: "ingredient.unknownProvenance" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| informationals { | |
| code: "ingredient.unknownProvenance" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3__1" | |
| } | |
| informationals { | |
| code: "ingredient.unknownProvenance" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3__2" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| )pb"); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| EXPECT_THAT(ValidateClaim(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result.active_manifest().claim()))); | |
| EXPECT_THAT(ValidateManifestStore(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result)))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, EncodeManifestWithOpenedAction) { | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "12345678-1234-1234-1234-123456789012", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = | |
| { | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| }, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie( | |
| R"pb( | |
| active_manifest { | |
| label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| claim { | |
| claim_generator_info { | |
| name: "Test generator" | |
| spec_version: "2.2.0" | |
| } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| hash: "\202\377\224T\003\374\022X)\214\244z\337\002\020Wd\351\261\236\324\205\345T\321\267u\216\227]\332\014" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| algorithm: "sha256" | |
| hash: "\300\273\\<\213\017\004\226\266\032\210!\274~\013A\274+\200\215\333[\270#\035\014\247\371\356i\006\214" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3" | |
| algorithm: "sha256" | |
| hash: "D\242\244\274\235o\350\352\312\307\225|C\033\204\347\311\243\202\'\203c\362\331T1a\377P\271\255\366" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "12345678-1234-1234-1234-123456789012" | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| description: "created for testing" | |
| software_agent { name: "test generator" } | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| hash: "\300\273\\<\213\017\004\226\266\032\210!\274~\013A\274+\200\215\333[\270#\035\014\247\371\356i\006\214" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| informationals { | |
| code: "ingredient.unknownProvenance" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| )pb"); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| EXPECT_THAT(ValidateClaim(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result.active_manifest().claim()))); | |
| EXPECT_THAT(ValidateManifestStore(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result)))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, EncodeManifestWithC2paOpenedAction) { | |
| FakeUuidGenerator uuid_generator; | |
| SimpleManifestStoreOptions options = { | |
| .uuid_generator = &uuid_generator, | |
| }; | |
| absl::StatusOr<std::string> ingredient_manifest_store_bytes = | |
| CreateSimpleManifestStoreBytes(options); | |
| ABSL_ASSERT_OK(ingredient_manifest_store_bytes); | |
| ManifestStoreBuilder manifest_store_builder(&hasher_factory_); | |
| absl::StatusOr<IngredientManifestUris> ingredient_manifest_uris = | |
| manifest_store_builder.AddIngredientManifestStore( | |
| *ingredient_manifest_store_bytes); | |
| ABSL_ASSERT_OK(ingredient_manifest_uris); | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = | |
| { | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| }, | |
| .manifest_store_builder = manifest_store_builder, | |
| }; | |
| *manifest_box.ingredient_assertions[0] | |
| .mutable_ingredient_v3() | |
| ->mutable_active_manifest() = ingredient_manifest_uris->active_manifest; | |
| manifest_box.ingredient_assertions[0] | |
| .mutable_ingredient_v3() | |
| ->mutable_validation_results() | |
| ->mutable_active_manifest() | |
| ->add_successes() | |
| ->set_code("assertion.dataHash.match"); | |
| *manifest_box.ingredient_assertions[0] | |
| .mutable_ingredient_v3() | |
| ->mutable_claim_signature() = ingredient_manifest_uris->claim_signature; | |
| PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie( | |
| R"pb( | |
| active_manifest { | |
| label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| claim { | |
| claim_generator_info { | |
| name: "Test generator" | |
| spec_version: "2.2.0" | |
| } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| active_manifest { | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012" | |
| } | |
| validation_results { | |
| active_manifest { | |
| successes { code: "assertion.dataHash.match" } | |
| } | |
| } | |
| claim_signature { | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| description: "created for testing" | |
| software_agent { name: "test generator" } | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "ingredient.claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| } | |
| } | |
| ingredient_manifests { | |
| label: "urn:c2pa:12345678-1234-1234-1234-123456789012" | |
| claim { | |
| claim_generator_info { name: "Test C2PA Generator" } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.boxes" | |
| algorithm: "sha256" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "12345678-1234-1234-1234-123456789012" | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| software_agent { name: "test generator" } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.hash.boxes" | |
| boxes_hash { | |
| boxes { names: "some name" names: "other name" hash: "some hash" } | |
| alg: "some alg" | |
| } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.hash.boxes" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| )pb"); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| EXPECT_THAT(ValidateClaim(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result.active_manifest().claim()))); | |
| EXPECT_THAT(ValidateManifestStore(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result)))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, EncodeManifestWithRedactions) { | |
| FakeUuidGenerator uuid_generator; | |
| SimpleManifestStoreOptions options = { | |
| .uuid_generator = &uuid_generator, | |
| }; | |
| absl::StatusOr<std::string> ingredient_manifest_store_bytes = | |
| CreateSimpleManifestStoreBytes(options); | |
| ABSL_ASSERT_OK(ingredient_manifest_store_bytes); | |
| ManifestStoreBuilder manifest_store_builder(&hasher_factory_); | |
| absl::StatusOr<IngredientManifestUris> ingredient_manifest_uris = | |
| manifest_store_builder.AddIngredientManifestStore( | |
| *ingredient_manifest_store_bytes); | |
| ABSL_ASSERT_OK(ingredient_manifest_uris); | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| actions { | |
| action: "c2pa.redacted" | |
| description: "redacted for testing" | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = | |
| { | |
| ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| )pb"), | |
| }, | |
| .manifest_store_builder = manifest_store_builder, | |
| }; | |
| std::string redacted_uri = | |
| absl::StrJoin({ingredient_manifest_uris->active_manifest.url(), | |
| kAssertionStoreLabel, kIngredientAssertionV3Label}, | |
| kManifestLabelDelimiter); | |
| manifest_box.actions_assertion.mutable_actions() | |
| ->mutable_actions(1) | |
| ->mutable_parameters() | |
| ->set_redacted_uri(redacted_uri); | |
| *manifest_box.ingredient_assertions[0] | |
| .mutable_ingredient_v3() | |
| ->mutable_active_manifest() = ingredient_manifest_uris->active_manifest; | |
| manifest_box.ingredient_assertions[0] | |
| .mutable_ingredient_v3() | |
| ->mutable_validation_results() | |
| ->mutable_active_manifest() | |
| ->add_successes() | |
| ->set_code("assertion.dataHash.match"); | |
| *manifest_box.ingredient_assertions[0] | |
| .mutable_ingredient_v3() | |
| ->mutable_claim_signature() = ingredient_manifest_uris->claim_signature; | |
| PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie( | |
| R"pb( | |
| active_manifest { | |
| label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| claim { | |
| claim_generator_info { | |
| name: "Test generator" | |
| spec_version: "2.2.0" | |
| } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| title: "test ingredient" | |
| media_type: "video/mp4" | |
| relationship: "parentOf" | |
| instance_id: "test_ingredient" | |
| active_manifest { | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012" | |
| } | |
| validation_results { | |
| active_manifest { | |
| successes { code: "assertion.dataHash.match" } | |
| } | |
| } | |
| claim_signature { | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| description: "test ingredient" | |
| informational_uri: "https://test.com/ingredient" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| description: "created for testing" | |
| software_agent { name: "test generator" } | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| actions { | |
| action: "c2pa.redacted" | |
| description: "redacted for testing" | |
| } | |
| } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "ingredient.claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| } | |
| } | |
| ingredient_manifests { | |
| label: "urn:c2pa:12345678-1234-1234-1234-123456789012" | |
| claim { | |
| claim_generator_info { name: "Test C2PA Generator" } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.boxes" | |
| algorithm: "sha256" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "12345678-1234-1234-1234-123456789012" | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| software_agent { name: "test generator" } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.hash.boxes" | |
| boxes_hash { | |
| boxes { names: "some name" names: "other name" hash: "some hash" } | |
| alg: "some alg" | |
| } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.hash.boxes" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| )pb"); | |
| expected_validated_result.mutable_active_manifest() | |
| ->mutable_claim() | |
| ->add_redacted_assertions(redacted_uri); | |
| expected_validated_result.mutable_active_manifest() | |
| ->mutable_assertions(2) | |
| ->mutable_actions() | |
| ->mutable_actions(1) | |
| ->mutable_parameters() | |
| ->set_redacted_uri(redacted_uri); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| EXPECT_THAT(ValidateClaim(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result.active_manifest().claim()))); | |
| EXPECT_THAT(ValidateManifestStore(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result)))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, EncodesPartHashAssertionsWithRelativePath) { | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "12345678-1234-1234-1234-123456789012", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| } | |
| } | |
| )pb"), | |
| .part_hash_assertions = {ParseTextProtoOrDie( | |
| R"pb( | |
| label: "c2pa.hash.bmff.v3.part" | |
| bmff_based_hash { | |
| exclusions { xpath: "" } | |
| } | |
| )pb"), | |
| ParseTextProtoOrDie( | |
| R"pb( | |
| label: "c2pa.hash.bmff.v3.part__1" | |
| bmff_based_hash { | |
| exclusions { xpath: "" } | |
| } | |
| )pb")}, | |
| .multi_part_assertion = {ParseTextProtoOrDie( | |
| R"pb( | |
| label: "c2pa.hash.multi-asset" | |
| multi_asset_hash { | |
| parts { | |
| location { byte_offset: 0 length: 10 } | |
| hash_assertion { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3.part" | |
| algorithm: "sha256" | |
| hash: "some hash" | |
| } | |
| } | |
| parts { | |
| location { byte_offset: 10 length: 10 } | |
| hash_assertion { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3.part__1" | |
| algorithm: "sha256" | |
| hash: "some hash" | |
| } | |
| } | |
| pad: "\000\000" | |
| pad2: "" | |
| } | |
| )pb")}, | |
| .ingredient_assertions = {}, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie( | |
| R"pb( | |
| active_manifest { | |
| label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a" | |
| claim { | |
| claim_generator_info { | |
| name: "Test generator" | |
| spec_version: "2.2.0" | |
| } | |
| signature_info { | |
| issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3.part" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3.part__1" | |
| algorithm: "sha256" | |
| } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.multi-asset" | |
| algorithm: "sha256" | |
| } | |
| default_algorithm: "sha256" | |
| label: "c2pa.claim.v2" | |
| instance_id: "12345678-1234-1234-1234-123456789012" | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| software_agent { name: "test generator" } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3.part" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| assertions { | |
| label: "c2pa.hash.bmff.v3.part__1" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| } | |
| assertions { | |
| label: "c2pa.hash.multi-asset" | |
| multi_asset_hash { | |
| parts { | |
| location { byte_offset: 0 length: 10 } | |
| hash_assertion { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3.part" | |
| } | |
| } | |
| parts { | |
| location { byte_offset: 10 length: 10 } | |
| hash_assertion { | |
| url: "self#jumbf=c2pa.assertions/c2pa.hash.bmff.v3.part__1" | |
| } | |
| } | |
| pad: "\000\000" | |
| pad2: "" | |
| } | |
| } | |
| validation { | |
| successes { | |
| code: "timeStamp.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "timeStamp.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "signingCredential.trusted" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.insideValidity" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "claimSignature.validated" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.multi-asset" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3.part" | |
| } | |
| successes { | |
| code: "assertion.hashedURI.match" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3.part__1" | |
| } | |
| informationals { | |
| code: "signingCredential.ocsp.skipped" | |
| url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature" | |
| } | |
| } | |
| } | |
| hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.bmff.v3" | |
| multi_asset_hash_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.multi-asset" | |
| )pb"); | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| EXPECT_THAT(ValidateClaim(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result.active_manifest().claim()))); | |
| EXPECT_THAT(ValidateManifestStore(result_manifest_store_bytes.Flatten()), | |
| IsOkAndHolds(Pointee(EqualsProtoIgnoringFieldsAndOrdering( | |
| expected_validated_result)))); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, EncodeManifestWithGatheredAssertions) { | |
| std::string label = "com.example.gathered"; | |
| std::string json_data = "{\"test\": \"data\"}"; | |
| GatheredAssertion gathered; | |
| gathered.set_label(label); | |
| gathered.set_data(json_data); | |
| gathered.set_content_type(GatheredAssertion::CONTENT_TYPE_JSON); | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "test_instance_id", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = {}, | |
| .gathered_assertions = {gathered}, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_manifest_store_box = | |
| Encode(manifest_box_encoder); | |
| ABSL_ASSERT_OK(built_manifest_store_box); | |
| absl::Cord result_manifest_store_bytes = | |
| std::move(*built_manifest_store_box).AsCord(); | |
| absl::StatusOr<Claim> claim = | |
| ValidateClaim(result_manifest_store_bytes.Flatten()); | |
| ABSL_ASSERT_OK(claim); | |
| ASSERT_EQ(claim->gathered_assertions_size(), 1); | |
| EXPECT_EQ(claim->gathered_assertions(0).url(), | |
| "self#jumbf=c2pa.assertions/com.example.gathered"); | |
| absl::StatusOr<std::unique_ptr<PartialValidationResultProto>> validated = | |
| ValidateManifestStore(result_manifest_store_bytes.Flatten()); | |
| ABSL_ASSERT_OK(validated); | |
| ASSERT_TRUE((*validated)->has_active_manifest()); | |
| bool found_match = false; | |
| for (const auto& success : | |
| (*validated)->active_manifest().validation().successes()) { | |
| if (success.code() == "assertion.hashedURI.match" && | |
| success.url() == | |
| "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/" | |
| "c2pa.assertions/com.example.gathered") { | |
| found_match = true; | |
| break; | |
| } | |
| } | |
| EXPECT_TRUE(found_match); | |
| } | |
| TEST_F(ManifestBoxEncoderTest, | |
| EncodeManifestWithInvalidGatheredAssertionContentTypeInvalid) { | |
| GatheredAssertion gathered; | |
| gathered.set_label("com.example.gathered"); | |
| gathered.set_data("{}"); | |
| gathered.set_content_type(GatheredAssertion::CONTENT_TYPE_UNSPECIFIED); | |
| ManifestBox manifest_box = { | |
| .label = "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a", | |
| .instance_id = "test_instance_id", | |
| .hard_binding_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.hash.bmff.v3" | |
| bmff_based_hash { exclusions { xpath: "" } } | |
| )pb"), | |
| .actions_assertion = ParseTextProtoOrDie(R"pb( | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.created" | |
| software_agent { name: "test generator" } | |
| description: "created for testing" | |
| digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture" | |
| } | |
| } | |
| )pb"), | |
| .ingredient_assertions = {}, | |
| .gathered_assertions = {gathered}, | |
| .manifest_store_builder = ManifestStoreBuilder(&hasher_factory_), | |
| }; | |
| AssertionReferencer assertion_reference(&hasher_factory_); | |
| LocalClaimSigner claim_signer(crypto_writer_.get(), &hasher_factory_); | |
| ManifestBoxEncoder manifest_box_encoder( | |
| &assertion_reference, &generator_info_, &claim_signer, | |
| &assertion_cbor_encoder_, &claim_cbor_encoder_, &manifest_box, | |
| hasher_factory_.algorithm()); | |
| EXPECT_THAT(Encode(manifest_box_encoder), | |
| StatusIs(absl::StatusCode::kInvalidArgument, | |
| HasSubstr("content type must be JSON"))); | |
| } | |
| } // namespace | |
| } // namespace credentio |