blob: cfd64d987ceee7dbefa295de5a8e07d2b3b52a39 [file] [edit]
// 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 "assertion/validator.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/no_destructor.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/log.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "assertion/actions_assertion_validator.h"
#include "assertion/assertion_parser.h"
#include "assertion/hashed_uri_validator.h"
#include "assertion/references_validator.h"
#include "constants/ingredient_relationships.h"
#include "constants/labels.h"
#include "constants/status_codes.h"
#include "google/protobuf/repeated_ptr_field.h"
#include "jumbf/box.h"
#include "jumbf/uri.h"
#include "proto/actions_assertion.pb.h"
#include "proto/assertion.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 "validator/tracker.h"
namespace credentio {
namespace {
using ::jumbf::BinaryDataBox;
using ::jumbf::CborBox;
using ::jumbf::EmbeddedFileDescriptionBox;
using ::jumbf::JsonBox;
using ::jumbf::SuperBox;
constexpr absl::string_view kUnknown = "c2pa.unknown";
enum ManifestType {
kStandardManifest,
kUpdateManifest,
};
absl::string_view Basename(absl::string_view path) {
const size_t pos = path.find_last_of('/');
if (pos == absl::string_view::npos) {
return path;
}
return path.substr(pos + 1);
}
template <typename ActionsT>
bool ValidateActions(const ActionsT& actions, absl::string_view assertion_url,
ValidationTracker& validation_tracker) {
for (const auto& action : actions.actions()) {
if (action.action() == kUnknown) {
validation_tracker.RecordFailure(
FailureStatusCode::kGoogleAssertionActionUnknownAction,
{.url = assertion_url});
return false;
}
}
return true;
}
bool HasSuccesses(const ValidationStatusSet& validation) {
return !validation.successes().empty();
}
bool HasFailures(const ValidationStatusSet& validation) {
return !validation.failures().empty();
}
bool IsHardBindingSuccessCode(absl::string_view code) {
static const absl::NoDestructor<absl::flat_hash_set<std::string>>
kHardBindingSuccessCodes({
absl::StrCat(SuccessStatusCode::kAssertionBmffHashMatch),
absl::StrCat(SuccessStatusCode::kAssertionBoxesHashMatch),
absl::StrCat(SuccessStatusCode::kAssertionCollectionHashMatch),
absl::StrCat(SuccessStatusCode::kAssertionDataHashMatch),
});
return kHardBindingSuccessCodes->contains(code);
}
bool HasHardBindingSuccess(const ValidationStatusSet& validation) {
return std::any_of(validation.successes().begin(),
validation.successes().end(), [](const auto& status) {
return IsHardBindingSuccessCode(status.code());
});
}
bool ValidateIngredientAssertionValidationResults(
const IngredientAssertionV3& ingredient, absl::string_view assertion_url,
ManifestType manifest_type, ValidationTracker& validation_tracker) {
if (!ingredient.has_active_manifest()) {
// No validation results expected.
return true;
}
const ValidationStatusSet& ingredient_validation =
ingredient.validation_results().active_manifest();
if (!HasFailures(ingredient_validation)) {
switch (manifest_type) {
case kStandardManifest:
if (!HasHardBindingSuccess(ingredient_validation)) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionIngredientMalformed,
{.url = assertion_url,
.explanation = "no hard binding check recorded"});
return false;
}
break;
case kUpdateManifest:
if (!HasSuccesses(ingredient_validation)) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionIngredientMalformed,
{.url = assertion_url,
.explanation = "no validation successes or failures recorded"});
return false;
}
break;
}
}
return true;
}
bool IsValidIngredientRelationship(absl::string_view relationship) {
return relationship == kIngredientRelationshipParentOf ||
relationship == kIngredientRelationshipComponentOf ||
relationship == kIngredientRelationshipInputTo;
}
bool ValidateIngredientAssertion(const IngredientAssertionV3& ingredient,
absl::string_view assertion_url,
ManifestType manifest_type,
ValidationTracker& validation_tracker) {
if (ingredient.has_active_manifest() &&
!ingredient.digital_source_type().empty()) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionIngredientMalformed,
{.url = assertion_url,
.explanation = "Ingredient assertion must not contain both "
"activeManifest and digitalSourceType"});
return false;
}
// Note that while
// https://spec.c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_performing_explicit_validation
// only calls for relationship validation on v3 ingredient assertions, this
// was subsequently corrected for C2PA 2.2 in
// https://github.com/c2pa-org/specs-core/pull/1513.
if (ingredient.relationship().empty()) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionIngredientMalformed,
{.url = assertion_url,
.explanation = "Missing ingredient relationship"});
return false;
}
if (!IsValidIngredientRelationship(ingredient.relationship())) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionIngredientMalformed,
{.url = assertion_url,
.explanation = "Unrecognized ingredient relationship"});
return false;
}
return ValidateIngredientAssertionValidationResults(
ingredient, assertion_url, manifest_type, validation_tracker);
}
bool ActionAllowedInUpdateManifest(absl::string_view action) {
return action == "c2pa.edited.metadata" || action == "c2pa.opened" ||
action == "c2pa.published" || action == "c2pa.redacted";
}
struct AssertionCounter {
uint32_t hard_bindings = 0;
uint32_t ingredients = 0;
uint32_t thumbnails = 0;
std::string action_disallowed_in_update;
absl::flat_hash_map<std::string, uint32_t> ingredient_relationships;
bool has_soft_binding = false;
bool has_watermarked_bound_action = false;
void RecordAction(absl::string_view action) {
if (action_disallowed_in_update.empty() &&
!ActionAllowedInUpdateManifest(action)) {
action_disallowed_in_update = action;
}
if (action == "c2pa.watermarked.bound" || action == "c2pa.watermarked") {
has_watermarked_bound_action = true;
}
}
void Add(const Assertion& assertion) {
if (assertion.has_soft_binding()) {
has_soft_binding = true;
}
if (assertion.has_data_hash() || assertion.has_bmff_based_hash() ||
assertion.has_boxes_hash() || assertion.has_collection_data_hash()) {
if (!absl::StrContains(assertion.label(), ".part")) {
// Only count the full asset hard binding.
++hard_bindings;
}
}
for (const auto& action : assertion.actions().actions()) {
RecordAction(action.action());
}
for (const auto& action : assertion.actions_v1().actions()) {
RecordAction(action.action());
}
if (assertion.has_ingredient_v3()) {
++ingredients;
++ingredient_relationships[assertion.ingredient_v3().relationship()];
}
if (assertion.has_claim_thumbnail() ||
assertion.has_ingredient_thumbnail()) {
++thumbnails;
}
}
// See:
// https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_validate_the_assertions
void ValidateStandardManifestCounts(absl::string_view claim_box_url,
ValidationTracker& validation_tracker) {
if (hard_bindings == 0) {
validation_tracker.RecordFailure(
FailureStatusCode::kClaimHardBindingsMissing,
{.url = claim_box_url,
.explanation = "Standard manifest must contain one hard binding."});
} else if (hard_bindings > 1) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionMultipleHardBindings,
{.url = kAssertionStoreLabel,
.explanation = "Standard manifest must not contain more than one "
"hard binding."});
}
if (ingredient_relationships[kIngredientRelationshipParentOf] > 1) {
validation_tracker.RecordFailure(
FailureStatusCode::kManifestMultipleParents,
{.url = claim_box_url,
.explanation = "Standard manifest must contain at most one "
"ingredient with parentOf relationship."});
}
}
// See:
// https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_validate_the_assertions
void ValidateUpdateManifestCounts(absl::string_view claim_box_url,
ValidationTracker& validation_tracker) {
if (hard_bindings != 0) {
validation_tracker.RecordFailure(
FailureStatusCode::kManifestUpdateInvalid,
{.url = claim_box_url,
.explanation = "Update manifest must not contain hard bindings."});
}
if (!action_disallowed_in_update.empty()) {
validation_tracker.RecordFailure(
FailureStatusCode::kManifestUpdateInvalid,
{.url = claim_box_url,
.explanation =
absl::StrCat("Update manifest contains disallowed action: ",
action_disallowed_in_update)});
}
if (ingredients != 1) {
validation_tracker.RecordFailure(
FailureStatusCode::kManifestUpdateWrongParents,
{.url = claim_box_url,
.explanation = absl::StrCat(
"Update manifest must contain exactly one ingredient; found ",
ingredients)});
} else if (ingredient_relationships[kIngredientRelationshipParentOf] != 1) {
validation_tracker.RecordFailure(
FailureStatusCode::kManifestUpdateWrongParents,
{.url = claim_box_url,
.explanation =
"Update manifest ingredient relationship must be parentOf."});
}
}
void ValidateSoftBinding(absl::string_view claim_box_url,
ValidationTracker& validation_tracker) {
if (has_watermarked_bound_action && !has_soft_binding) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionActionSoftBindingMissing,
{.url = claim_box_url,
.explanation = "c2pa.watermarked.bound action requires a "
"c2pa.soft-binding assertion"});
}
}
};
void ValidateCborAssertion(
absl::string_view label, absl::string_view payload,
ManifestType manifest_type, const AssertionParser& assertion_parser,
const ReferencesValidator& references_validator,
google::protobuf::RepeatedPtrField<Assertion>* assertions_out,
ValidationTracker& validation_tracker,
AssertionCounter& assertion_counter) {
std::optional<Assertion> assertion_proto =
assertion_parser.ParseCbor(label, payload, validation_tracker);
if (!assertion_proto.has_value()) {
return;
}
assertion_counter.Add(*assertion_proto);
if (!references_validator.Validate(*assertion_proto)) {
return;
}
const std::string assertion_url = absl::StrCat(
kAssertionStoreLabel, kManifestLabelDelimiter, assertion_proto->label());
switch (assertion_proto->assertion_case()) {
case Assertion::kActionsV1:
if (!ValidateActions(assertion_proto->actions_v1(), assertion_url,
validation_tracker)) {
return;
}
break;
case Assertion::kActions:
if (!ValidateActions(assertion_proto->actions(), assertion_url,
validation_tracker)) {
return;
}
break;
case Assertion::kIngredientV3:
if (!ValidateIngredientAssertion(assertion_proto->ingredient_v3(),
assertion_url, manifest_type,
validation_tracker)) {
return;
}
break;
default:
break;
}
assertions_out->Add(*std::move(assertion_proto));
}
void ValidateAssertionContents(
const SuperBox* assertion_box, ManifestType manifest_type,
const AssertionParser& assertion_parser,
const ReferencesValidator& references_validator,
google::protobuf::RepeatedPtrField<Assertion>* assertions_out,
ValidationTracker& validation_tracker,
AssertionCounter& assertion_counter) {
// CBOR assertion.
if (assertion_box->contents.size() == 1 &&
assertion_box->contents[0].Holds<CborBox>()) {
ValidateCborAssertion(assertion_box->description.label.value_or(""),
assertion_box->contents[0].Get<CborBox>().payload,
manifest_type, assertion_parser, references_validator,
assertions_out, validation_tracker,
assertion_counter);
return;
}
// Thumbnail assertion (EmbeddedFileDescriptionBox + BinaryDataBox).
if (assertion_box->contents.size() == 2 &&
assertion_box->contents[0].Holds<EmbeddedFileDescriptionBox>() &&
assertion_box->contents[1].Holds<BinaryDataBox>()) {
std::optional<Assertion> assertion_proto = assertion_parser.ParseThumbnail(
assertion_box->description.label.value_or(""),
assertion_box->contents[0].Get<EmbeddedFileDescriptionBox>().media_type,
assertion_box->contents[1].Get<BinaryDataBox>().payload,
assertion_box->contents[0].Get<EmbeddedFileDescriptionBox>().file_name,
validation_tracker);
if (assertion_proto.has_value()) {
assertions_out->Add(*std::move(assertion_proto));
}
return;
}
// JSON-LD assertion (e.g., c2pa.metadata).
if (assertion_box->contents.size() == 1 &&
assertion_box->contents[0].Holds<JsonBox>()) {
std::optional<Assertion> assertion_proto = assertion_parser.ParseMetadata(
assertion_box->description.label.value_or(""),
assertion_box->contents[0].Get<JsonBox>().payload, validation_tracker);
if (assertion_proto.has_value()) {
assertions_out->Add(*std::move(assertion_proto));
}
return;
}
// Ignore other types of assertions.
}
bool ContainsAllZeroBytes(absl::string_view data) {
return std::all_of(data.begin(), data.end(), [](char c) { return c == 0; });
}
bool ContainsAllZeroBytes(const jumbf::ContentBox& content) {
if (content.Holds<CborBox>()) {
return ContainsAllZeroBytes(content.Get<CborBox>().payload);
}
if (content.Holds<JsonBox>()) {
return ContainsAllZeroBytes(content.Get<JsonBox>().payload);
}
if (content.Holds<BinaryDataBox>()) {
return ContainsAllZeroBytes(content.Get<BinaryDataBox>().payload);
}
if (content.Holds<EmbeddedFileDescriptionBox>()) {
// We don't (yet) require redaction of the file description.
return true;
}
return false;
}
bool ContainsAllZeroBytes(const jumbf::SuperBox& box) {
return std::all_of(
box.contents.begin(), box.contents.end(),
[](const auto& content) { return ContainsAllZeroBytes(content); });
}
void ValidateRedactionsAllowed(const Claim& claim,
ValidationTracker& validation_tracker) {
// Note that the selfRedacted check is done in ManifestGraph.
const AssertionTypeMatcher actions_v1_matcher(kActionsAssertionV1Label);
const AssertionTypeMatcher actions_v2_matcher(kActionsAssertionV2Label);
for (const auto& redacted_assertion : claim.redacted_assertions()) {
absl::string_view label = Basename(redacted_assertion);
if (actions_v1_matcher.Matches(label) ||
actions_v2_matcher.Matches(label)) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionActionRedacted,
{.url = absl::StrCat(kAssertionStoreLabel, kManifestLabelDelimiter,
label)});
}
}
}
// Returns true if `path` is a child of `parent`.
bool IsChildPath(absl::string_view path, absl::string_view parent) {
if (!absl::StartsWith(path, parent)) return false;
if (path.size() <= parent.size()) return false;
if (path[parent.size()] != '/') return false;
return path.find('/', parent.size() + 1) == absl::string_view::npos;
}
// Returns true if the validation succeeded.
bool ValidateRedactedAssertion(absl::string_view path,
const jumbf::UriResolver& uri_resolver,
ValidationTracker& validation_tracker) {
// https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html#_claim_signature_hash_validation_method
// "if the referenced assertion is present and its data consists of anything
// other than zero or more 0x00 bytes, the claim shall be rejected with a
// failure code of assertion.notRedacted."
auto box = uri_resolver.ResolvePath(path);
if (!box.ok()) {
return true;
}
if (!ContainsAllZeroBytes(**box)) {
validation_tracker.RecordFailure(FailureStatusCode::kAssertionNotRedacted,
{.url = path});
return false;
}
return true;
}
std::optional<HashedUri> ConvertToHashedUri(
const InternalOrExternalHashedUri& uri, absl::string_view default_algorithm,
absl::string_view claim_uri, ValidationTracker& validation_tracker) {
if (!uri.has_url()) {
validation_tracker.RecordFailure(
FailureStatusCode::kHashedUriMissing,
{.url = claim_uri, .explanation = "Icon URI is missing"});
return std::nullopt;
}
if (!absl::StartsWith(uri.url(), "self#jumbf=")) {
validation_tracker.RecordFailure(
FailureStatusCode::kGoogleHashedUriUnsupported,
{.url = claim_uri,
.explanation = "Icon URI is pointing to an external resource"});
return std::nullopt;
}
HashedUri hashed_uri;
hashed_uri.set_url(uri.url());
hashed_uri.set_hash(uri.hash());
hashed_uri.set_algorithm(uri.has_algorithm() ? uri.algorithm()
: default_algorithm);
return hashed_uri;
}
void ValidateAssertion(
const HashedUri& hashed_uri, absl::string_view manifest_path,
ManifestType manifest_type,
const absl::flat_hash_set<std::string>& redacted_assertion_paths,
const HashedUriValidator& assertion_uri_validator,
const ReferencesValidator& references_validator,
absl::string_view assertion_store_path, const Claim& claim,
const jumbf::UriResolver& uri_resolver,
const AssertionParser& assertion_parser,
google::protobuf::RepeatedPtrField<Assertion>* assertions_out,
absl::flat_hash_set<std::string>& assertion_labels,
ValidationTracker& validation_tracker,
AssertionCounter& assertion_counter) {
auto assertion_path = jumbf::UriResolver::GetAbsolutePathFromUri(
hashed_uri.url(), manifest_path);
if (!assertion_path.ok()) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionMissing,
{.explanation = "malformed assertion URI"});
return;
}
if (redacted_assertion_paths.contains(*assertion_path)) {
if (ValidateRedactedAssertion(*assertion_path, uri_resolver,
validation_tracker)) {
Assertion* redacted_assertion = assertions_out->Add();
redacted_assertion->set_label(Basename(*assertion_path));
redacted_assertion->mutable_redacted_by_generator();
}
// Don't treat the redacted assertion as undeclared.
assertion_labels.insert(std::string(Basename(*assertion_path)));
return;
}
if (!assertion_uri_validator
.Validate(hashed_uri, absl::StrCat("self#jumbf=", *assertion_path),
validation_tracker)
.has_value()) {
return; // Failure has been recorded by Validate().
}
validation_tracker.RecordSuccess(
SuccessStatusCode::kAssertionHashedUriMatch,
{.url = absl::StrCat("self#jumbf=", *assertion_path)});
if (!absl::StartsWith(*assertion_path, manifest_path)) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionOutsideManifest,
{.url = claim.label(),
.explanation =
absl::StrCat("Violated claim assertion: ", *assertion_path)});
return;
}
if (!IsChildPath(*assertion_path, assertion_store_path)) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionMissing,
{.url = claim.label(),
.explanation =
absl::StrCat("Violated claim assertion: ", *assertion_path)});
return;
}
auto assertion_box = uri_resolver.ResolvePath(*assertion_path);
if (!assertion_box.ok()) {
// Can't happen -- assertion_uri_validator.Validate would have failed.
DLOG(FATAL) << "unexpected missing assertion: " << assertion_box.status();
validation_tracker.RecordFailure(
FailureStatusCode::kGoogleInternalError,
{.url = claim.label(), .explanation = "unexpected missing assertion"});
return;
}
ValidateAssertionContents(*assertion_box, manifest_type, assertion_parser,
references_validator, assertions_out,
validation_tracker, assertion_counter);
assertion_labels.insert(std::string(Basename(*assertion_path)));
}
void ValidateClaimGeneratorIcon(const Claim& claim,
absl::string_view manifest_path,
const HashedUriValidator& hashed_uri_validator,
ValidationTracker& validation_tracker) {
if (!claim.claim_generator_info().has_icon()) {
return;
}
std::optional<HashedUri> icon_hashed_uri = ConvertToHashedUri(
claim.claim_generator_info().icon(), claim.default_algorithm(),
claim.label(), validation_tracker);
if (icon_hashed_uri.has_value()) {
auto icon_path = jumbf::UriResolver::GetAbsolutePathFromUri(
icon_hashed_uri->url(), manifest_path);
if (!icon_path.ok()) {
// The only way this can happen is if the icon URI doesn't start with
// "self#jumbf=". This would have already been caught by
// ConvertToHashedUri.
validation_tracker.RecordFailure(
FailureStatusCode::kHashedUriMissing,
{.url = claim.label(), .explanation = "Icon URI is malformed"});
} else if (hashed_uri_validator
.Validate(*icon_hashed_uri, claim.label(),
validation_tracker)
.has_value()) {
// No success code for icon URI validation. See
// https://spec.c2pa.org/specifications/specifications/2.4/specs/C2PA_Specification.html#_validation_of_references
}
}
}
void ValidateUndeclaredAssertions(
absl::string_view assertion_store_path,
const absl::flat_hash_set<std::string>& assertion_labels,
const jumbf::UriResolver& uri_resolver,
ValidationTracker& validation_tracker) {
auto assertion_store_box = uri_resolver.ResolvePath(assertion_store_path);
if (!assertion_store_box.ok()) {
return;
}
for (const auto& content : (*assertion_store_box)->contents) {
if (!content.Holds<SuperBox>()) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionUndeclared,
{.explanation =
"Assertion store contains a box that is not a superbox."});
continue;
}
const SuperBox& assertion_box = content.Get<SuperBox>();
auto assertion_label = assertion_box.description.label.value_or("");
if (!assertion_labels.contains(assertion_label)) {
validation_tracker.RecordFailure(
FailureStatusCode::kAssertionUndeclared,
{.url = absl::StrCat(kAssertionStoreLabel, kManifestLabelDelimiter,
assertion_label)});
continue;
}
}
}
} // namespace
void AssertionValidator::ValidateClaimAssertions(
const jumbf::UriResolver& uri_resolver,
const absl::flat_hash_set<std::string>& redacted_assertion_paths,
Manifest& manifest, ValidationTracker& validation_tracker) const {
std::string manifest_path = absl::StrCat("/c2pa/", manifest.label());
const Claim& claim = manifest.claim();
AssertionCounter assertion_counter;
auto assertion_store_path =
jumbf::UriResolver::GetAbsolutePath(kAssertionStoreLabel, manifest_path);
// Validates claim assertion URIs.
HashedUriValidator assertion_uri_validator(std::string(manifest_path),
uri_resolver);
assertion_uri_validator.SetErrorCodes(HashedUriValidator::Codes{
.missing = FailureStatusCode::kAssertionMissing,
.mismatch = FailureStatusCode::kAssertionHashedUriMismatch});
assertion_uri_validator.SetDefaultAlgorithm(claim.default_algorithm());
HashedUriValidator hashed_uri_validator(std::string(manifest_path),
uri_resolver);
hashed_uri_validator.SetDefaultAlgorithm(claim.default_algorithm());
ValidateClaimGeneratorIcon(claim, manifest_path, hashed_uri_validator,
validation_tracker);
ManifestType manifest_type = manifest.is_update_manifest()
? ManifestType::kUpdateManifest
: ManifestType::kStandardManifest;
ReferencesValidator references_validator(
&hashed_uri_validator, manifest.label(), &validation_tracker);
absl::flat_hash_set<std::string> assertion_labels;
assertion_labels.reserve(claim.created_assertions_size() +
claim.gathered_assertions_size());
for (const auto& assertions :
{claim.created_assertions(), claim.gathered_assertions()}) {
for (const HashedUri& hashed_uri : assertions) {
ValidateAssertion(
hashed_uri, manifest_path, manifest_type, redacted_assertion_paths,
assertion_uri_validator, references_validator, assertion_store_path,
claim, uri_resolver, assertion_parser_, manifest.mutable_assertions(),
assertion_labels, validation_tracker, assertion_counter);
}
}
// Validate that this claim's redactions are allowed.
ValidateRedactionsAllowed(claim, validation_tracker);
switch (manifest_type) {
case ManifestType::kUpdateManifest:
assertion_counter.ValidateUpdateManifestCounts(claim.label(),
validation_tracker);
break;
case ManifestType::kStandardManifest:
assertion_counter.ValidateStandardManifestCounts(claim.label(),
validation_tracker);
break;
}
assertion_counter.ValidateSoftBinding(claim.label(), validation_tracker);
// Validates any undeclared assertions.
ValidateUndeclaredAssertions(assertion_store_path, assertion_labels,
uri_resolver, validation_tracker);
if (!options_.skip_actions_assertion_validation_for_test) {
ActionsAssertionValidator actions_validator;
actions_validator.Validate(manifest, validation_tracker);
}
}
} // namespace credentio