commit c259190fe63592296abc3f611ecb840dec5cda3a
parent ed3c238aebfc31d4c19a140d563aa70fdac0b25e
Author: Gavin Peacock <gpeacock@adobe.com>
Date: Tue, 7 Mar 2023 15:06:50 -0800
Update Validation Status codes (#200)
* update validation_status codes to 1.2 spec
* Add ingredient validation status MANIFEST_INACCESSIBLE
Diffstat:
3 files changed, 76 insertions(+), 9 deletions(-)
diff --git a/sdk/src/ingredient.rs b/sdk/src/ingredient.rs
@@ -460,7 +460,10 @@ impl Ingredient {
#[cfg(feature = "file_io")]
fn from_file_impl(path: &Path, options: &dyn IngredientOptions) -> Result<Self> {
// these are declared inside this function in order to isolate them for wasm builds
- use crate::status_tracker::{log_item, DetailedStatusTracker, StatusTracker};
+ use crate::{
+ status_tracker::{log_item, DetailedStatusTracker, StatusTracker},
+ validation_status::MANIFEST_INACCESSIBLE,
+ };
#[cfg(feature = "diagnostics")]
let _t = crate::utils::time_it::TimeIt::new("Ingredient:from_file_with_options");
@@ -552,6 +555,13 @@ impl Ingredient {
| Err(Error::ProvenanceMissing)
| Err(Error::UnsupportedType) => {} // no claims but valid file
Err(Error::BadParam(desc)) if desc == *"unrecognized file type" => {}
+ Err(Error::RemoteManifestFetch(e)) => {
+ let mut vs = ValidationStatus::new(MANIFEST_INACCESSIBLE).set_explanation(e);
+ if let Some(url) = ingredient.provenance() {
+ vs = vs.set_url(url.to_string());
+ }
+ ingredient.validation_status = Some([vs].to_vec())
+ }
Err(e) => {
// we can ignore the error here because it should have a log entry corresponding to it
debug!("ingredient {:?}", e);
@@ -1080,6 +1090,19 @@ mod tests_file_io {
}
#[test]
+ #[cfg(feature = "fetch_remote_manifests")]
+ fn test_jpg_cloud_failure() {
+ let ap = fixture_path("cloudx.jpg");
+ let ingredient = Ingredient::from_file(ap).expect("from_file");
+ println!("ingredient = {ingredient}");
+ assert!(ingredient.validation_status().is_some());
+ assert_eq!(
+ ingredient.validation_status().unwrap()[0].code(),
+ validation_status::MANIFEST_INACCESSIBLE
+ );
+ }
+
+ #[test]
#[cfg(feature = "file_io")]
fn test_jpg_with_path() {
let ap = fixture_path("CIE-sig-CA.jpg");
diff --git a/sdk/src/validation_status.rs b/sdk/src/validation_status.rs
@@ -96,10 +96,11 @@ impl ValidationStatus {
match error {
e if e.starts_with("ClaimMissing") => CLAIM_MISSING,
e if e.starts_with("AssertionMissing") => ASSERTION_MISSING,
- e if e.starts_with("AssertionDecoding") => STATUS_ASSERTION_MALFORMED, // todo: no code for invalid assertion format
+ e if e.starts_with("AssertionDecoding") => ASSERTION_REQUIRED_MISSING,
e if e.starts_with("HashMismatch") => ASSERTION_DATAHASH_MATCH,
+ e if e.starts_with("RemoteManifestFetch") => MANIFEST_INACCESSIBLE,
e if e.starts_with("PrereleaseError") => STATUS_PRERELEASE,
- _ => STATUS_OTHER,
+ _ => GENERAL_ERROR,
}
}
@@ -108,10 +109,11 @@ impl ValidationStatus {
match error {
Error::ClaimMissing { .. } => CLAIM_MISSING,
Error::AssertionMissing { .. } => ASSERTION_MISSING,
- Error::AssertionDecoding(_code) => STATUS_ASSERTION_MALFORMED, // todo: no code for invalid assertion format
+ Error::AssertionDecoding(_code) => ASSERTION_REQUIRED_MISSING, //todo detect json/cbor errors
Error::HashMismatch(_) => ASSERTION_DATAHASH_MATCH,
+ Error::RemoteManifestFetch(_) => MANIFEST_INACCESSIBLE,
Error::PrereleaseError => STATUS_PRERELEASE,
- _ => STATUS_OTHER,
+ _ => GENERAL_ERROR,
}
}
@@ -263,6 +265,16 @@ pub const CLAIM_MULTIPLE: &str = "claim.multiple";
/// `ValidationStatus.url()` will point to a C2PA claim box.
pub const HARD_BINDINGS_MISSING: &str = "claim.hardBindings.missing";
+/// A required field is not present in the claim.
+///
+/// `ValidationStatus.url()` will point to a C2PA claim box.
+pub const CLAIM_REQUIRED_MISSING: &str = "claim.required.missing";
+
+/// The cbor of the claim is not valid.
+///
+/// `ValidationStatus.url()` will point to a C2PA claim box.
+pub const CLAIM_CBOR_INVALID: &str = "claim.cbor.invalid";
+
/// The hash of the the referenced ingredient claim in the manifest
/// does not match the corresponding hash in the ingredient's hashed
/// URI in the claim.
@@ -282,6 +294,14 @@ pub const CLAIM_SIGNATURE_MISSING: &str = "claimSignature.missing";
/// `ValidationStatus.url()` will point to a C2PA claim signature box.
pub const CLAIM_SIGNATURE_MISMATCH: &str = "claimSignature.mismatch";
+/// If a manifest was documented to exist in a remote location,
+/// but is not present there, or the location is not currently available
+/// (such as in an offline scenario),
+/// the `manifest.inaccessible` error code shall be used to report the situation.
+///
+/// `ValidationStatus.url()` URI reference to the C2PA Manifest that could not be accessed.
+pub const MANIFEST_INACCESSIBLE: &str = "manifest.inaccessible";
+
/// The manifest has more than one ingredient whose `relationship`
/// is `parentOf`.
///
@@ -370,6 +390,27 @@ pub const ASSERTION_NOT_REDACTED: &str = "assertion.notRedacted";
/// `ValidationStatus.url()` will point to a C2PA claim box.
pub const ASSERTION_SELF_REDACTED: &str = "assertion.selfRedacted";
+/// A required field is not present in an assertion.
+///
+/// `ValidationStatus.url()` will point to a C2PA assertion.
+pub const ASSERTION_REQUIRED_MISSING: &str = "assertion.required.missing";
+
+/// The JSON(-LD) of an assertion is not valid.
+///
+/// `ValidationStatus.url()` will point to a C2PA assertion.
+pub const ASSERTION_JSON_INVALID: &str = "assertion.json.invalid";
+
+/// The cbor of an assertion is not valid.
+///
+/// `ValidationStatus.url()` will point to a C2PA assertion.
+pub const ASSERTION_CBOR_INVALID: &str = "assertion.cbor.invalid";
+
+/// An action that requires an associated ingredient either does not have one
+/// or the one specified cannot be located
+///
+/// `ValidationStatus.url()` will point to a C2PA assertion.
+pub const ACTION_ASSERTION_INGREDIENT_MISMATCH: &str = "assertion.action.ingredientMismatch";
+
/// An `action` assertion was redacted when the ingredient's
/// claim was created.
///
@@ -391,13 +432,13 @@ pub const ASSERTION_BMFFHASH_MISMATCH: &str = "assertion.bmffHash.mismatch";
/// A hard binding assertion is in a cloud data assertion.
///
/// `ValidationStatus.url()` will point to a C2PA assertion.
-pub const ASSERTION_CLOUDDATA_HARD_BINDING: &str = "assertion.clouddata.hardBinding";
+pub const ASSERTION_CLOUD_DATA_HARD_BINDING: &str = "assertion.cloud-data.hardBinding";
/// An update manifest contains a cloud data assertion referencing
/// an actions assertion.
///
/// `ValidationStatus.url()` will point to a C2PA assertion.
-pub const ASSERTION_CLOUDDATA_ACTIONS: &str = "assertion.clouddata.actions";
+pub const ASSERTION_CLOUD_DATA_ACTIONS: &str = "assertion.cloud-data.actions";
/// The value of an `alg` header, or other header that specifies an
/// algorithm used to compute the value of another field, is unknown
@@ -406,11 +447,14 @@ pub const ASSERTION_CLOUDDATA_ACTIONS: &str = "assertion.clouddata.actions";
/// `ValidationStatus.url()` will point to a C2PA claim box or C2PA assertion.
pub const ALGORITHM_UNSUPPORTED: &str = "algorithm.unsupported";
+/// A value to be used when there was an error not specifically listed here.
+///
+/// `ValidationStatus.url()` will point to a C2PA claim box or C2PA assertion.
+pub const GENERAL_ERROR: &str = "general.error";
+
// -- unofficial status codes --
-pub(crate) const STATUS_OTHER: &str = "com.adobe.other";
pub(crate) const STATUS_PRERELEASE: &str = "com.adobe.prerelease";
-pub(crate) const STATUS_ASSERTION_MALFORMED: &str = "com.adobe.assertion.malformed";
/// Returns `true` if the status code is a known C2PA success status code.
///
diff --git a/sdk/tests/fixtures/cloudx.jpg b/sdk/tests/fixtures/cloudx.jpg
Binary files differ.