From d6ae355e1acdae05dec99fc9c83ecd9c73c72ac4 Mon Sep 17 00:00:00 2001 From: Gavin Peacock Date: Fri, 25 Aug 2023 09:39:35 -0700 Subject: [PATCH 01/10] make ManifestStore new public. --- sdk/src/manifest_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index df70c9d33..2847d8620 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -44,7 +44,7 @@ pub struct ManifestStore { impl ManifestStore { /// allocates a new empty ManifestStore - pub(crate) fn new() -> Self { + pub fn new() -> Self { ManifestStore { active_manifest: None, manifests: HashMap::::new(), From 719628809849201dfb1d714fa43ae89d94916cfd Mon Sep 17 00:00:00 2001 From: Gavin Peacock Date: Fri, 1 Sep 2023 13:40:51 -0700 Subject: [PATCH 02/10] update schmars version --- sdk/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 7255c008e..aef07bc0c 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -84,7 +84,7 @@ png_pong = "0.8.2" range-set = "0.0.9" ring = "0.16.20" riff = "1.0.1" -schemars = { version = "0.8.12", optional = true } +schemars = { version = "0.8.13", optional = true } serde = { version = "1.0.137", features = ["derive"] } serde_bytes = "0.11.5" serde_cbor = "0.11.1" From 09fdeb2685ebede3945f00354f491dcc9aac4e1c Mon Sep 17 00:00:00 2001 From: Gavin Peacock Date: Wed, 27 Sep 2023 11:39:43 -0700 Subject: [PATCH 03/10] Add ManifestStore::from_stream() --- sdk/src/manifest_store.rs | 40 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index 00c296493..d246b72fc 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -25,7 +25,7 @@ use crate::{ store::Store, utils::base64, validation_status::{status_for_store, ValidationStatus}, - Manifest, Result, + CAIRead, Manifest, Result, }; #[derive(Serialize)] @@ -160,6 +160,27 @@ impl ManifestStore { .map(|store| Self::from_store(&store, &mut validation_log)) } + /// generate a Store from a format string and stream + pub fn from_stream( + format: &str, + stream: &mut dyn CAIRead, + verify: bool, + ) -> Result { + let mut validation_log = DetailedStatusTracker::new(); + + let manifest_bytes = Store::load_jumbf_from_stream(format, stream)?; + let store = Store::from_jumbf(&manifest_bytes, &mut validation_log)?; + if verify { + // verify store and claims + Store::verify_store( + &store, + &mut ClaimAssetData::Stream(stream, format), + &mut validation_log, + )?; + } + Ok(Self::from_store(&store, &mut validation_log)) + } + #[cfg(feature = "file_io")] /// Loads a ManifestStore from a file /// Example: @@ -501,4 +522,21 @@ mod tests { assert_eq!(manifest.issuer().unwrap(), "C2PA Test Signing Cert"); assert!(manifest.time().is_some()); } + + #[test] + fn manifest_report_from_stream() { + let image_bytes: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); + let mut stream = std::io::Cursor::new(image_bytes); + let manifest_store = ManifestStore::from_stream("image/jpeg", &mut stream, true).unwrap(); + println!("{manifest_store}"); + + assert!(manifest_store.active_label().is_some()); + assert!(manifest_store.get_active().is_some()); + assert!(!manifest_store.manifests().is_empty()); + assert!(manifest_store.validation_status().is_none()); + let manifest = manifest_store.get_active().unwrap(); + assert!(!manifest.ingredients().is_empty()); + assert_eq!(manifest.issuer().unwrap(), "C2PA Test Signing Cert"); + assert!(manifest.time().is_some()); + } } From 51b488c6a1cda1046ec25a339b876368d68ecd81 Mon Sep 17 00:00:00 2001 From: Gavin Peacock Date: Thu, 28 Sep 2023 09:43:52 -0700 Subject: [PATCH 04/10] Adds embed_to_stream with output stream and returning c2pa_data replaces embed_stream Fixes a bug in embed_stream where missing thumbnails were not reported --- sdk/src/manifest.rs | 61 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 6d5035025..e7668c0c1 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -29,7 +29,7 @@ use crate::{ assertions::{ labels, Actions, CreativeWork, DataHash, Exif, SoftwareAgent, Thumbnail, User, UserCbor, }, - asset_io::CAIRead, + asset_io::{CAIRead, CAIReadWrite}, claim::{Claim, RemoteManifest}, error::{Error, Result}, jumbf, @@ -1023,11 +1023,30 @@ impl Manifest { /// Embed a signed manifest into a stream using a supplied signer. /// returns the bytes of the new asset + /// We plan to deprecate this, please use embed_to_stream instead pub fn embed_stream( &mut self, format: &str, stream: &mut dyn CAIRead, signer: &dyn Signer, + ) -> Result> { + // sign and write our store to to the output image file + let output_vec: Vec = Vec::new(); + let mut output_stream = Cursor::new(output_vec); + + self.embed_to_stream(format, stream, &mut output_stream, signer)?; + + Ok(output_stream.into_inner()) + } + + /// Embed a signed manifest into a stream using a supplied signer. + /// returns the bytes of c2pa_manifest that was embedded + pub fn embed_to_stream( + &mut self, + format: &str, + source: &mut dyn CAIRead, + dest: &mut dyn CAIReadWrite, + signer: &dyn Signer, ) -> Result> { self.set_format(format); // todo:: read instance_id from xmp from stream @@ -1036,9 +1055,9 @@ impl Manifest { // generate thumbnail if we don't already have one #[cfg(feature = "add_thumbnails")] { - if self.thumbnail().is_none() { + if self.thumbnail_ref().is_none() { if let Ok((format, image)) = - crate::utils::thumbnail::make_thumbnail_from_stream(format, stream) + crate::utils::thumbnail::make_thumbnail_from_stream(format, source) { self.set_thumbnail(format, image)?; } @@ -1049,12 +1068,7 @@ impl Manifest { let mut store = self.to_store()?; // sign and write our store to to the output image file - let output_vec: Vec = Vec::new(); - let mut output_stream = Cursor::new(output_vec); - - store.save_to_stream(format, stream, &mut output_stream, signer)?; - - Ok(output_stream.into_inner()) + store.save_to_stream(format, source, dest, signer) } /// Embed a signed manifest into a stream using a supplied signer. @@ -1075,7 +1089,7 @@ impl Manifest { let mut stream = std::io::Cursor::new(asset); #[cfg(feature = "add_thumbnails")] { - if self.thumbnail().is_none() { + if self.thumbnail_ref().is_none() { if let Ok((format, image)) = crate::utils::thumbnail::make_thumbnail_from_stream(format, &mut stream) { @@ -2209,6 +2223,33 @@ pub(crate) mod tests { assert!(active_manifest.thumbnail().is_none()); } + #[test] + fn test_missing_thumbnail() { + const MANIFEST_JSON: &str = r#" + { + "claim_generator": "test", + "format" : "image/jpeg", + "thumbnail": { + "format": "image/jpeg", + "identifier": "does_not_exist.jpg" + } + } + "#; + + let mut manifest = Manifest::from_json(MANIFEST_JSON).expect("from_json"); + + let mut source = std::io::Cursor::new(vec![1, 2, 3]); + let mut dest = std::io::Cursor::new(Vec::new()); + let signer = temp_signer(); + let result = + manifest.embed_to_stream("image/jpeg", &mut source, &mut dest, signer.as_ref()); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("resource not found: does_not_exist.jpg")); + } + #[test] #[cfg(feature = "file_io")] fn test_data_hash_embeddable_manifest() { From cc297e02cb089020ace2f7c2c3c4a0c70e88271d Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 4 Oct 2023 12:15:04 -0700 Subject: [PATCH 05/10] Also update schemars dependency in export_schema crate --- export_schema/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/export_schema/Cargo.toml b/export_schema/Cargo.toml index e0664a5bf..10d4d7d58 100644 --- a/export_schema/Cargo.toml +++ b/export_schema/Cargo.toml @@ -9,5 +9,5 @@ rust-version = "1.70.0" [dependencies] anyhow = "1.0.40" c2pa = { path = "../sdk", features = ["file_io", "json_schema"] } -schemars = "0.8.12" +schemars = "0.8.13" serde_json = "1.0.81" From ccf0bc7b597019fde2b7d04d474633ca45cd174e Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 4 Oct 2023 12:22:54 -0700 Subject: [PATCH 06/10] Proofreading manifest.rs --- sdk/src/manifest.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index e7668c0c1..afa19bd61 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1022,8 +1022,10 @@ impl Manifest { } /// Embed a signed manifest into a stream using a supplied signer. - /// returns the bytes of the new asset - /// We plan to deprecate this, please use embed_to_stream instead + /// + /// Returns the bytes of the new asset. + /// + /// We plan to deprecate this; please use embed_to_stream instead. pub fn embed_stream( &mut self, format: &str, From a1ca4910593e8f4a3a1f0d84b3b854cd625d0ade Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 4 Oct 2023 12:23:28 -0700 Subject: [PATCH 07/10] Proofreading manifest.rs --- sdk/src/manifest.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index afa19bd61..f9110fea1 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1042,7 +1042,8 @@ impl Manifest { } /// Embed a signed manifest into a stream using a supplied signer. - /// returns the bytes of c2pa_manifest that was embedded + /// + /// Returns the bytes of c2pa_manifest that was embedded. pub fn embed_to_stream( &mut self, format: &str, From e25ff66d293ad6b172764f517529ecce500b7dc7 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 4 Oct 2023 12:24:30 -0700 Subject: [PATCH 08/10] Proofreading manifest_store.rs --- sdk/src/manifest_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index d246b72fc..94c7e9b31 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -152,7 +152,7 @@ impl ManifestStore { )) } - /// generate a Store from a format string and bytes + /// Generate a Store from a format string and bytes. pub fn from_bytes(format: &str, image_bytes: &[u8], verify: bool) -> Result { let mut validation_log = DetailedStatusTracker::new(); From e7982bb14d035504cd935eac7a87f47c653bcd67 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 4 Oct 2023 12:24:55 -0700 Subject: [PATCH 09/10] Proofreading manifest_store.rs --- sdk/src/manifest_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index 94c7e9b31..b0214d7a0 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -160,7 +160,7 @@ impl ManifestStore { .map(|store| Self::from_store(&store, &mut validation_log)) } - /// generate a Store from a format string and stream + /// Generate a Store from a format string and stream. pub fn from_stream( format: &str, stream: &mut dyn CAIRead, From a2afc7703424faaa5ba8baf08998594b59664076 Mon Sep 17 00:00:00 2001 From: Gavin Peacock Date: Thu, 12 Oct 2023 19:33:11 -0700 Subject: [PATCH 10/10] clippy fix --- sdk/src/manifest_store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index 2751f930b..4c0073dfc 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -175,7 +175,7 @@ impl ManifestStore { &mut validation_log, )?; } - Ok(Self::from_store(&store, &mut validation_log)) + Ok(Self::from_store(&store, &validation_log)) } #[cfg(feature = "file_io")]