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 d9e052b594b1fa87bf639a1b167b8c26dd532513
parent 2cb845f1a9dc2a1faaebabb85eef78af09dc9800
Author: Gavin  Peacock <gpeacock@adobe.com>
Date:   Tue, 11 Jul 2023 13:24:41 -0700

(MINOR) Converts DataHash and BoxHash methods to use RemoteSigner instead of AsyncSigner (#280)

*  Change 
          manifest.data_hash_manifest_placeholder
          manifest.data_hash_embeddable_manifest
          manifest.box_hash_embeddable_manifest
     to use RemoteSigner instead of AsyncSigner
Added a new test function - temp_remote_signer()
Diffstat:
Msdk/src/manifest.rs | 86+++++++++++++++++++++++++------------------------------------------------------
Msdk/src/store.rs | 65++++++++++++++++++++++++++---------------------------------------
Msdk/src/utils/test.rs | 49++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 101 insertions(+), 99 deletions(-)

diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs @@ -22,6 +22,8 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::Value; use uuid::Uuid; +#[cfg(feature = "file_io")] +use crate::AsyncSigner; use crate::{ assertion::{AssertionBase, AssertionData}, assertions::{ @@ -35,8 +37,7 @@ use crate::{ resource_store::{skip_serializing_resources, ResourceRef, ResourceStore}, salt::DefaultSalt, store::Store, - AsyncSigner, ClaimGeneratorInfo, Ingredient, ManifestAssertion, ManifestAssertionKind, - RemoteSigner, Signer, + ClaimGeneratorInfo, Ingredient, ManifestAssertion, ManifestAssertionKind, RemoteSigner, Signer, }; /// A Manifest represents all the information in a c2pa manifest @@ -1167,7 +1168,7 @@ impl Manifest { /// This is used to create a properly formatted file ready for signing pub fn data_hash_placeholder( &mut self, - signer: &dyn AsyncSigner, + signer: &dyn RemoteSigner, format: &str, ) -> Result<Vec<u8>> { let dh: Result<DataHash> = self.find_assertion(DataHash::LABEL); @@ -1196,7 +1197,7 @@ impl Manifest { pub async fn data_hash_embeddable_manifest( &mut self, dh: &DataHash, - signer: &dyn AsyncSigner, + signer: &dyn RemoteSigner, format: &str, mut asset_reader: Option<&mut dyn CAIRead>, ) -> Result<Vec<u8>> { @@ -1215,7 +1216,7 @@ impl Manifest { /// The manifest must include a box hash assertion with correct hashes pub async fn box_hash_embeddable_manifest( &mut self, - signer: &dyn AsyncSigner, + signer: &dyn RemoteSigner, format: Option<&str>, ) -> Result<Vec<u8>> { let mut store = self.to_store()?; @@ -1265,7 +1266,7 @@ pub(crate) mod tests { use crate::{ assertions::{c2pa_action, Action, Actions}, - utils::test::{temp_signer, TEST_VC}, + utils::test::{temp_remote_signer, temp_signer, TEST_VC}, Ingredient, Manifest, Result, }; #[cfg(feature = "file_io")] @@ -1280,7 +1281,7 @@ pub(crate) mod tests { fixture_path, temp_dir_path, temp_fixture_path, write_jpeg_placeholder_file, TEST_SMALL_JPEG, }, - validation_status, SigningAlg, + validation_status, }; // example of random data structure as an assertion @@ -1601,32 +1602,14 @@ pub(crate) mod tests { #[cfg(all(feature = "file_io", feature = "openssl_sign"))] #[actix::test] async fn test_embed_remote_sign() { - struct MyRemoteSigner {} - - #[async_trait::async_trait] - impl crate::signer::RemoteSigner for MyRemoteSigner { - async fn sign_remote(&self, claim_bytes: &[u8]) -> Result<Vec<u8>> { - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new( - crate::SigningAlg::Ps256, - ); - - // this would happen on some remote server - crate::cose_sign::cose_sign_async(&signer, claim_bytes, self.reserve_size()).await - } - - fn reserve_size(&self) -> usize { - 10000 - } - } - let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); - let remote_signer = MyRemoteSigner {}; + let remote_signer = temp_remote_signer(); let mut manifest = test_manifest(); manifest - .embed_remote_signed(&output, &output, &remote_signer) + .embed_remote_signed(&output, &output, remote_signer.as_ref()) .await .expect("embed"); let manifest_store = crate::ManifestStore::from_file(&output).expect("from_file"); @@ -1686,28 +1669,6 @@ pub(crate) mod tests { ); } - struct MyRemoteSigner {} - - #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] - impl crate::signer::RemoteSigner for MyRemoteSigner { - async fn sign_remote(&self, claim_bytes: &[u8]) -> crate::error::Result<Vec<u8>> { - use std::io::{Seek, Write}; - - let mut sign_bytes = std::io::Cursor::new(vec![0u8; self.reserve_size()]); - - sign_bytes.rewind()?; - sign_bytes.write_all(claim_bytes)?; - - // fake sig - Ok(sign_bytes.into_inner()) - } - - fn reserve_size(&self) -> usize { - 10000 - } - } - #[cfg_attr(not(target_arch = "wasm32"), actix::test)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] async fn test_embed_jpeg_stream_wasm() { @@ -1731,11 +1692,11 @@ pub(crate) mod tests { ingredient.set_title("parent.jpg"); manifest.set_parent(ingredient).expect("set_parent"); - let signer = MyRemoteSigner {}; + let signer = temp_remote_signer(); // Embed a manifest using the signer. let (out_vec, _out_manifest) = manifest - .embed_from_memory_remote_signed("jpeg", image, &signer) + .embed_from_memory_remote_signed("jpeg", image, signer.as_ref()) .await .expect("embed_stream"); @@ -1767,11 +1728,11 @@ pub(crate) mod tests { )) .unwrap(); - let signer = MyRemoteSigner {}; + let signer = temp_remote_signer(); // Embed a manifest using the signer. let (out_vec, _out_manifest) = manifest - .embed_from_memory_remote_signed("png", image, &signer) + .embed_from_memory_remote_signed("png", image, signer.as_ref()) .await .expect("embed_stream"); @@ -2185,12 +2146,14 @@ pub(crate) mod tests { async fn test_data_hash_embeddable_manifest() { let ap = fixture_path("cloud.jpg"); - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = temp_remote_signer(); let mut manifest = Manifest::new("claim_generator"); // get a placeholder the manifest - let placeholder = manifest.data_hash_placeholder(&signer, "jpeg").unwrap(); + let placeholder = manifest + .data_hash_placeholder(signer.as_ref(), "jpeg") + .unwrap(); let temp_dir = tempfile::tempdir().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); @@ -2215,7 +2178,12 @@ pub(crate) mod tests { dh.exclusions = Some(exclusions); let signed_manifest = manifest - .data_hash_embeddable_manifest(&dh, &signer, "image/jpeg", Some(&mut output_file)) + .data_hash_embeddable_manifest( + &dh, + signer.as_ref(), + "image/jpeg", + Some(&mut output_file), + ) .await .unwrap(); @@ -2232,7 +2200,7 @@ pub(crate) mod tests { #[actix::test] #[cfg(feature = "file_io")] - async fn test_box_hash_embedable_manifest() { + async fn test_box_hash_embeddable_manifest() { let asset_bytes = include_bytes!("../tests/fixtures/CA.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(); @@ -2244,10 +2212,10 @@ pub(crate) mod tests { .add_labeled_assertion(crate::assertions::labels::BOX_HASH, &box_hash) .unwrap(); - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = temp_remote_signer(); let embeddable = manifest - .box_hash_embeddable_manifest(&signer, None) + .box_hash_embeddable_manifest(signer.as_ref(), None) .await .expect("embeddable_manifest"); diff --git a/sdk/src/store.rs b/sdk/src/store.rs @@ -51,7 +51,7 @@ use crate::{ hash_utils::{hash256, HashRange}, patch::patch_bytes, }, - validation_status, AsyncSigner, ManifestStoreReport, Signer, + validation_status, AsyncSigner, ManifestStoreReport, RemoteSigner, Signer, }; #[cfg(feature = "file_io")] use crate::{ @@ -1628,7 +1628,7 @@ impl Store { /// from `get_data_hashed_embeddable_manifest` will have a size that matches this function. pub fn get_data_hashed_manifest_placeholder( &mut self, - signer: &dyn AsyncSigner, + signer: &dyn RemoteSigner, format: &str, ) -> Result<Vec<u8>> { let pc = self.provenance_claim_mut().ok_or(Error::ClaimEncoding)?; @@ -1668,7 +1668,7 @@ impl Store { pub async fn get_data_hashed_embeddable_manifest( &mut self, dh: &DataHash, - signer: &dyn AsyncSigner, + signer: &dyn RemoteSigner, format: &str, asset_reader: Option<&mut dyn CAIRead>, ) -> Result<Vec<u8>> { @@ -1705,9 +1705,9 @@ impl Store { let mut jumbf_bytes = self.to_jumbf_internal(signer.reserve_size())?; // sign contents - let sig = self - .sign_claim_async(pc, signer, signer.reserve_size()) - .await?; + let claim_bytes = pc.data()?; + let sig = signer.sign_remote(&claim_bytes).await?; + let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); if sig_placeholder.len() != sig.len() { @@ -1724,7 +1724,7 @@ impl Store { /// included the necessary box hash assertion with the pregenerated hashes. pub async fn get_box_hashed_embeddable_manifest( &mut self, - signer: &dyn AsyncSigner, + signer: &dyn RemoteSigner, ) -> Result<Vec<u8>> { let pc = self.provenance_claim().ok_or(Error::ClaimEncoding)?; @@ -1743,9 +1743,8 @@ impl Store { let mut jumbf_bytes = self.to_jumbf_internal(signer.reserve_size())?; // sign contents - let sig = self - .sign_claim_async(pc, signer, signer.reserve_size()) - .await?; + let claim_bytes = pc.data()?; + let sig = signer.sign_remote(&claim_bytes).await?; let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); if sig_placeholder.len() != sig.len() { @@ -2896,8 +2895,8 @@ pub mod tests { hash_utils::Hasher, patch::patch_file, test::{ - create_test_claim, fixture_path, temp_dir_path, temp_fixture_path, temp_signer, - write_jpeg_placeholder_file, + create_test_claim, fixture_path, temp_dir_path, temp_fixture_path, + temp_remote_signer, temp_signer, write_jpeg_placeholder_file, }, }, AssertionJson, SigningAlg, @@ -3121,23 +3120,6 @@ pub mod tests { } } - struct MyRemoteSigner {} - - #[async_trait::async_trait] - impl crate::signer::RemoteSigner for MyRemoteSigner { - async fn sign_remote(&self, claim_bytes: &[u8]) -> crate::error::Result<Vec<u8>> { - let signer = - crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); - - // this would happen on some remote server - crate::cose_sign::cose_sign_async(&signer, claim_bytes, self.reserve_size()).await - } - - fn reserve_size(&self) -> usize { - 10000 - } - } - #[test] #[cfg(feature = "file_io")] fn test_detects_unverifiable_signature() { @@ -3294,11 +3276,11 @@ pub mod tests { let claim1 = create_test_claim().unwrap(); // create my remote signer to map the CoseSign1 data back into the asset - let remote_signer = MyRemoteSigner {}; + let remote_signer = crate::utils::test::temp_remote_signer(); store.commit_claim(claim1).unwrap(); store - .save_to_asset_remote_signed(&ap, &remote_signer, &op) + .save_to_asset_remote_signed(&ap, remote_signer.as_ref(), &op) .await .unwrap(); @@ -4512,11 +4494,11 @@ pub mod tests { store.commit_claim(claim).unwrap(); // Do we generate JUMBF? - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = temp_remote_signer(); // get the embeddable manifest let em = store - .get_box_hashed_embeddable_manifest(&signer) + .get_box_hashed_embeddable_manifest(signer.as_ref()) .await .unwrap(); @@ -4577,7 +4559,7 @@ pub mod tests { let ap = fixture_path("cloud.jpg"); // Do we generate JUMBF? - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = temp_remote_signer(); // Create claims store. let mut store = Store::new(); @@ -4589,7 +4571,7 @@ pub mod tests { // get a placeholder the manifest let placeholder = store - .get_data_hashed_manifest_placeholder(&signer, "jpeg") + .get_data_hashed_manifest_placeholder(signer.as_ref(), "jpeg") .unwrap(); let temp_dir = tempfile::tempdir().unwrap(); @@ -4617,7 +4599,12 @@ pub mod tests { // get the embeddable manifest, letting API do the hashing output_file.rewind().unwrap(); let cm = store - .get_data_hashed_embeddable_manifest(&dh, &signer, "jpeg", Some(&mut output_file)) + .get_data_hashed_embeddable_manifest( + &dh, + signer.as_ref(), + "jpeg", + Some(&mut output_file), + ) .await .unwrap(); @@ -4640,7 +4627,7 @@ pub mod tests { let mut hasher = Hasher::SHA256(Sha256::new()); // Do we generate JUMBF? - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = temp_remote_signer(); // Create claims store. let mut store = Store::new(); @@ -4652,7 +4639,7 @@ pub mod tests { // get a placeholder for the manifest let placeholder = store - .get_data_hashed_manifest_placeholder(&signer, "jpeg") + .get_data_hashed_manifest_placeholder(signer.as_ref(), "jpeg") .unwrap(); let temp_dir = tempfile::tempdir().unwrap(); @@ -4681,7 +4668,7 @@ pub mod tests { // get the embeddable manifest, using user hashing let cm = store - .get_data_hashed_embeddable_manifest(&dh, &signer, "jpeg", None) + .get_data_hashed_embeddable_manifest(&dh, signer.as_ref(), "jpeg", None) .await .unwrap(); diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs @@ -27,7 +27,7 @@ use crate::{ claim::Claim, salt::DefaultSalt, store::Store, - Result, Signer, SigningAlg, + RemoteSigner, Result, Signer, SigningAlg, }; #[cfg(feature = "file_io")] use crate::{ @@ -284,6 +284,11 @@ impl crate::Signer for TestGoodSigner { } } +/// Create a [`Signer`] instance that can be used for testing purposes using ps256 alg. +/// +/// # Returns +/// +/// Returns a boxed [`Signer`] instance. pub fn temp_signer() -> Box<dyn Signer> { #[cfg(feature = "openssl_sign")] { @@ -331,6 +336,48 @@ pub fn temp_signer_with_alg(alg: SigningAlg) -> Box<dyn Signer> { .expect("get_temp_signer_with_alg") } +struct TempRemoteSigner {} + +#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] +impl crate::signer::RemoteSigner for TempRemoteSigner { + async fn sign_remote(&self, claim_bytes: &[u8]) -> crate::error::Result<Vec<u8>> { + #[cfg(feature = "openssl_sign")] + { + let signer = + crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + + // this would happen on some remote server + crate::cose_sign::cose_sign_async(&signer, claim_bytes, self.reserve_size()).await + } + #[cfg(not(feature = "openssl_sign"))] + { + use std::io::{Seek, Write}; + + let mut sign_bytes = std::io::Cursor::new(vec![0u8; self.reserve_size()]); + + sign_bytes.rewind()?; + sign_bytes.write_all(claim_bytes)?; + + // fake sig + Ok(sign_bytes.into_inner()) + } + } + + fn reserve_size(&self) -> usize { + 10000 + } +} + +/// Create a [`RemoteSigner`] instance that can be used for testing purposes. +/// +/// # Returns +/// +/// Returns a boxed [`RemoteSigner`] instance. +pub fn temp_remote_signer() -> Box<dyn RemoteSigner> { + Box::new(TempRemoteSigner {}) +} + #[test] fn test_create_test_store() { #[allow(clippy::expect_used)]