-
Notifications
You must be signed in to change notification settings - Fork 111
feat: add Unknown validation state #1543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 12 commits
a6d3823
75bc108
10f0679
b839a50
3fc7e91
3f72571
628683f
e6351cf
307d865
38fdd24
2f8d88f
3c4ba9a
f25723b
aadcb02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,8 @@ | |
| // specific language governing permissions and limitations under | ||
| // each license. | ||
|
|
||
| use std::collections::HashSet; | ||
|
|
||
| #[cfg(feature = "json_schema")] | ||
| use schemars::JsonSchema; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
@@ -21,30 +23,48 @@ use crate::{ | |
| jumbf::labels::manifest_label_from_uri, | ||
| status_tracker::{LogKind, StatusTracker}, | ||
| store::Store, | ||
| validation_status::{log_kind, ValidationStatus}, | ||
| validation_status::{self, log_kind, ValidationStatus}, | ||
| }; | ||
|
|
||
| /// Represents the levels of assurance a manifest store achives when evaluated against the C2PA | ||
| /// specifications structural, cryptographic, and trust requirements. | ||
| /// | ||
| /// See [§14.3. Validation states]. | ||
| /// | ||
| /// [§14.3. Validation states]: https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_validation_states | ||
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
| #[cfg_attr(feature = "json_schema", derive(JsonSchema))] | ||
| /// Indicates if the manifest store is valid and trusted. | ||
| /// | ||
| /// The Trusted state implies the manifest store is valid and the active signature is trusted. | ||
| pub enum ValidationState { | ||
| /// Errors were found in the manifest store. | ||
| /// Validation is disabled in the SDK and the [ValidationState] is unable to be determined. | ||
| Unknown, | ||
| /// The manifest store fails to meet [ValidationState::WellFormed] requirements, meaning it cannot | ||
| /// even be parsed or its basic structure is non-compliant. | ||
| Invalid, | ||
| /// No errors were found in validation, but the active signature is not trusted. | ||
| /// The manifest store is well-formed and the cryptographic integrity checks succeed. | ||
| /// | ||
| /// See [§14.3.5. Valid Manifest]. | ||
| /// | ||
| /// [§14.3.5. Valid Manifest]: https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_valid_manifest | ||
| Valid, | ||
| /// The manifest store is valid and the active signature is trusted. | ||
| /// The manifest store is valid and signed by a certificate that chains up to a trusted root or known | ||
| /// authority in the trust list. | ||
| /// | ||
| /// See [§14.3.6. Trusted Manifest]. | ||
| /// | ||
| /// [§14.3.6. Trusted Manifest]: https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_trusted_manifest | ||
| Trusted, | ||
| } | ||
|
|
||
| /// Contains a set of success, informational, and failure validation status codes. | ||
| #[derive(Clone, Serialize, Default, Deserialize, Debug, PartialEq, Eq)] | ||
| #[cfg_attr(feature = "json_schema", derive(JsonSchema))] | ||
| /// Contains a set of success, informational, and failure validation status codes. | ||
| pub struct StatusCodes { | ||
| pub success: Vec<ValidationStatus>, // an array of validation success codes. May be empty. | ||
| pub informational: Vec<ValidationStatus>, // an array of validation informational codes. May be empty. | ||
| pub failure: Vec<ValidationStatus>, // an array of validation failure codes. May be empty. | ||
| /// An array of validation success codes. May be empty. | ||
| pub success: Vec<ValidationStatus>, | ||
| /// An array of validation informational codes. May be empty. | ||
| pub informational: Vec<ValidationStatus>, | ||
| // An array of validation failure codes. May be empty. | ||
| pub failure: Vec<ValidationStatus>, | ||
| } | ||
|
|
||
| impl StatusCodes { | ||
|
|
@@ -85,18 +105,22 @@ impl StatusCodes { | |
| } | ||
| } | ||
|
|
||
| #[derive(Clone, Serialize, Default, Deserialize, Debug, PartialEq, Eq)] | ||
| #[cfg_attr(feature = "json_schema", derive(JsonSchema))] | ||
| /// A map of validation results for a manifest store. | ||
| /// | ||
| /// The map contains the validation results for the active manifest and any ingredient deltas. | ||
| /// It is normal for there to be many | ||
| #[derive(Clone, Serialize, Default, Deserialize, Debug, PartialEq, Eq)] | ||
| #[cfg_attr(feature = "json_schema", derive(JsonSchema))] | ||
| pub struct ValidationResults { | ||
| /// Validation status codes for the ingredient's active manifest. Present if ingredient is a C2PA | ||
| /// asset. Not present if the ingredient is not a C2PA asset. | ||
| #[serde(rename = "activeManifest", skip_serializing_if = "Option::is_none")] | ||
| active_manifest: Option<StatusCodes>, // Validation status codes for the ingredient's active manifest. Present if ingredient is a C2PA asset. Not present if the ingredient is not a C2PA asset. | ||
| active_manifest: Option<StatusCodes>, | ||
|
|
||
| /// List of any changes/deltas between the current and previous validation results for each ingredient's | ||
| /// manifest. Present if the the ingredient is a C2PA asset. | ||
| #[serde(rename = "ingredientDeltas", skip_serializing_if = "Option::is_none")] | ||
| ingredient_deltas: Option<Vec<IngredientDeltaValidationResult>>, // List of any changes/deltas between the current and previous validation results for each ingredient's manifest. Present if the the ingredient is a C2PA asset. | ||
| ingredient_deltas: Option<Vec<IngredientDeltaValidationResult>>, | ||
| } | ||
|
|
||
| impl ValidationResults { | ||
|
|
@@ -180,29 +204,47 @@ impl ValidationResults { | |
| } | ||
|
|
||
| /// Returns the [ValidationState] of the manifest store based on the validation results. | ||
| /// | ||
| /// See [§14.3. Validation states]. | ||
| /// | ||
| /// [§14.3. Validation states]: https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_validation_states | ||
| pub fn validation_state(&self) -> ValidationState { | ||
| let mut is_trusted = true; // Assume the state is trusted until proven otherwise | ||
| if let Some(active_manifest) = self.active_manifest.as_ref() { | ||
| if !active_manifest.failure().is_empty() { | ||
| return ValidationState::Invalid; | ||
| } | ||
| // There must be a trusted credential in the active manifest for the state to be trusted | ||
| is_trusted = active_manifest.success().iter().any(|status| { | ||
| status.code() == crate::validation_status::SIGNING_CREDENTIAL_TRUSTED | ||
| 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 | ||
| .iter() | ||
| .any(|idv| !idv.validation_deltas().failure().is_empty()) | ||
| }); | ||
| } | ||
| if let Some(ingredient_deltas) = self.ingredient_deltas.as_ref() { | ||
| for idv in ingredient_deltas.iter() { | ||
| if !idv.validation_deltas().failure().is_empty() { | ||
| return ValidationState::Invalid; | ||
| } | ||
|
|
||
| // 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; | ||
|
|
||
| // 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; | ||
|
|
||
| if is_trusted { | ||
| return ValidationState::Trusted; | ||
| } else if is_valid { | ||
| return ValidationState::Valid; | ||
| } | ||
| } | ||
| if is_trusted { | ||
| ValidationState::Trusted | ||
| } else { | ||
| ValidationState::Valid | ||
| // REVIEW-NOTE: is this the best way to detect that it wasn't validated? should we also check if success/failure is empty if there | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work? I'm not sure if there will always be no active manifest in validation_results if we did not validate.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think it's a safe assumption that if there's no active_manifest then it hasn't been validated, or at the very least it couldn't be determined. The only problem is that we populate the validation results on a |
||
| // is an active manifest? | ||
| return ValidationState::Unknown; | ||
| } | ||
|
|
||
| ValidationState::Invalid | ||
| } | ||
|
|
||
| /// Returns a list of all validation errors in [ValidationResults]. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -171,4 +171,4 @@ | |
| ] | ||
| }, | ||
| "validation_state": "Invalid" | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.