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 cf041601e27ce7aed2ddc805744e60d28320c502
parent 2723117d95d8e234ea20d1425c1be6fba2046400
Author: Gavin  Peacock <gpeacock@adobe.com>
Date:   Thu, 13 Jul 2023 13:47:08 -0700

Fix for #195 make_test_images missing ingredient references (#254)

* remove e-dat-ca test image
relies on XMP that is no longer written, use XCA instead.
* Remove deduplication logic  when adding ingredients.
This caused some errors and is better left to the calling application to optimize
* test image actions can refer to the same ingredient
* update test images
* add source type and softwareagent to c.jpg
* update all generated unit test images
* remove tests for provenance that are no longer valid
* fix xmp modification test
* removes e-dat-ca test image
* update readme and refresh images
Diffstat:
MREADME.md | 3+++
Mmake_test_images/src/make_test_images.rs | 48++++++++++++++++++++++++++++++++++--------------
Mmake_test_images/tests.json | 1-
Msdk/src/asset_handlers/bmff_io.rs | 2+-
Msdk/src/ingredient.rs | 5-----
Msdk/src/manifest.rs | 36+++++++-----------------------------
Msdk/src/store.rs | 3++-
Msdk/tests/fixtures/C.jpg | 0
Msdk/tests/fixtures/CA.jpg | 0
Msdk/tests/fixtures/CIE-sig-CA.jpg | 0
Msdk/tests/fixtures/E-sig-CA.jpg | 0
Msdk/tests/fixtures/XCA.jpg | 0
12 files changed, 47 insertions(+), 51 deletions(-)

