| // 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/validator.h" | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/log/check.h" | |
| #include "absl/status/status_matchers.h" | |
| #include "absl/strings/cord.h" | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/strings/substitute.h" | |
| #include "constants/labels.h" | |
| #include "constants/status_codes.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "jumbf/box_builder.h" | |
| #include "jumbf/constants.h" | |
| #include "jumbf/encode_params.h" | |
| #include "proto/cose_verification_result.pb.h" | |
| #include "proto/manifest.pb.h" | |
| #include "proto/signature_info.pb.h" | |
| #include "testing/cbor_utils.h" | |
| #include "testing/jumbf_utils.h" | |
| #include "testing/mock_cose_verifier.h" | |
| #include "testing/proto_test_utils.h" | |
| #include "testing/test_validation_tracker.h" | |
| #include "uuid/uuid.h" | |
| #include "validator/tracker.h" | |
| namespace credentio { | |
| namespace { | |
| using ::cbor::FromJson; | |
| using ::credentio_testing::EqualsProto; | |
| using ::credentio_testing::ParseTextProtoOrDie; | |
| using ::jumbf::SuperBoxBuilder; | |
| using ::testing::Eq; | |
| using ::testing::IsEmpty; | |
| using ::testing::Optional; | |
| using ::testing::Return; | |
| using ::testing::SizeIs; | |
| struct ClaimBoxOptions { | |
| std::string signature; | |
| }; | |
| jumbf::BuiltSuperBox CreateClaimBox(const ClaimBoxOptions& options) { | |
| auto cbor = FromJson(absl::Substitute( | |
| R"json({"signature": "$0", "created_assertions": []})json", | |
| options.signature)); | |
| SuperBoxBuilder builder(kClaimUuid, jumbf::DescriptionBoxOptions{ | |
| .label = absl::Cord(kClaimV2Label)}); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, absl::Cord(cbor))); | |
| auto built_box = std::move(builder).Finalize(); | |
| CHECK_OK(built_box); | |
| return *std::move(built_box); | |
| } | |
| jumbf::BuiltSuperBox CreateClaimBoxFromJson(absl::string_view json) { | |
| auto cbor = FromJson(json); | |
| SuperBoxBuilder builder(kClaimUuid, jumbf::DescriptionBoxOptions{ | |
| .label = absl::Cord(kClaimV2Label)}); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, absl::Cord(cbor))); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_box = | |
| std::move(builder).Finalize(); | |
| CHECK_OK(built_box); | |
| return *std::move(built_box); | |
| } | |
| jumbf::BuiltSuperBox CreateOldClaimBoxFromJson(absl::string_view json) { | |
| auto cbor = FromJson(json); | |
| SuperBoxBuilder builder(kClaimUuid, jumbf::DescriptionBoxOptions{ | |
| .label = absl::Cord(kClaimV1Label)}); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, absl::Cord(cbor))); | |
| absl::StatusOr<jumbf::BuiltSuperBox> box = std::move(builder).Finalize(); | |
| CHECK_OK(box); | |
| return *std::move(box); | |
| } | |
| jumbf::BuiltSuperBox CreateClaimBoxFromCbor(absl::string_view cbor) { | |
| SuperBoxBuilder builder(kClaimUuid, jumbf::DescriptionBoxOptions{ | |
| .label = absl::Cord(kClaimV2Label)}); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, absl::Cord(cbor))); | |
| auto built_box = std::move(builder).Finalize(); | |
| CHECK_OK(built_box); | |
| return *std::move(built_box); | |
| } | |
| jumbf::BuiltSuperBox CreateClaimBoxWithMultipleContents( | |
| absl::string_view cbor, absl::string_view other_cbor) { | |
| SuperBoxBuilder builder(kClaimUuid, jumbf::DescriptionBoxOptions{ | |
| .label = absl::Cord(kClaimV2Label)}); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, absl::Cord(cbor))); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, absl::Cord(other_cbor))); | |
| auto built_box = std::move(builder).Finalize(); | |
| CHECK_OK(built_box); | |
| return *std::move(built_box); | |
| } | |
| jumbf::BuiltSuperBox CreateClaimSignatureBox( | |
| absl::string_view cose_certificate) { | |
| SuperBoxBuilder builder( | |
| kClaimSignatureUuid, | |
| jumbf::DescriptionBoxOptions{.label = absl::Cord(kClaimSignatureLabel)}); | |
| CHECK_OK( | |
| builder.AddContent(jumbf::kCborBoxType, absl::Cord(cose_certificate))); | |
| absl::StatusOr<jumbf::BuiltSuperBox> built_box = | |
| std::move(builder).Finalize(); | |
| CHECK_OK(built_box); | |
| return *std::move(built_box); | |
| } | |
| jumbf::BuiltSuperBox CreateClaimSignatureBoxWithMultipleContents( | |
| absl::string_view cose_certificate, | |
| absl::string_view other_cose_certificate) { | |
| SuperBoxBuilder builder( | |
| kClaimSignatureUuid, | |
| jumbf::DescriptionBoxOptions{.label = absl::Cord(kClaimSignatureLabel)}); | |
| CHECK_OK( | |
| builder.AddContent(jumbf::kCborBoxType, absl::Cord(cose_certificate))); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, | |
| absl::Cord(other_cose_certificate))); | |
| auto built_box = std::move(builder).Finalize(); | |
| CHECK_OK(built_box); | |
| return *std::move(built_box); | |
| } | |
| jumbf::BuiltSuperBox CreateAssertionStoreBox() { | |
| SuperBoxBuilder builder( | |
| kAssertionStoreUuid, | |
| jumbf::DescriptionBoxOptions{.label = absl::Cord(kAssertionStoreLabel)}); | |
| CHECK_OK(builder.AddContent(jumbf::kCborBoxType, absl::Cord("foo"))); | |
| auto built_box = std::move(builder).Finalize(); | |
| CHECK_OK(built_box); | |
| return *std::move(built_box); | |
| } | |
| SuperBoxBuilder CreateManifestBuilder() { | |
| return SuperBoxBuilder( | |
| kStandardManifestUuid, | |
| jumbf::DescriptionBoxOptions{.label = absl::Cord("c2pa.manifest")}); | |
| } | |
| std::optional<Claim> ValidateWithBuilder(const ClaimValidatorImpl& validator, | |
| SuperBoxBuilder builder, | |
| ValidationTracker& tracker) { | |
| auto built_super_box = std::move(builder).Finalize(); | |
| CHECK_OK(built_super_box); | |
| std::string manifest_bytes = | |
| std::string(std::move(*built_super_box).AsCord()); | |
| return validator.Validate(ParseSuperBoxOrDie(&manifest_bytes), tracker); | |
| } | |
| TEST(ValidateClaimTest, ClaimMissing) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimSignatureBox("some cbor"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimSignatureMissing) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromCbor("some cbor"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimSignatureMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, BothClaimAndSignatureMissing) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| auto failures = t.GetFailures(); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, MultipleClaims) { | |
| auto manifest = CreateManifestBuilder(); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify).Times(0); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromCbor("some cbor"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromCbor("some other cbor"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimSignatureBox("some cbor"))); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMultiple)); | |
| EXPECT_THAT(t.GetFailures(), SizeIs(1)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimHasMultipleContents) { | |
| auto manifest = CreateManifestBuilder(); | |
| auto claim_box = | |
| CreateClaimBoxWithMultipleContents("some cbor", "some other cbor"); | |
| ABSL_ASSERT_OK(manifest.AddChild(std::move(claim_box))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimSignatureBox("some cbor"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimCborInvalid)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimIsNotCborBox) { | |
| auto manifest = CreateManifestBuilder(); | |
| SuperBoxBuilder claim_box( | |
| kClaimUuid, | |
| jumbf::DescriptionBoxOptions{.label = absl::Cord(kClaimV2Label)}); | |
| SuperBoxBuilder child_box(kClaimSignatureUuid, {}); | |
| ABSL_ASSERT_OK(child_box.AddContent(jumbf::kCborBoxType, absl::Cord("foo"))); | |
| ABSL_ASSERT_OK(claim_box.AddChild(std::move(child_box))); | |
| ABSL_ASSERT_OK(manifest.AddChild(std::move(claim_box))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimSignatureBox("some cbor"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimCborInvalid)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimSignatureHasMultipleContents) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromCbor("some cbor"))); | |
| auto claim_signature_box = CreateClaimSignatureBoxWithMultipleContents( | |
| "some cbor", "some other cbor"); | |
| ABSL_ASSERT_OK(manifest.AddChild(std::move(claim_signature_box))); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimSignatureMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimSignatureIsNotCborBox) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromCbor("some_cbor"))); | |
| SuperBoxBuilder claim_signature_box( | |
| kClaimSignatureUuid, | |
| jumbf::DescriptionBoxOptions{.label = absl::Cord(kClaimSignatureLabel)}); | |
| SuperBoxBuilder child_box(kClaimUuid, {}); | |
| ABSL_ASSERT_OK(child_box.AddContent(jumbf::kCborBoxType, absl::Cord("foo"))); | |
| ABSL_ASSERT_OK(claim_signature_box.AddChild(std::move(child_box))); | |
| ABSL_ASSERT_OK(manifest.AddChild(std::move(claim_signature_box))); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimSignatureMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimCborIsNotAMap) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimBoxFromJson(R"json([1, 2, 3])json"))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimCborInvalid)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimHasIncorrectSignature) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBox({.signature = "not_valid"}))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimSignatureMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, CoseVerificationFails) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild( | |
| CreateClaimBox({.signature = "self#jumbf=c2pa.signature"}))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce([](absl::string_view cose_certificate, | |
| absl::string_view claim_cbor) { | |
| CoseVerificationResult result; | |
| result.mutable_validation_status()->mutable_failures()->Add()->set_code( | |
| absl::StrCat(FailureStatusCode::kGeneralError)); | |
| return result; | |
| }); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kGeneralError)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| } | |
| TEST(ValidateClaimTest, CoseVerifierFailsWithoutRecordingStatus) { | |
| auto manifest = CreateManifestBuilder(); | |
| ABSL_ASSERT_OK(manifest.AddChild( | |
| CreateClaimBox({.signature = "self#jumbf=c2pa.signature"}))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| // Internal error recorded because the mock verifier didn't record one. | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kGoogleInternalError)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| } | |
| TEST(ValidateClaimTest, CreatedAssertionIsNotMap) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "signature": "self#jumbf=c2pa.signature", | |
| "created_assertions": [{"url":"self#jumbf=c2pa.some_assertion"}], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kHashedUriMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, AssertionUriIsNotString) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "signature": "self#jumbf=c2pa.signature", | |
| "created_assertions": [{ | |
| "url": 12, | |
| "hash": "b64'abcd'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kHashedUriMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, HashedUriMissingRequiredFieldInCreated) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "signature": "self#jumbf=c2pa.signature", | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_assertion" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kHashedUriMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, HashedUriMissingRequiredFieldInGathered) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "signature": "self#jumbf=c2pa.signature", | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_assertion", | |
| "hash": "b64'abcd'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_assertion" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kHashedUriMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, HashedUriMissingRequiredFieldInRedacted) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "signature": "self#jumbf=c2pa.signature", | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_assertion", | |
| "hash": "b64'abcd'" | |
| }], | |
| "redacted_assertions": [12] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kHashedUriMissing)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimV1ProvidedWithClaimV2Label) { | |
| auto manifest = CreateManifestBuilder(); | |
| // Note use of v1 `assertions`. | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "dc:format": "image/jpeg", | |
| "assertions": [ | |
| {"url": "self#jumbf=c2pa.some_created_assertion", "hash": "b64'aGVsbG8'"}, | |
| {"url": "self#jumbf=c2pa.some_gathered_assertion", "hash": "b64'aGVsbG8'"} | |
| ] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild( | |
| CreateClaimBoxFromJson(claim_json))); // Uses c2pa.claim.v2 label. | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailureStatuses(), | |
| Contains(C2paStatusIs(FailureStatusCode::kClaimMalformed, | |
| "Claim is missing created assertions"))); | |
| } | |
| TEST(ValidateClaimTest, ClaimV1Unsupported) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "dc:format": "image/jpeg", | |
| "assertions": [ | |
| {"url": "self#jumbf=c2pa.some_created_assertion", "hash": "b64'aGVsbG8'"}, | |
| {"url": "self#jumbf=c2pa.some_gathered_assertion", "hash": "b64'aGVsbG8'"} | |
| ], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateOldClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT( | |
| t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kGoogleUnsupportedSpecVersion)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2HasNoSignature) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "dc:format": "image/jpeg", | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_created_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| EXPECT_CALL(mock_cose_verifier, Verify) | |
| .WillOnce(Return(CoseVerificationResult())); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMalformed)); | |
| EXPECT_THAT(t.GetSuccesses(), IsEmpty()); | |
| EXPECT_THAT(result, Eq(std::nullopt)); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2HasNoInstanceId) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "dc:format": "image/jpeg", | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_created_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMalformed)); | |
| EXPECT_THAT( | |
| result, Optional(EqualsProto(ParseTextProtoOrDie<Claim>(R"pb( | |
| claim_generator_info { | |
| name: "test_generator" | |
| version: "1.0" | |
| icon { url: "some_icon_link" hash: "hello" } | |
| operating_system: "linux" | |
| } | |
| signature_info { cert_serial_number: "1234" issuer: "fake_issuer" } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.some_created_assertion" | |
| algorithm: "" | |
| hash: "hello" | |
| } | |
| gathered_assertions { | |
| url: "self#jumbf=c2pa.some_gathered_assertion" | |
| algorithm: "" | |
| hash: "hello" | |
| } | |
| label: "c2pa.claim.v2" | |
| redacted_assertions: "self#jumbf=c2pa.some_redacted_assertion" | |
| )pb")))); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2HasNoGeneratorInfo) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "dc:format": "image/jpeg", | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_created_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMalformed)); | |
| EXPECT_THAT( | |
| result, Optional(EqualsProto(ParseTextProtoOrDie<Claim>(R"pb( | |
| instance_id: "1234" | |
| label: "c2pa.claim.v2" | |
| signature_info { cert_serial_number: "1234" issuer: "fake_issuer" } | |
| claim_generator_info {} | |
| created_assertions { | |
| url: "self#jumbf=c2pa.some_created_assertion" | |
| algorithm: "" | |
| hash: "hello" | |
| } | |
| gathered_assertions { | |
| url: "self#jumbf=c2pa.some_gathered_assertion" | |
| algorithm: "" | |
| hash: "hello" | |
| } | |
| redacted_assertions: "self#jumbf=c2pa.some_redacted_assertion" | |
| )pb")))); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2ProvidedInLegacyBox) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_created_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateOldClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT( | |
| t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kGoogleUnsupportedSpecVersion)); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2WithDcFormat) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "dc:format": "image/jpeg", | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_created_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| // We ignore `dc:format` fields in non-legacy claim boxes. | |
| EXPECT_THAT(t.GetFailures(), IsEmpty()); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2LegacyAssertionsIgnored) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "assertions": [ | |
| {"url": "self#jumbf=c2pa.some_created_assertion", "hash": "b64'aGVsbG8'"}, | |
| {"url": "self#jumbf=c2pa.some_gathered_assertion", "hash": "b64'aGVsbG8'"} | |
| ], | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_created_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), IsEmpty()); | |
| ASSERT_TRUE(result.has_value()); | |
| EXPECT_THAT(result->created_assertions(), SizeIs(1)); | |
| EXPECT_THAT(result->gathered_assertions(), SizeIs(1)); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2MissingSignature) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMalformed)); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2MissingCreatedAssertions) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kClaimMalformed)); | |
| } | |
| TEST(ValidateClaimTest, ClaimV2Ok) { | |
| auto manifest = CreateManifestBuilder(); | |
| absl::string_view claim_json = R"json({ | |
| "instanceID": "1234", | |
| "signature": "self#jumbf=c2pa.signature", | |
| "signature_info": { | |
| "cert_serial_number": "1234", | |
| "issuer": "fake_issuer" | |
| }, | |
| "claim_generator_info": { | |
| "name": "test_generator", | |
| "version": "1.0", | |
| "icon": { | |
| "url": "some_icon_link", | |
| "hash": "b64'aGVsbG8'" | |
| }, | |
| "operating_system": "linux" | |
| }, | |
| "created_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_created_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "gathered_assertions": [{ | |
| "url": "self#jumbf=c2pa.some_gathered_assertion", | |
| "hash": "b64'aGVsbG8'" | |
| }], | |
| "redacted_assertions": ["self#jumbf=c2pa.some_redacted_assertion"] | |
| })json"; | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateClaimBoxFromJson(claim_json))); | |
| ABSL_ASSERT_OK( | |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate"))); | |
| ABSL_ASSERT_OK(manifest.AddChild(CreateAssertionStoreBox())); | |
| MockCoseVerifier mock_cose_verifier; | |
| SignatureInfo signature_info; | |
| signature_info.set_issuer("fake_issuer"); | |
| signature_info.set_cert_serial_number("1234"); | |
| CoseVerificationResult verification_result; | |
| *verification_result.mutable_signature_info() = signature_info; | |
| EXPECT_CALL(mock_cose_verifier, Verify).WillOnce(Return(verification_result)); | |
| TestValidationTracker t; | |
| ClaimValidatorImpl validator(&mock_cose_verifier); | |
| auto result = | |
| ValidateWithBuilder(validator, std::move(manifest), t.tracker()); | |
| EXPECT_THAT(t.GetFailures(), IsEmpty()); | |
| EXPECT_THAT( | |
| result, Optional(EqualsProto(ParseTextProtoOrDie<Claim>(R"pb( | |
| instance_id: "1234" | |
| claim_generator_info { | |
| name: "test_generator" | |
| version: "1.0" | |
| icon { url: "some_icon_link" hash: "hello" } | |
| operating_system: "linux" | |
| } | |
| signature_info { cert_serial_number: "1234" issuer: "fake_issuer" } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.some_created_assertion" | |
| algorithm: "" | |
| hash: "hello" | |
| } | |
| gathered_assertions { | |
| url: "self#jumbf=c2pa.some_gathered_assertion" | |
| algorithm: "" | |
| hash: "hello" | |
| } | |
| redacted_assertions: "self#jumbf=c2pa.some_redacted_assertion" | |
| label: "c2pa.claim.v2" | |
| )pb")))); | |
| } | |
| } // namespace | |
| } // namespace credentio |