| // 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 ::absl_testing::IsOk; |
| 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(); |
| ASSERT_THAT(manifest.AddChild(CreateClaimSignatureBox("some cbor")), IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromCbor("some cbor")), IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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.GetFailures(), |
| ContainsFailure(FailureStatusCode::kClaimSignatureMissing)); |
| 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) |
| .WillOnce(Return(CoseVerificationResult())); |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromCbor("some cbor")), IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromCbor("some other cbor")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateClaimSignatureBox("some cbor")), IsOk()); |
| 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.GetSuccesses(), IsEmpty()); |
| EXPECT_THAT(result, Eq(std::nullopt)); |
| } |
| |
| TEST(ValidateClaimTest, ClaimHasMultipleContents) { |
| auto manifest = CreateManifestBuilder(); |
| auto claim_box = |
| CreateClaimBoxWithMultipleContents("some cbor", "some other cbor"); |
| ASSERT_THAT(manifest.AddChild(std::move(claim_box)), IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateClaimSignatureBox("some cbor")), IsOk()); |
| 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, {}); |
| ASSERT_THAT(child_box.AddContent(jumbf::kCborBoxType, absl::Cord("foo")), |
| IsOk()); |
| ASSERT_THAT(claim_box.AddChild(std::move(child_box)), IsOk()); |
| ASSERT_THAT(manifest.AddChild(std::move(claim_box)), IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateClaimSignatureBox("some cbor")), IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromCbor("some cbor")), IsOk()); |
| auto claim_signature_box = CreateClaimSignatureBoxWithMultipleContents( |
| "some cbor", "some other cbor"); |
| ASSERT_THAT(manifest.AddChild(std::move(claim_signature_box)), IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromCbor("some_cbor")), IsOk()); |
| SuperBoxBuilder claim_signature_box( |
| kClaimSignatureUuid, |
| jumbf::DescriptionBoxOptions{.label = absl::Cord(kClaimSignatureLabel)}); |
| SuperBoxBuilder child_box(kClaimUuid, {}); |
| ASSERT_THAT(child_box.AddContent(jumbf::kCborBoxType, absl::Cord("foo")), |
| IsOk()); |
| ASSERT_THAT(claim_signature_box.AddChild(std::move(child_box)), IsOk()); |
| ASSERT_THAT(manifest.AddChild(std::move(claim_signature_box)), IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(R"json([1, 2, 3])json")), |
| IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild(CreateClaimBox({.signature = "not_valid"})), |
| IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild( |
| CreateClaimBox({.signature = "self#jumbf=c2pa.signature"})), |
| IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| 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(); |
| ASSERT_THAT(manifest.AddChild( |
| CreateClaimBox({.signature = "self#jumbf=c2pa.signature"})), |
| IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), |
| IsOk()); // Uses c2pa.claim.v2 label. |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateOldClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateOldClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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"; |
| ASSERT_THAT(manifest.AddChild(CreateClaimBoxFromJson(claim_json)), IsOk()); |
| ASSERT_THAT( |
| manifest.AddChild(CreateClaimSignatureBox("fake_cose_certificate")), |
| IsOk()); |
| ASSERT_THAT(manifest.AddChild(CreateAssertionStoreBox()), IsOk()); |
| 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 |