diff --git a/README.md b/README.md @@ -98,6 +98,9 @@ Refer to the [CHANGELOG](https://github.com/contentauth/c2pa-rs/blob/main/CHANGE _14 July 2023_ * (important!) the add_thumbnails feature is no longer tied to file_io, so you will need to specify it or thumbnails will not be generated. * DataHash and BoxHash SDK support (generates a signed manifest ready to write into a file without writing to the file) +* The SDK will no longer remove duplicate ingredients based on hash +* make_test_images updated to fix issue 195, actions without required ingredients +* updated the test fixtures generated by make_test_images * Expose CAIRead and CAIWrite traits required by some SDK calls. * Bug fix for certain BMFF formats (AVIF) that causes images to be unreadable diff --git a/make_test_images/src/make_test_images.rs b/make_test_images/src/make_test_images.rs @@ -13,6 +13,7 @@ //! Constructs a set of test images using a configuration script use std::{ + collections::HashMap, fs, path::{Path, PathBuf}, }; @@ -199,6 +200,8 @@ impl MakeTestImages { } let generator = format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + let software_agent = format!("{} {}", "Make Test Images", env!("CARGO_PKG_VERSION")); + let mut manifest = Manifest::new(generator); manifest.set_vendor("contentauth".to_owned()); // needed for generating error cases below @@ -209,6 +212,9 @@ impl MakeTestImages { manifest.add_assertion(&creative_work)?; } + // keep track of ingredient instances so we don't duplicate them + let mut ingredient_table = HashMap::new(); + // process parent first let mut img = match src { Some(src) => { @@ -216,11 +222,16 @@ impl MakeTestImages { let parent = Ingredient::from_file_with_options(src_path, &ImageOptions::new())?; + let instance_id = parent.instance_id().to_string(); + actions = actions.add_action( Action::new(c2pa_action::OPENED).set_instance_id(parent.instance_id()), ); manifest.set_parent(parent)?; + // keep track of all ingredients we add via the instance Id + ingredient_table.insert(src, instance_id); + // load the image for editing let mut img = image::open(src_path).context(format!("opening parent {src_path:?}"))?; @@ -244,13 +255,14 @@ impl MakeTestImages { *pixel = image::Rgb([r, 100, b]); } } - actions = actions - .add_action(Action::new(c2pa_action::CREATED)) - .add_action( - Action::new(c2pa_action::DRAWING) - .set_parameter("name".to_owned(), "gradient")?, - ); - + actions = actions.add_action( + Action::new(c2pa_action::CREATED) + .set_source_type( + "http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia", + ) + .set_software_agent(software_agent.as_str()) + .set_parameter("name".to_owned(), "gradient")?, + ); img } }; @@ -274,13 +286,21 @@ impl MakeTestImages { let img_small = img_ingredient.thumbnail(width, height); image::imageops::overlay(&mut img, &img_small, x, 0); - // create and add the ingredient - let ingredient = - Ingredient::from_file_with_options(ing_path, &ImageOptions::new())?; - actions = actions.add_action( - Action::new(c2pa_action::PLACED).set_instance_id(ingredient.instance_id()), - ); - manifest.add_ingredient(ingredient); + // if we have already created an ingredient, get the instanceId, otherwise create a new one + let instance_id = match ingredient_table.get(ing.as_str()) { + Some(id) => id.to_owned(), + None => { + let ingredient = + Ingredient::from_file_with_options(ing_path, &ImageOptions::new())?; + let instance_id = ingredient.instance_id().to_string(); + ingredient_table.insert(ing, instance_id.clone()); + manifest.add_ingredient(ingredient); + instance_id + } + }; + + actions = actions + .add_action(Action::new(c2pa_action::PLACED).set_instance_id(instance_id)); x += width as i64; } diff --git a/make_test_images/tests.json b/make_test_images/tests.json @@ -20,7 +20,6 @@ { "op": "make", "ingredients": ["CA","CA","CA"], "output": "CICACACA" }, { "op": "ogp", "parent": "CA", "output": "XCA" }, { "op": "ogp", "parent": "CI", "output": "XCI" }, - { "op": "dat", "parent": "CA", "output": "E-dat-CA" }, { "op": "sig", "parent": "CA", "output": "E-sig-CA" }, { "op": "uri", "parent": "CA", "output": "E-uri-CA" }, { "op": "clm", "parent": "CAICAI", "output": "E-clm-CAICAI" }, diff --git a/sdk/src/asset_handlers/bmff_io.rs b/sdk/src/asset_handlers/bmff_io.rs @@ -1660,7 +1660,7 @@ pub mod tests { use crate::utils::test::{fixture_path, temp_dir_path}; #[cfg(not(target_arch = "wasm32"))] - #[cfg(file_io)] + #[cfg(feature = "file_io")] #[test] fn test_read_mp4() { use crate::{ diff --git a/sdk/src/ingredient.rs b/sdk/src/ingredient.rs @@ -1372,7 +1372,6 @@ mod tests { println!("ingredient = {ingredient}"); assert_eq!(&ingredient.title, title); assert_eq!(ingredient.format(), format); - assert!(ingredient.provenance().is_some()); assert!(ingredient.manifest_data().is_some()); assert!(ingredient.metadata().is_none()); #[cfg(target_arch = "wasm32")] @@ -1396,7 +1395,6 @@ mod tests { println!("ingredient = {ingredient}"); assert_eq!(&ingredient.title, title); assert_eq!(ingredient.format(), format); - assert!(ingredient.provenance().is_some()); assert!(ingredient.manifest_data().is_some()); assert!(ingredient.metadata().is_none()); assert!(ingredient.validation_status().is_none()); @@ -1561,7 +1559,6 @@ mod tests_file_io { assert_eq!(&ingredient.title, MANIFEST_JPEG); assert_eq!(ingredient.format(), "image/jpeg"); assert!(ingredient.thumbnail().is_some()); // we don't generate this thumbnail - assert!(ingredient.provenance().is_some()); assert!(ingredient.manifest_data().is_some()); assert!(ingredient.metadata().is_none()); } @@ -1616,7 +1613,6 @@ mod tests_file_io { assert_eq!(ingredient.format(), "image/jpeg"); assert!(ingredient.hash().is_some()); assert!(ingredient.thumbnail().is_some()); // always generated - assert!(ingredient.provenance().is_some()); assert!(ingredient.manifest_data().is_some()); assert!(ingredient.metadata().is_none()); } @@ -1646,7 +1642,6 @@ mod tests_file_io { assert_eq!(ingredient.title(), BAD_SIGNATURE_JPEG); assert_eq!(ingredient.format(), "image/jpeg"); test_thumbnail(&ingredient, "image/jpeg"); - assert!(ingredient.provenance().is_some()); assert!(ingredient.manifest_data().is_some()); assert!(ingredient.validation_status().is_some()); assert!(ingredient diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs @@ -15,7 +15,7 @@ use std::{borrow::Cow, collections::HashMap, io::Cursor}; #[cfg(feature = "file_io")] use std::{fs::create_dir_all, path::Path}; -use log::{debug, error, warn}; +use log::{debug, error}; #[cfg(feature = "json_schema")] use schemars::JsonSchema; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -303,38 +303,15 @@ impl Manifest { error!("parent already added"); return Err(Error::BadParam("Parent parent already added".to_owned())); } - // if the hash of our new ingredient does not match any of the ingredients - // then add it - if !self - .ingredients - .iter() - .any(|i| ingredient.hash().is_some() && i.hash() == ingredient.hash()) - { - debug!("ingredients:set_is_parent {:?}", ingredient.title()); - ingredient.set_is_parent(); - self.ingredients.insert(0, ingredient); - } else { - // dup so just keep the ingredient instead of adding the parent - warn!("duplicate parent {}", ingredient.title()); - } + ingredient.set_is_parent(); + self.ingredients.insert(0, ingredient); Ok(self) } /// Add an ingredient removing duplicates (consumes the asset) pub fn add_ingredient(&mut self, ingredient: Ingredient) -> &mut Self { - // if the hash of the new asset does not match any of the ingredients - // then add it - if !self - .ingredients - .iter() - .any(|i| ingredient.hash().is_some() && i.hash() == ingredient.hash()) - { - debug!("Manifest:add_ingredient {:?}", ingredient.title()); - self.ingredients.push(ingredient); - } else { - warn!("duplicate ingredient {}", ingredient.title()); - } + self.ingredients.push(ingredient); self } @@ -823,6 +800,7 @@ impl Manifest { }) .collect(); + dbg!(&needs_ingredient); for (index, action) in needs_ingredient { if let Some(id) = action.instance_id() { if let Some(hash_url) = ingredient_map.get(id) { @@ -2201,7 +2179,7 @@ pub(crate) mod tests { #[actix::test] #[cfg(feature = "file_io")] async fn test_box_hash_embeddable_manifest() { - let asset_bytes = include_bytes!("../tests/fixtures/CA.jpg"); + let asset_bytes = include_bytes!("../tests/fixtures/boxhash.jpg"); let box_hash_data = include_bytes!("../tests/fixtures/boxhash.json"); let box_hash: crate::assertions::BoxHash = serde_json::from_slice(box_hash_data).unwrap(); @@ -2227,8 +2205,8 @@ pub(crate) mod tests { ) .await .unwrap(); + println!("{manifest_store}"); assert!(!manifest_store.manifests().is_empty()); assert!(manifest_store.validation_status().is_none()); - println!("{manifest_store}"); } } diff --git a/sdk/src/store.rs b/sdk/src/store.rs @@ -3991,7 +3991,7 @@ pub mod tests { fn test_modify_xmp() { // modify the XMP (change xmp magic id value) - this should cause a data hash mismatch (OTGP) let mut report = patch_and_report( - "C.jpg", + "cloud.jpg", b"W5M0MpCehiHzreSzNTczkc9d", b"W5M0MpCehiHzreSzNTczkXXX", ); @@ -4474,6 +4474,7 @@ pub mod tests { } #[actix::test] + #[cfg(feature = "file_io")] async fn test_boxhash_embeddable_manifest() { // test adding to actual image let ap = fixture_path("boxhash.jpg"); diff --git a/sdk/tests/fixtures/C.jpg b/sdk/tests/fixtures/C.jpg Binary files differ. diff --git a/sdk/tests/fixtures/CA.jpg b/sdk/tests/fixtures/CA.jpg Binary files differ. diff --git a/sdk/tests/fixtures/CIE-sig-CA.jpg b/sdk/tests/fixtures/CIE-sig-CA.jpg Binary files differ. diff --git a/sdk/tests/fixtures/E-sig-CA.jpg b/sdk/tests/fixtures/E-sig-CA.jpg Binary files differ. diff --git a/sdk/tests/fixtures/XCA.jpg b/sdk/tests/fixtures/XCA.jpg Binary files differ.