c2pa-rs

A fork of https://github.com/contentauth/c2pa-rs/
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/c2pa-rs.git
Log | Files | Refs | README

commit dcdf70d3a1d47a135cad88572ecb10c065896ff7
parent 4a783b5d4e322c7213d4ab26a7d00283dad40087
Author: Gavin  Peacock <gpeacock@adobe.com>
Date:   Fri, 26 Aug 2022 09:19:58 -0700

Convert status_log error val to a string so that we can return full errors (#121)

* Convert status_tracker err_val to string
* Use set_error in load_from_asset and memory
* Simplify and return all errors
Diffstat:
Msdk/src/status_tracker.rs | 23+++++++++++++++++------
Msdk/src/store.rs | 93+++++++++++++++++++++++++++----------------------------------------------------
Msdk/src/validation_status.rs | 16++++++++++++++--
3 files changed, 63 insertions(+), 69 deletions(-)

diff --git a/sdk/src/status_tracker.rs b/sdk/src/status_tracker.rs @@ -22,7 +22,7 @@ pub struct LogItem { pub function: String, // Function where failure occurred pub line: String, // Line number for error pub description: String, // Description of the failure - pub err_val: Option<Error>, // Actual error code + err_val: Option<String>, // Actual error code as string pub validation_status: Option<String>, // C2PA code if available } @@ -42,12 +42,25 @@ impl LogItem { // add an error value pub fn error(self, err: Error) -> Self { LogItem { - err_val: Some(err), + err_val: Some(format!("{:?}", err)), ..self } } // add an error value + pub fn set_error(self, err: &Error) -> Self { + LogItem { + err_val: Some(format!("{:?}", err)), + ..self + } + } + + /// returns a reference to the error string if there is one + pub fn error_str(&self) -> Option<&str> { + self.err_val.as_deref() + } + + // add an error value pub fn validation_status(self, status: &str) -> Self { LogItem { validation_status: Some(status.to_string()), @@ -191,14 +204,12 @@ pub fn report_has_status(report: &[LogItem], val: &str) -> bool { } /// Check to see if report contains a specific error -/// Note: Only the out error object is matched for nested errors like -/// "Error::InvalidClaim(InvalidClaimError::ClaimSignatureDescriptionBoxInvalid)". -/// In this case any "InvalidClaim" would match #[allow(dead_code)] // in case we make use of these or export this pub fn report_has_err(report: &[LogItem], err: Error) -> bool { + let err_type = format!("{:?}", &err); report.iter().any(|vi| { if let Some(e) = &vi.err_val { - std::mem::discriminant(e) == std::mem::discriminant(&err) + e == &err_type } else { false } diff --git a/sdk/src/store.rs b/sdk/src/store.rs @@ -47,7 +47,7 @@ use crate::{ error::{Error, Result}, hash_utils::{hash_by_alg, vec_compare, verify_by_alg}, jumbf::{self, boxes::*}, - jumbf_io::{get_cailoader_handler, load_jumbf_from_memory}, + jumbf_io::load_jumbf_from_memory, status_tracker::{log_item, OneShotStatusTracker, StatusTracker}, validation_status, ManifestStoreReport, }; @@ -1906,19 +1906,10 @@ impl Store { Ok(store) }) .map_err(|e| { - let err = match e { - Error::PrereleaseError => Error::PrereleaseError, - Error::JumbfNotFound => Error::JumbfNotFound, - Error::IoError(_) => { - Error::FileNotFound(asset_path.to_string_lossy().to_string()) - } - Error::UnsupportedType => Error::UnsupportedType, - Error::RemoteManifestFetch(_) => Error::RemoteManifestFetch("".to_string()), - _ => Error::LogStop, - }; - let log_item = log_item!("asset", "error loading file", "load_from_asset").error(e); - validation_log.log_silent(log_item); - err + validation_log.log_silent( + log_item!("asset", "error loading file", "load_from_asset").set_error(&e), + ); + e }) } @@ -1926,30 +1917,14 @@ impl Store { asset_type: &str, data: &[u8], validation_log: &mut impl StatusTracker, - ) -> Result<(Store, Option<String>)> { - let cai_loader = get_cailoader_handler(asset_type).ok_or(Error::UnsupportedType)?; - - let mut buf_reader = Cursor::new(data); - - // check for xmp, error if not present - let xmp = cai_loader.read_xmp(&mut buf_reader); - + ) -> Result<Store> { // load jumbf if available - Self::load_cai_from_memory(asset_type, data, validation_log) - .map(|store| (store, xmp)) - .map_err(|e| { - let err = match e { - Error::PrereleaseError => Error::PrereleaseError, - Error::JumbfNotFound => Error::JumbfNotFound, - Error::UnsupportedType => Error::UnsupportedType, - Error::RemoteManifestFetch(_) => Error::RemoteManifestFetch("".to_string()), - _ => Error::LogStop, - }; - let log_item = - log_item!("asset", "error loading asset", "get_store_from_memory").error(e); - validation_log.log_silent(log_item); - err - }) + Self::load_cai_from_memory(asset_type, data, validation_log).map_err(|e| { + validation_log.log_silent( + log_item!("asset", "error loading asset", "get_store_from_memory").set_error(&e), + ); + e + }) } /// Returns embedded remote manifest URL if available @@ -1981,17 +1956,15 @@ impl Store { verify: bool, validation_log: &mut impl StatusTracker, ) -> Result<Store> { - Store::get_store_from_memory(asset_type, data, validation_log).and_then( - |(store, _xmp_opt)| { - // verify the store - if verify { - // verify store and claims - Store::verify_store(&store, &ClaimAssetData::ByteData(data), validation_log)?; - } + Store::get_store_from_memory(asset_type, data, validation_log).and_then(|store| { + // verify the store + if verify { + // verify store and claims + Store::verify_store(&store, &ClaimAssetData::ByteData(data), validation_log)?; + } - Ok(store) - }, - ) + Ok(store) + }) } /// Load Store from a in-memory asset asychronously validating @@ -2005,7 +1978,7 @@ impl Store { verify: bool, validation_log: &mut impl StatusTracker, ) -> Result<Store> { - let (store, _xmp_opt) = Store::get_store_from_memory(asset_type, data, validation_log)?; + let store = Store::get_store_from_memory(asset_type, data, validation_log)?; // verify the store if verify { @@ -2619,10 +2592,7 @@ pub mod tests { ); assert!(!report.get_log().is_empty()); let errors = report_split_errors(report.get_log_mut()); - assert!(matches!( - errors[0].err_val.as_ref(), - Some(Error::IoError(_err)) - )); + assert!(errors[0].error_str().unwrap().starts_with("IoError")); } #[test] @@ -2638,10 +2608,7 @@ pub mod tests { ); assert!(!report.get_log().is_empty()); let errors = report_split_errors(report.get_log_mut()); - assert!(matches!( - errors[0].err_val.as_ref(), - Some(Error::PrereleaseError) - )); + assert!(errors[0].error_str().unwrap().starts_with("Prerelease")); } #[test] @@ -2765,10 +2732,14 @@ pub mod tests { // modify a required field label in the claim - causes failure to read claim from cbor let report = patch_and_report("C.jpg", b"claim_generator", b"claim_generatur"); assert!(!report.get_log().is_empty()); - assert!(matches!( - report.get_log()[0].err_val, - Some(Error::ClaimDecoding) - )); + assert!(report.get_log()[0] + .error_str() + .unwrap() + .starts_with("ClaimDecoding")) + // assert!(matches!( + // report.get_log()[0].err_val, + // Some(Error::ClaimDecoding) + // )); //assert_eq!(report[0].validation_status.as_deref(), Some(???)); // what validation status should we have for this? } @@ -2783,7 +2754,7 @@ pub mod tests { assert!(!report.get_log().is_empty()); let errors = report_split_errors(report.get_log_mut()); - assert!(matches!(errors[0].err_val, Some(Error::HashMismatch(_)))); + assert!(errors[0].error_str().unwrap().starts_with("HashMismatch")); assert_eq!( errors[0].validation_status.as_deref(), Some(validation_status::ASSERTION_DATAHASH_MISMATCH) diff --git a/sdk/src/validation_status.rs b/sdk/src/validation_status.rs @@ -92,6 +92,18 @@ impl ValidationStatus { } // Maps errors into validation_status codes. + fn code_from_error_str(error: &str) -> &str { + 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("HashMismatch") => ASSERTION_DATAHASH_MATCH, + e if e.starts_with("PrereleaseError") => STATUS_PRERELEASE, + _ => STATUS_OTHER, + } + } + + // Maps errors into validation_status codes. fn code_from_error(error: &Error) -> &str { match error { Error::ClaimMissing { .. } => CLAIM_MISSING, @@ -121,8 +133,8 @@ impl ValidationStatus { ), // If we don't have a validation_status, then make one from the err_val // using the description plus error text explanation. - None => item.err_val.as_ref().map(|e| { - let code = Self::code_from_error(e); + None => item.error_str().as_ref().map(|e| { + let code = Self::code_from_error_str(e); Self::new(code.to_string()) .set_url(item.label.to_string()) .set_explanation(format!("{}: {}", item.description, e))