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 a680db2bd6d6a82398e361ee604ce639c24fdef6
parent 7f12199f569d7b218379d2bbdd087804e9d5bd85
Author: Gavin  Peacock <gpeacock@adobe.com>
Date:   Fri, 17 Jun 2022 10:17:47 -0700

(MINOR) Remove temp_signer from sdk; update docs and examples to use get_signer_from_files (#52)

* Remove temp_signer from sdk.
Update docs and examples to use get_signer_from_files.
Move test_signer to utils::test and update unit tests.
* Proofreading doc comments
* Use correct heading (h1) for example
* Fix some outdated doc comments

Co-authored-by: Eric Scouten <scouten@adobe.com>
Diffstat:
Mmake_test_images/src/make_test_images.rs | 23+++++++++++------------
Msdk/examples/client/client.rs | 17++++++-----------
Msdk/src/asset_handlers/c2pa_io.rs | 6++----
Msdk/src/lib.rs | 18++++++++++--------
Msdk/src/manifest.rs | 60++++++++++++++++++++++++++++++++++++++++++------------------
Msdk/src/openssl/mod.rs | 3++-
Msdk/src/openssl/rsa_signer.rs | 9+++++----
Msdk/src/openssl/temp_signer.rs | 82-------------------------------------------------------------------------------
Msdk/src/store.rs | 30++++++++++++------------------
Msdk/src/utils/test.rs | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msdk/tests/integration.rs | 21+++++++++++----------
11 files changed, 166 insertions(+), 168 deletions(-)

diff --git a/make_test_images/src/make_test_images.rs b/make_test_images/src/make_test_images.rs @@ -15,8 +15,8 @@ //! use c2pa::{ assertions::{c2pa_action, Action, Actions, CreativeWork, SchemaDotOrgPerson}, - get_temp_signer_by_alg, jumbf_io, Error, Ingredient, IngredientOptions, Manifest, - ManifestStore, + get_signer_from_files, jumbf_io, Error, Ingredient, IngredientOptions, Manifest, ManifestStore, + Signer, }; use anyhow::{Context, Result}; @@ -32,12 +32,13 @@ use twoway::find_bytes; const IMAGE_WIDTH: u32 = 2048; const IMAGE_HEIGHT: u32 = 1365; -// returns a path to a file in the fixtures folder -fn fixture_path(file_name: &str) -> PathBuf { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("../sdk/tests/fixtures"); - path.push(file_name); - path +fn get_signer_with_alg(alg: &str) -> c2pa::Result<Box<dyn Signer>> { + // sign and embed into the target file + let mut signcert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + signcert_path.push(format!("../sdk/tests/fixtures/certs/{}.pub", alg)); + let mut pkey_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + pkey_path.push(format!("../sdk/tests/fixtures/certs/{}.pem", alg)); + get_signer_from_files(signcert_path, pkey_path, alg, None) } /// Defines an operation for creating a test image @@ -265,10 +266,8 @@ impl MakeTestImages { // add all our actions as an assertion now. manifest.add_assertion(&actions)?; // extra get required here, since actions is an array - // now create store; sign claim and embed in target - let certs_dir = fixture_path("certs"); - let (signer, _) = - get_temp_signer_by_alg(&certs_dir, &self.config.alg, self.config.tsa_url.clone()); + // now sign manifest and embed in target + let signer = get_signer_with_alg(&self.config.alg)?; manifest.embed(&dst_path, &dst_path, signer.as_ref())?; diff --git a/sdk/examples/client/client.rs b/sdk/examples/client/client.rs @@ -17,16 +17,9 @@ use anyhow::Result; use c2pa::{ assertions::{c2pa_action, labels, Action, Actions, CreativeWork, SchemaDotOrgPerson}, - get_temp_signer, Ingredient, Manifest, ManifestStore, + get_signer_from_files, Ingredient, Manifest, ManifestStore, }; use std::path::PathBuf; -// returns a path to a file in the fixtures folder -fn fixture_path(file_name: &str) -> PathBuf { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("../sdk/tests/fixtures"); - path.push(file_name); - path -} const GENERATOR: &str = "test_app/0.1"; const INDENT_SPACE: usize = 2; @@ -118,9 +111,11 @@ pub fn main() -> Result<()> { .add_assertion(&creative_work)?; // sign and embed into the target file - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); - manifest.embed(&source, &dest, &signer)?; + let signcert_path = "../sdk/tests/fixtures/certs.ps256.pem"; + let pkey_path = "../sdk/tests/fixtures/certs.ps256.pub"; + let signer = get_signer_from_files(signcert_path, pkey_path, "ps256", None)?; + + manifest.embed(&source, &dest, &*signer)?; let manifest_store = ManifestStore::from_file(&dest)?; diff --git a/sdk/src/asset_handlers/c2pa_io.rs b/sdk/src/asset_handlers/c2pa_io.rs @@ -66,10 +66,9 @@ pub mod tests { use tempfile::tempdir; use crate::{ - openssl::temp_signer::get_temp_signer, status_tracker::OneShotStatusTracker, store::Store, - utils::test::{fixture_path, temp_dir_path}, + utils::test::{fixture_path, temp_dir_path, temp_signer}, }; #[test] @@ -88,8 +87,7 @@ pub mod tests { let store = Store::load_from_asset(&temp_path, false, &mut OneShotStatusTracker::new()) .expect("loading store"); - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); let manifest2 = store.to_jumbf(&signer).expect("to_jumbf"); assert_eq!(&manifest, &manifest2); diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs @@ -45,7 +45,7 @@ //! # use c2pa::Result; //! use c2pa::{ //! assertions::User, -//! get_temp_signer, +//! get_signer_from_files, //! Manifest //! }; //! @@ -60,9 +60,13 @@ //! let dir = tempdir()?; //! let dest = dir.path().join("test_file.jpg"); //! -//! let cert_dir = PathBuf::from("tests/fixtures/certs"); -//! let (signer, _) = get_temp_signer(&cert_dir); -//! manifest.embed(&source, &dest, &signer)?; +//! // Create a ps256 signer using certs and key files +//! let signcert_path = "tests/fixtures/certs/ps256.pub"; +//! let pkey_path = "tests/fixtures/certs/ps256.pem"; +//! let signer = get_signer_from_files(signcert_path, pkey_path, "ps256", None)?; +//! +//! // embed a manifest using the signer +//! manifest.embed(&source, &dest, &*signer)?; //! # Ok(()) //! # } //! ``` @@ -94,10 +98,8 @@ pub(crate) mod ocsp_utils; #[cfg(feature = "file_io")] mod openssl; #[cfg(feature = "file_io")] -pub use crate::openssl::{ - signer::{get_signer, get_signer_from_files}, - temp_signer::{get_temp_signer, get_temp_signer_by_alg}, -}; +pub use crate::openssl::signer::{get_signer, get_signer_from_files}; + #[cfg(feature = "file_io")] mod signer; #[cfg(feature = "async_signer")] diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs @@ -542,28 +542,55 @@ impl Manifest { Ok(store) } - /// Embed a signed manifest into the target file using a supplied signer + /// Embed a signed manifest into the target file using a supplied signer. + /// + /// # Example: Embed a manifest in a file + /// + /// ``` + /// # use c2pa::Result; + /// use c2pa::{ + /// assertions::User, + /// get_signer_from_files, + /// Manifest + /// }; + /// # fn main() -> Result<()> { + /// let mut manifest = Manifest::new("my_app".to_owned()); + /// manifest.add_assertion(&User::new("org.contentauth.mylabel", r#"{"my_tag":"Anything I want"}"#))?; + /// + /// let source = "tests/fixtures/C.jpg"; + /// let dest = "../target/test_file.jpg"; + /// + /// // Create a PS256 signer using certs and public key files. + /// let signcert_path = "tests/fixtures/certs/ps256.pub"; + /// let pkey_path = "tests/fixtures/certs/ps256.pem"; + /// let signer = get_signer_from_files(signcert_path, pkey_path, "ps256", None)?; + /// + /// // Embed a manifest using the signer. + /// manifest.embed(&source, &dest, &*signer)?; + /// # Ok(()) + /// # } + /// ``` #[cfg(feature = "file_io")] - pub fn embed( + pub fn embed<P: AsRef<Path>>( &mut self, - source_path: &Path, - dest_path: &Path, + source_path: P, + dest_path: P, signer: &dyn Signer, ) -> Result<Store> { - if !source_path.exists() { - let path = source_path.to_string_lossy().into_owned(); + if !source_path.as_ref().exists() { + let path = source_path.as_ref().to_string_lossy().into_owned(); return Err(Error::FileNotFound(path)); } // we need to copy the source to target before setting the asset info - if !dest_path.exists() { + if !dest_path.as_ref().exists() { std::fs::copy(&source_path, &dest_path)?; } // first add the information about the target file - self.set_asset_from_path(dest_path); + self.set_asset_from_path(dest_path.as_ref()); // convert the manifest to a store let mut store = self.to_store()?; // sign and write our store to to the output image file - store.save_to_asset(source_path, signer, dest_path.as_ref())?; + store.save_to_asset(source_path.as_ref(), signer, dest_path.as_ref())?; // todo: update xmp Ok(store) @@ -620,10 +647,11 @@ pub(crate) mod tests { #[cfg(feature = "file_io")] use crate::{ - openssl::temp_signer::get_temp_signer, status_tracker::{DetailedStatusTracker, StatusTracker}, store::Store, - utils::test::{fixture_path, temp_dir_path, temp_fixture_path, TEST_SMALL_JPEG}, + utils::test::{ + fixture_path, temp_dir_path, temp_fixture_path, temp_signer, TEST_SMALL_JPEG, + }, Ingredient, }; @@ -693,8 +721,7 @@ pub(crate) mod tests { let test_output = dir.path().join("wc_embed_test.jpg"); //embed a claim generated from this manifest - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); let _store = manifest .embed(&source_path, &test_output, &signer) @@ -797,8 +824,7 @@ pub(crate) mod tests { ) .expect("add_assertion"); - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); let store1 = manifest.embed(&output, &output, &signer).expect("embed"); let claim1_label = store1.provenance_label().unwrap(); @@ -822,9 +848,7 @@ pub(crate) mod tests { .expect("add_redaction"); //embed a claim in output2 - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); - + let signer = temp_signer(); let _store2 = manifest2.embed(&output2, &output2, &signer).expect("embed"); let mut report = DetailedStatusTracker::new(); diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs @@ -30,7 +30,8 @@ mod ed_validator; pub(crate) use ed_validator::EdValidator; pub mod signer; -pub mod temp_signer; +#[cfg(test)] +pub(crate) mod temp_signer; use openssl::x509::X509; diff --git a/sdk/src/openssl/rsa_signer.rs b/sdk/src/openssl/rsa_signer.rs @@ -212,13 +212,14 @@ mod tests { use super::*; - use crate::{openssl::temp_signer::get_temp_signer, utils::test::fixture_path, Signer}; + use crate::{ + utils::test::{fixture_path, temp_signer}, + Signer, + }; #[test] fn signer_from_files() { - let cert_dir = fixture_path("certs"); - - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); let data = b"some sample content to sign"; let signature = signer.sign(data).unwrap(); diff --git a/sdk/src/openssl/temp_signer.rs b/sdk/src/openssl/temp_signer.rs @@ -36,43 +36,8 @@ use std::path::{Path, PathBuf}; use crate::{ openssl::{EcSigner, EdSigner, RsaSigner}, signer::ConfigurableSigner, - Signer, }; -/// Create a [`Signer`] instance that can be used for testing purposes. -/// -/// This is a suitable default for use when you need a [`Signer`], but -/// don't care what the format is. -/// -/// # Arguments -/// -/// * `path` - A directory (which must already exist) to receive the temporary -/// private key / certificate pair. -/// -/// # Returns -/// -/// Returns a tuple of `(signer, sign_cert_path)` where `signer` is -/// the [`Signer`] instance and `sign_cert_path` is the path to the -/// signing certificate. -/// -/// # Panics -/// -/// Can panic if unable to invoke OpenSSL executable properly. -pub fn get_temp_signer<P: AsRef<Path>>(path: P) -> (RsaSigner, PathBuf) { - let mut sign_cert_path = path.as_ref().to_path_buf(); - sign_cert_path.push("ps256"); - sign_cert_path.set_extension("pub"); - - let mut pem_key_path = path.as_ref().to_path_buf(); - pem_key_path.push("ps256"); - pem_key_path.set_extension("pem"); - - ( - RsaSigner::from_files(&sign_cert_path, &pem_key_path, "ps256".to_string(), None).unwrap(), - sign_cert_path, - ) -} - /// Create an OpenSSL ES256 signer that can be used for testing purposes. /// /// # Arguments @@ -210,50 +175,3 @@ pub fn get_rsa_signer<P: AsRef<Path>>( sign_cert_path, ) } - -/// Create a signer that can be used for testing purposes. -/// -/// Can generate a [`Signer`] instance for all supported formats. -/// -/// # Arguments -/// -/// * `path` - A directory (which must already exist) to receive the temporary -/// private key / certificate pair. -/// * `alg` - A format for signing. Must be one of (`rs256`, `rs384`, `rs512`, -/// `ps256`, `ps384`, `ps512`, `es256`, `es384`, `es512`, or `ed25519`). -/// * `tsa_url` - Optional URL for a timestamp authority. -/// -/// # Returns -/// -/// Returns a tuple of `(signer, sign_cert_path)` where `signer` is -/// the [`Signer`] instance and `sign_cert_path` is the path to the -/// signing certificate. -/// -/// # Panics -/// -/// Can panic if unable to invoke OpenSSL executable properly. -pub fn get_temp_signer_by_alg<P: AsRef<Path>>( - path: P, - alg: &str, - tsa_url: Option<String>, -) -> (Box<dyn Signer>, PathBuf) { - match alg.to_lowercase().as_str() { - "ps256" | "ps384" | "ps512" => { - let (signer, sign_cert_path) = get_rsa_signer(path, alg, tsa_url); - (Box::new(signer), sign_cert_path) - } - "es256" | "es384" | "es512" => { - let (signer, sign_cert_path) = get_ec_signer(path, alg, tsa_url); - (Box::new(signer), sign_cert_path) - } - - "ed25519" => { - let (signer, sign_cert_path) = get_ed_signer(path, alg, tsa_url); - (Box::new(signer), sign_cert_path) - } - _ => { - let (signer, sign_cert_path) = get_rsa_signer(path, "ps256", tsa_url); - (Box::new(signer), sign_cert_path) - } - } -} diff --git a/sdk/src/store.rs b/sdk/src/store.rs @@ -1721,15 +1721,15 @@ pub mod tests { use crate::{ assertions::{Action, Actions, Ingredient, Uuid}, - claim::Claim, - jumbf_io::{load_jumbf_from_file, save_jumbf_to_file}, + claim::{AssertionStoreJsonFormat, Claim}, + jumbf_io::{load_jumbf_from_file, save_jumbf_to_file, update_file_jumbf}, status_tracker::*, - utils::test::{create_test_claim, fixture_path, temp_dir_path, temp_fixture_path}, - }; - - use crate::{ - claim::AssertionStoreJsonFormat, jumbf_io::update_file_jumbf, - openssl::temp_signer::get_temp_signer, utils::patch::patch_file, + utils::{ + patch::patch_file, + test::{ + create_test_claim, fixture_path, temp_dir_path, temp_fixture_path, temp_signer, + }, + }, }; fn create_editing_claim(claim: &mut Claim) -> Result<&mut Claim> { @@ -1779,8 +1779,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); // Test generate JUMBF // Get labels for label test @@ -2072,8 +2071,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commmits store.commit_claim(claim1).unwrap(); @@ -2228,9 +2226,7 @@ pub mod tests { fn test_verifiable_credentials() { use crate::utils::test::create_test_store; - let cert_dir = fixture_path("certs"); - - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); @@ -2282,9 +2278,7 @@ pub mod tests { fn test_update_manifest() { use crate::{hashed_uri::HashedUri, utils::test::create_test_store}; - let cert_dir = fixture_path("certs"); - - let (signer, _) = get_temp_signer(&cert_dir); + let signer = temp_signer(); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs @@ -20,6 +20,14 @@ use crate::{ store::Store, Result, }; + +#[cfg(feature = "file_io")] +use crate::{ + get_signer_from_files, + openssl::RsaSigner, + signer::{ConfigurableSigner, Signer}, +}; + use std::path::PathBuf; use tempfile::TempDir; @@ -164,6 +172,63 @@ pub fn temp_fixture_path(temp_dir: &TempDir, file_name: &str) -> PathBuf { fixture_copy } +/// Create a [`Signer`] instance that can be used for testing purposes. +/// +/// This is a suitable default for use when you need a [`Signer`], but +/// don't care what the format is. +/// +/// # Returns +/// +/// Returns a boxed [`Signer`] instance. +/// +/// # Panics +/// +/// Can panic if the certs cannot be read. (This function should only +/// be used as part of testing infrastructure.) +#[cfg(feature = "file_io")] +pub fn temp_signer() -> RsaSigner { + #![allow(clippy::expect_used)] + let mut sign_cert_path = fixture_path("certs"); + sign_cert_path.push("ps256"); + sign_cert_path.set_extension("pub"); + + let mut pem_key_path = fixture_path("certs"); + pem_key_path.push("ps256"); + pem_key_path.set_extension("pem"); + + RsaSigner::from_files(&sign_cert_path, &pem_key_path, "ps256".to_string(), None) + .expect("get_temp_signer") +} + +/// Create a [`Signer`] instance for a specific algorithm that can be used for testing purposes. +/// +/// # Parameters: +/// alg: The algorithm to use +/// +/// # Returns +/// +/// Returns a boxed [`Signer`] instance. +/// +/// # Panics +/// +/// Can panic if the certs cannot be read. (This function should only +/// be used as part of testing infrastructure.) +#[cfg(feature = "file_io")] +pub fn temp_signer_with_alg(alg: &str) -> Box<dyn Signer> { + #![allow(clippy::expect_used)] + // sign and embed into the target file + let mut sign_cert_path = fixture_path("certs"); + sign_cert_path.push(alg); + sign_cert_path.set_extension("pub"); + + let mut pem_key_path = fixture_path("certs"); + pem_key_path.push(alg); + pem_key_path.set_extension("pem"); + + get_signer_from_files(sign_cert_path.clone(), pem_key_path, alg, None) + .expect("get_temp_signer_with_alg") +} + #[test] fn test_create_test_store() { #[allow(clippy::expect_used)] diff --git a/sdk/tests/integration.rs b/sdk/tests/integration.rs @@ -18,18 +18,21 @@ mod integration_1 { use c2pa::{ assertions::{c2pa_action, Action, Actions}, - get_temp_signer, Ingredient, Manifest, ManifestStore, Result, + get_signer_from_files, Ingredient, Manifest, ManifestStore, Result, Signer, }; use std::path::PathBuf; use tempfile::tempdir; const GENERATOR: &str = "app"; - fn fixture_path(file_name: &str) -> PathBuf { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/fixtures"); - path.push(file_name); - path + fn get_temp_signer() -> Box<dyn Signer> { + // sign and embed into the target file + let mut signcert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + signcert_path.push("tests/fixtures/certs/ps256.pub"); + let mut pkey_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + pkey_path.push("tests/fixtures/certs/ps256.pem"); + get_signer_from_files(signcert_path, pkey_path, "ps256", None) + .expect("get_signer_from_files") } #[test] @@ -98,10 +101,8 @@ mod integration_1 { img.save(&output_path)?; // sign and embed into the target file - let cert_dir = fixture_path("certs"); - let (signer, _) = get_temp_signer(&cert_dir); - - manifest.embed(&output_path, &output_path, &signer)?; + let signer = get_temp_signer(); + manifest.embed(&output_path, &output_path, &*signer)?; // read our new file with embedded manifest let manifest_store = ManifestStore::from_file(&output_path)?;