Skip to content

Commit

Permalink
Expose authoring support in WASM (#369)
Browse files Browse the repository at this point in the history
* Expose authoring support in WASM
Expose async version of 'embed_from_memory' which can be called from wasm.
Update the SyncSigner/AsyncSigner to separate the timestamp message creation from sending HTTP request.
Add unit test for the same.

* additional merge changes
  • Loading branch information
duggaraju authored May 1, 2024
1 parent c017f93 commit 5160b6f
Show file tree
Hide file tree
Showing 5 changed files with 299 additions and 139 deletions.
73 changes: 68 additions & 5 deletions sdk/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ use std::{borrow::Cow, collections::HashMap, io::Cursor};
#[cfg(feature = "file_io")]
use std::{fs::create_dir_all, path::Path};

use async_generic::async_generic;
use log::{debug, error};
#[cfg(feature = "json_schema")]
use schemars::JsonSchema;
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::{
Expand All @@ -38,7 +37,8 @@ use crate::{
resource_store::{skip_serializing_resources, ResourceRef, ResourceStore},
salt::DefaultSalt,
store::Store,
ClaimGeneratorInfo, HashRange, ManifestAssertionKind, RemoteSigner, Signer, SigningAlg,
AsyncSigner, ClaimGeneratorInfo, HashRange, ManifestAssertionKind, RemoteSigner, Signer,
SigningAlg,
};

/// A Manifest represents all the information in a c2pa manifest
Expand Down Expand Up @@ -1000,6 +1000,12 @@ impl Manifest {

/// Embed a signed manifest into a stream using a supplied signer.
/// returns the bytes of the manifest that was embedded
#[async_generic(async_signature(
&mut self,
format: &str,
asset: &[u8],
signer: &dyn AsyncSigner,
))]
pub fn embed_from_memory(
&mut self,
format: &str,
Expand All @@ -1011,7 +1017,12 @@ impl Manifest {
let asset = asset.to_vec();
let mut stream = std::io::Cursor::new(asset);
let mut output_stream = Cursor::new(Vec::new());
self.embed_to_stream(format, &mut stream, &mut output_stream, signer)?;
if _sync {
self.embed_to_stream(format, &mut stream, &mut output_stream, signer)?;
} else {
self.embed_to_stream_async(format, &mut stream, &mut output_stream, signer)
.await?;
}
Ok(output_stream.into_inner())
}

Expand All @@ -1037,6 +1048,13 @@ impl Manifest {
/// Embed a signed manifest into a stream using a supplied signer.
///
/// Returns the bytes of c2pa_manifest that was embedded.
#[async_generic(async_signature(
&mut self,
format: &str,
source: &mut dyn CAIRead,
dest: &mut dyn CAIReadWrite,
signer: &dyn AsyncSigner,
))]
pub fn embed_to_stream(
&mut self,
format: &str,
Expand Down Expand Up @@ -1064,7 +1082,13 @@ impl Manifest {
let mut store = self.to_store()?;

// sign and write our store to to the output image file
store.save_to_stream(format, source, dest, signer)
if _sync {
store.save_to_stream(format, source, dest, signer)
} else {
store
.save_to_stream_async(format, source, dest, signer)
.await
}
}

/// Embed a signed manifest into a stream using a supplied signer.
Expand Down Expand Up @@ -1914,6 +1938,45 @@ pub(crate) mod tests {
//println!("{manifest_store}");main
}

#[cfg(any(target_arch = "wasm32", feature = "openssl_sign"))]
#[cfg_attr(feature = "openssl_sign", actix::test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
async fn test_embed_from_memory_async() {
use crate::{assertions::User, utils::test::temp_async_signer};
let image = include_bytes!("../tests/fixtures/earth_apollo17.jpg");
// convert buffer to cursor with Read/Write/Seek capability
let mut stream = std::io::Cursor::new(image.to_vec());
// let mut image = image.to_vec();
// let mut stream = std::io::Cursor::new(image.as_mut_slice());

let mut manifest = Manifest::new("my_app".to_owned());
manifest.set_title("EmbedStream");
manifest
.add_assertion(&User::new(
"org.contentauth.mylabel",
r#"{"my_tag":"Anything I want"}"#,
))
.unwrap();

let signer = temp_async_signer();
let mut output = Cursor::new(Vec::new());
// Embed a manifest using the signer.
manifest
.embed_to_stream_async("jpeg", &mut stream, &mut output, signer.as_ref())
.await
.expect("embed_stream");

let manifest_store = crate::ManifestStore::from_bytes("jpeg", &output.into_inner(), true)
.expect("from_bytes");
assert_eq!(
manifest_store.get_active().unwrap().title().unwrap(),
"EmbedStream"
);
#[cfg(feature = "add_thumbnails")]
assert!(manifest_store.get_active().unwrap().thumbnail().is_some());
//println!("{manifest_store}");main
}

#[cfg(feature = "file_io")]
#[actix::test]
/// Verify that an ingredient with error is reported on the ingredient and not on the manifest_store
Expand Down
34 changes: 26 additions & 8 deletions sdk/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ pub trait Signer {
None
}

fn timestamp_request_body(&self, message: &[u8]) -> Result<Vec<u8>> {
crate::time_stamp::default_rfc3161_message(message)
}

/// Request RFC 3161 timestamp to be included in the manifest data
/// structure.
///
Expand All @@ -51,10 +55,15 @@ pub trait Signer {
/// provided by [`Self::time_authority_url()`], if any.
#[cfg(not(target_arch = "wasm32"))]
fn send_timestamp_request(&self, message: &[u8]) -> Option<Result<Vec<u8>>> {
let headers: Option<Vec<(String, String)>> = self.timestamp_request_headers();

self.time_authority_url()
.map(|url| crate::time_stamp::default_rfc3161_request(&url, headers, message))
if let Some(url) = self.time_authority_url() {
if let Ok(body) = self.timestamp_request_body(message) {
let headers: Option<Vec<(String, String)>> = self.timestamp_request_headers();
return Some(crate::time_stamp::default_rfc3161_request(
&url, headers, &body, message,
));
}
}
None
}
#[cfg(target_arch = "wasm32")]
fn send_timestamp_request(&self, _message: &[u8]) -> Option<Result<Vec<u8>>> {
Expand Down Expand Up @@ -134,6 +143,10 @@ pub trait AsyncSigner: Sync {
None
}

fn timestamp_request_body(&self, message: &[u8]) -> Result<Vec<u8>> {
crate::time_stamp::default_rfc3161_message(message)
}

/// Request RFC 3161 timestamp to be included in the manifest data
/// structure.
///
Expand All @@ -145,10 +158,15 @@ pub trait AsyncSigner: Sync {
async fn send_timestamp_request(&self, message: &[u8]) -> Option<Result<Vec<u8>>> {
// NOTE: This is currently synchronous, but may become
// async in the future.
let headers: Option<Vec<(String, String)>> = self.timestamp_request_headers();

self.time_authority_url()
.map(|url| crate::time_stamp::default_rfc3161_request(&url, headers, message))
if let Some(url) = self.time_authority_url() {
if let Ok(body) = self.timestamp_request_body(message) {
let headers: Option<Vec<(String, String)>> = self.timestamp_request_headers();
return Some(crate::time_stamp::default_rfc3161_request(
&url, headers, &body, message,
));
}
}
None
}
#[cfg(target_arch = "wasm32")]
async fn send_timestamp_request(&self, message: &[u8]) -> Option<Result<Vec<u8>>>;
Expand Down
Loading

0 comments on commit 5160b6f

Please sign in to comment.