From 4b4806c68a1bcb1874963b88771ab94ea1aaff22 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 26 Feb 2025 08:54:52 -0800 Subject: [PATCH] WIP on error reporting --- .../src/identity_assertion/assertion.rs | 22 ++++- .../src/tests/identity_assertion/assertion.rs | 99 +++++++++++++++++++ .../src/tests/identity_assertion/mod.rs | 1 + sdk/src/error.rs | 3 + 4 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 cawg_identity/src/tests/identity_assertion/assertion.rs diff --git a/cawg_identity/src/identity_assertion/assertion.rs b/cawg_identity/src/identity_assertion/assertion.rs index bb7e3ac8b..d754ba1be 100644 --- a/cawg_identity/src/identity_assertion/assertion.rs +++ b/cawg_identity/src/identity_assertion/assertion.rs @@ -17,7 +17,7 @@ use std::{ }; use c2pa::{Manifest, Reader}; -use c2pa_status_tracker::StatusTracker; +use c2pa_status_tracker::{log_item, StatusTracker}; use serde::{Deserialize, Serialize}; use serde_bytes::ByteBuf; @@ -66,13 +66,29 @@ impl IdentityAssertion { /// Aside from CBOR parsing, no further validation is performed. pub fn from_manifest<'a>( manifest: &'a Manifest, - _status_tracker: &'a mut StatusTracker, + status_tracker: &'a mut StatusTracker, ) -> impl Iterator> + use<'a> { manifest .assertions() .iter() .filter(|a| a.label().starts_with("cawg.identity")) - .map(|a| a.to_assertion()) + .map(|a| (a.label().to_owned(), a.to_assertion())) + .inspect(|(label, r)| { + if let Err(err) = r { + // TO DO: a.label() is probably wrong (not a full JUMBF URI) + log_item!( + label.clone(), + "invalid CBOR", + "IdentityAssertion::from_manifest" + ) + .validation_status("cawg.identity.cbor.invalid") + .failure_no_throw( + status_tracker, + c2pa::Error::AssertionSpecificError(err.to_string()), + ); + } + }) + .map(move |(_label, r)| r) } /// Create a summary report from this `IdentityAssertion`. diff --git a/cawg_identity/src/tests/identity_assertion/assertion.rs b/cawg_identity/src/tests/identity_assertion/assertion.rs new file mode 100644 index 000000000..d2acc93df --- /dev/null +++ b/cawg_identity/src/tests/identity_assertion/assertion.rs @@ -0,0 +1,99 @@ +// Copyright 2025 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use std::io::{Cursor, Seek}; + +use c2pa::{Builder, Reader, SigningAlg}; +use c2pa_crypto::raw_signature; +use c2pa_status_tracker::StatusTracker; +use serde_json::json; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::wasm_bindgen_test; + +use crate::{ + builder::{AsyncIdentityAssertionBuilder, AsyncIdentityAssertionSigner}, + tests::fixtures::cert_chain_and_private_key_for_alg, + x509::{X509CredentialHolder, X509SignatureVerifier}, + IdentityAssertion, +}; + +const TEST_IMAGE: &[u8] = include_bytes!("../../../sdk/tests/fixtures/CA.jpg"); +const TEST_THUMBNAIL: &[u8] = include_bytes!("../../../sdk/tests/fixtures/thumbnail.jpg"); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn not_cbor() { + let format = "image/jpeg"; + let mut source = Cursor::new(TEST_IMAGE); + let mut dest = Cursor::new(Vec::new()); + + let mut builder = Builder::from_json(&manifest_json()).unwrap(); + builder + .add_ingredient_from_stream(parent_json(), format, &mut source) + .unwrap(); + + builder + .add_resource("thumbnail.jpg", Cursor::new(TEST_THUMBNAIL)) + .unwrap(); + + let mut c2pa_signer = AsyncIdentityAssertionSigner::from_test_credentials(SigningAlg::Ps256); + + let (cawg_cert_chain, cawg_private_key) = + cert_chain_and_private_key_for_alg(SigningAlg::Ed25519); + + let cawg_raw_signer = raw_signature::async_signer_from_cert_chain_and_private_key( + &cawg_cert_chain, + &cawg_private_key, + SigningAlg::Ed25519, + None, + ) + .unwrap(); + + let x509_holder = X509CredentialHolder::from_async_raw_signer(cawg_raw_signer); + let iab = AsyncIdentityAssertionBuilder::for_credential_holder(x509_holder); + c2pa_signer.add_identity_assertion(iab); + + builder + .sign_async(&c2pa_signer, format, &mut source, &mut dest) + .await + .unwrap(); + + // Read back the Manifest that was generated. + dest.rewind().unwrap(); + + let manifest_store = Reader::from_stream(format, &mut dest).unwrap(); + assert_eq!(manifest_store.validation_status(), None); + + let manifest = manifest_store.active_manifest().unwrap(); + let mut st = StatusTracker::default(); + let mut ia_iter = IdentityAssertion::from_manifest(manifest, &mut st); + + // Should find exactly one identity assertion. + let ia = ia_iter.next().unwrap().unwrap(); + assert!(ia_iter.next().is_none()); + drop(ia_iter); + + // And that identity assertion should be valid for this manifest. + let x509_verifier = X509SignatureVerifier {}; + let sig_info = ia + .validate(manifest, &mut st, &x509_verifier) + .await + .unwrap(); + + let cert_info = &sig_info.cert_info; + assert_eq!(cert_info.alg.unwrap(), SigningAlg::Ed25519); + assert_eq!( + cert_info.issuer_org.as_ref().unwrap(), + "C2PA Test Signing Cert" + ); +} diff --git a/cawg_identity/src/tests/identity_assertion/mod.rs b/cawg_identity/src/tests/identity_assertion/mod.rs index f7cceb843..0fe40a3f8 100644 --- a/cawg_identity/src/tests/identity_assertion/mod.rs +++ b/cawg_identity/src/tests/identity_assertion/mod.rs @@ -11,4 +11,5 @@ // specific language governing permissions and limitations under // each license. +mod assertion; mod signer_payload; diff --git a/sdk/src/error.rs b/sdk/src/error.rs index 49d4892ff..eccaa49d8 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -46,6 +46,9 @@ pub enum Error { #[error("could not find the assertion to redact")] AssertionRedactionNotFound, + #[error("assertion-specific error: {0}")] + AssertionSpecificError(String), + #[error("bad parameter: {0}")] BadParam(String),