diff --git a/sdk/src/validation_results.rs b/sdk/src/validation_results.rs index f66a341fa..341f47e0e 100644 --- a/sdk/src/validation_results.rs +++ b/sdk/src/validation_results.rs @@ -11,8 +11,6 @@ // specific language governing permissions and limitations under // each license. -use std::collections::HashSet; - #[cfg(feature = "json_schema")] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -210,28 +208,55 @@ impl ValidationResults { /// [ยง14.3. Validation states]: https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_validation_states pub fn validation_state(&self) -> ValidationState { if let Some(active_manifest) = self.active_manifest.as_ref() { - let success_codes: HashSet<&str> = active_manifest - .success() - .iter() - .map(|status| status.code()) - .collect(); - let failure_codes = active_manifest.failure(); - let ingredient_failure = self.ingredient_deltas.as_ref().is_some_and(|deltas| { - deltas + fn is_manifest_valid( + success_codes: &[ValidationStatus], + failure_codes: &[ValidationStatus], + ) -> bool { + success_codes .iter() - .any(|idv| !idv.validation_deltas().failure().is_empty()) - }); + .any(|status| status.code() == validation_status::CLAIM_SIGNATURE_VALIDATED) + && success_codes.iter().any(|status| { + status.code() == validation_status::CLAIM_SIGNATURE_INSIDE_VALIDITY + }) + && (failure_codes.is_empty() + || failure_codes.iter().all(|status| { + status.code() == validation_status::SIGNING_CREDENTIAL_UNTRUSTED + })) + } + + fn is_manifest_trusted( + success_codes: &[ValidationStatus], + failure_codes: &[ValidationStatus], + ) -> bool { + success_codes + .iter() + .any(|status| status.code() == validation_status::SIGNING_CREDENTIAL_TRUSTED) + && failure_codes.is_empty() + } // https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_valid_manifest - let is_valid = success_codes.contains(validation_status::CLAIM_SIGNATURE_VALIDATED) - && success_codes.contains(validation_status::CLAIM_SIGNATURE_INSIDE_VALIDITY) - && failure_codes.is_empty() - && !ingredient_failure; + let is_valid = is_manifest_valid(active_manifest.success(), active_manifest.failure()) + && self.ingredient_deltas.as_ref().iter().all(|deltas| { + deltas.iter().all(|idv| { + let deltas = idv.validation_deltas(); + deltas.informational().iter().any(|status| { + status.code() == validation_status::INGREDIENT_PROVENANCE_UNKNOWN + }) || is_manifest_valid(deltas.success(), deltas.failure()) + }) + }); // https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_trusted_manifest - let is_trusted = success_codes.contains(validation_status::SIGNING_CREDENTIAL_TRUSTED) - && failure_codes.is_empty() - && is_valid; + let is_trusted = + is_manifest_trusted(active_manifest.success(), active_manifest.failure()) + && self.ingredient_deltas.as_ref().iter().all(|deltas| { + deltas.iter().all(|idv| { + let deltas = idv.validation_deltas(); + deltas.informational().iter().any(|status| { + status.code() == validation_status::INGREDIENT_PROVENANCE_UNKNOWN + }) || is_manifest_trusted(deltas.success(), deltas.failure()) + }) + }) + && is_valid; if is_trusted { return ValidationState::Trusted;