Skip to content

Commit

Permalink
feat: Add WASI support to cawg_identity (#942)
Browse files Browse the repository at this point in the history
* feat: Add WASI support to cawg_identity


---------

Co-authored-by: Eric Scouten <[email protected]>
  • Loading branch information
cdmurph32 and scouten-adobe authored Feb 25, 2025
1 parent d9d2526 commit 753f4a0
Show file tree
Hide file tree
Showing 18 changed files with 151 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ jobs:
CC: /opt/wasi-sdk/bin/clang
WASI_SDK_PATH: /opt/wasi-sdk
RUST_MIN_STACK: 16777216
run: cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto --all-features
run: cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto -p cawg-identity --all-features

test-direct-minimal-versions:
name: Unit tests with minimum versions of direct dependencies
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test-wasi:
ifeq ($(PLATFORM),mac)
$(eval CC := /opt/homebrew/opt/llvm/bin/clang)
endif
CC=$(CC) CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime -S cli -S http --dir ." cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto --all-features
CC=$(CC) CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime -S cli -S http --dir ." cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto -p cawg-identity --all-features
rm -r sdk/Users

# Full local validation, build and test all features including wasm
Expand Down
13 changes: 9 additions & 4 deletions cawg_identity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[features]
v1_api = ["c2pa/v1_api"]
v1_api = ["c2pa/v1_api", "c2pa/file_io"]

[dependencies]
async-trait = "0.1.78"
Expand All @@ -44,15 +44,20 @@ non-empty-string = { version = "=0.2.4", features = ["serde"] }
nonempty-collections = { version = "0.2.9", features = ["serde"] }
rand = "0.8.5"
regex = "1.11"
reqwest = { version = "0.12.8", default-features = false, features = ["rustls-tls"] }
serde = { version = "1.0.197", features = ["derive"] }
serde_bytes = "0.11.14"
serde_json = "1.0.117"
static-iref = "3.0"
thiserror = "1.0.61"
zeroize = { version = "1.8", features = ["zeroize_derive"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
[target.'cfg(target_os = "wasi")'.dependencies]
wstd = "0.5"

[target.'cfg(not(target_os = "wasi"))'.dependencies]
reqwest = { version = "0.12.8", default-features = false, features = ["rustls-tls"] }

[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies]
wasm-bindgen = "0.2.95"

[dev-dependencies]
Expand All @@ -62,5 +67,5 @@ serde = { version = "1.0.197", features = ["derive"] }
httpmock = "0.7.0"
tokio = { version = "1.42", features = ["macros", "rt"] }

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies]
wasm-bindgen-test = "0.3.31"
2 changes: 2 additions & 0 deletions cawg_identity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ The toolkit has been tested on the following operating systems:

* Ubuntu Linux on x86 and ARM v8 (aarch64)

* WebAssembly System Interface (WASI)

## License

The `cawg-identity` crate is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
Expand Down
77 changes: 34 additions & 43 deletions cawg_identity/src/claim_aggregation/w3c_vc/did_web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ thread_local! {
pub(crate) static PROXY: RefCell<Option<String>> = const { RefCell::new(None) };
}

// #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_os = "wasi"))]
use reqwest::Error as HttpError;
// #[cfg(target_arch = "wasm32")]
// use String as HttpError;
#[cfg(target_os = "wasi")]
use String as HttpError;

#[derive(Debug, thiserror::Error)]
pub enum DidWebError {
Expand Down Expand Up @@ -81,7 +81,7 @@ pub(crate) async fn resolve(did: &Did<'_>) -> Result<DidDocument, DidWebError> {
}

async fn get_did_doc(url: &str) -> Result<Vec<u8>, DidWebError> {
// #[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_os = "wasi"))]
{
use reqwest::header;

Expand Down Expand Up @@ -116,45 +116,36 @@ async fn get_did_doc(url: &str) -> Result<Vec<u8>, DidWebError> {
Ok(document.to_vec())
}

// #[cfg(target_arch = "wasm32")]
// {
// use wasm_bindgen::prelude::*;
// use wasm_bindgen_futures::JsFuture;
// use web_sys::{Request, RequestInit, RequestMode, Response};

// let opts = RequestInit::new();
// opts.set_method("GET");
// opts.set_mode(RequestMode::Cors);

// let request = Request::new_with_str_and_init(&url, &opts)
// .map_err(|_|
// DidWebError::Client("Request::new_with_str_and_init".to_string()))?;

// request
// .headers()
// .set("accept", "application/did+json")
// .map_err(|_| DidWebError::Client("Set headers".to_string()))?;

// let window = web_sys::window().unwrap();
// let resp_value = JsFuture::from(window.fetch_with_request(&request))
// .await
// .map_err(|_|
// DidWebError::Client("window.fetch_with_request".to_string()))?;

// assert!(resp_value.is_instance_of::<Response>());
// let resp: Response = resp_value.dyn_into().unwrap();

// JsFuture::from(
// resp.blob()
// .map_err(|_|
// DidWebError::Client("window.resp.bytes()".to_string()))?, )
// .await
// .map(|blob| {
// let array = js_sys::Uint8Array::new(&blob);
// array.to_vec()
// })
// .map_err(|e| DidWebError::Server("resp.blob()".to_string()))
// }
#[cfg(target_os = "wasi")]
{
use wstd::{http, io, io::AsyncRead};

let request = http::Request::get(url)
.header("User-Agent", http::HeaderValue::from_static(USER_AGENT))
.header(
"Accept",
http::HeaderValue::from_static("application/did+json"),
)
.body(io::empty())
.map_err(|e| DidWebError::Request(url.to_owned(), e.to_string()))?;
let resp = http::Client::new()
.send(request)
.await
.map_err(|e| DidWebError::Request(url.to_owned(), e.to_string()))?;

let (parts, mut body) = resp.into_parts();
match parts.status {
http::StatusCode::OK => (),
http::StatusCode::NOT_FOUND => return Err(DidWebError::NotFound(url.to_string())),
_ => return Err(DidWebError::Server(parts.status.to_string())),
};

let mut document = Vec::new();
body.read_to_end(&mut document)
.await
.map_err(|e| DidWebError::Response(e.to_string()))?;
Ok(document)
}
}

pub(crate) fn to_url(did: &str) -> Result<String, DidWebError> {
Expand Down
7 changes: 5 additions & 2 deletions cawg_identity/src/tests/builder/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
// specific language governing permissions and limitations under
// each license.

#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use crate::builder::IdentityBuilderError;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn impl_from_ciborium_err() {
let ciborium_err: ciborium::ser::Error<String> = ciborium::ser::Error::Value("foo".to_string());
let builder_err: IdentityBuilderError = ciborium_err.into();
Expand Down
14 changes: 11 additions & 3 deletions cawg_identity/src/tests/builder/simple_case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::io::{Cursor, Seek};

use c2pa::{Builder, Reader, SigningAlg};
use serde_json::json;
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use crate::{
Expand All @@ -31,7 +31,11 @@ const TEST_IMAGE: &[u8] = include_bytes!("../../../../sdk/tests/fixtures/CA.jpg"
const TEST_THUMBNAIL: &[u8] = include_bytes!("../../../../sdk/tests/fixtures/thumbnail.jpg");

#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
#[cfg_attr(target_os = "wasi", wstd::test)]
async fn simple_case() {
// NOTE: This needs to be async for now because the verification side is
// async-only.
Expand Down Expand Up @@ -84,7 +88,11 @@ async fn simple_case() {
}

#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
#[cfg_attr(target_os = "wasi", wstd::test)]
async fn simple_case_async() {
let format = "image/jpeg";
let mut source = Cursor::new(TEST_IMAGE);
Expand Down
14 changes: 11 additions & 3 deletions cawg_identity/src/tests/claim_aggregation/interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use c2pa::{HashedUri, Reader};
use chrono::{DateTime, FixedOffset};
use iref::UriBuf;
use non_empty_string::NonEmptyString;
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use crate::{
Expand All @@ -26,7 +26,11 @@ use crate::{
};

#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
#[cfg_attr(target_os = "wasi", wstd::test)]
async fn adobe_connected_identities() {
let format = "image/jpeg";
let test_image = include_bytes!("../fixtures/claim_aggregation/adobe_connected_identities.jpg");
Expand Down Expand Up @@ -102,7 +106,11 @@ async fn adobe_connected_identities() {
}

#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
#[cfg_attr(target_os = "wasi", wstd::test)]
async fn ims_multiple_manifests() {
let format = "image/jpeg";
let test_image = include_bytes!("../fixtures/claim_aggregation/ims_multiple_manifests.jpg");
Expand Down
24 changes: 18 additions & 6 deletions cawg_identity/src/tests/claim_aggregation/w3c_vc/did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
// each license.

mod new {
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use crate::claim_aggregation::w3c_vc::did::Did;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn valid_dids() {
let did = Did::new("did:method:foo").unwrap();
assert_eq!(did.method_name(), "method");
Expand All @@ -45,7 +48,10 @@ mod new {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn err_invalid_did() {
Did::new("http:a:b").unwrap_err();
Did::new("did::b").unwrap_err();
Expand All @@ -54,13 +60,16 @@ mod new {
}

mod split_fragment {
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use crate::claim_aggregation::w3c_vc::did::Did;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn has_fragment() {
let did = Did::new("did:method:foo#bar").unwrap();
assert_eq!(did.method_name(), "method");
Expand All @@ -72,7 +81,10 @@ mod split_fragment {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn no_fragment() {
let did = Did::new("did:method:foo").unwrap();
let did2 = Did::new("did:method:foo").unwrap();
Expand Down
29 changes: 22 additions & 7 deletions cawg_identity/src/tests/claim_aggregation/w3c_vc/did_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
// each license.

mod new {
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use crate::claim_aggregation::w3c_vc::did::DidBuf;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn valid_dids() {
let did = DidBuf::new("did:method:foo".to_string()).unwrap();
let did = did.as_did();
Expand All @@ -49,7 +52,10 @@ mod new {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn err_invalid_did() {
DidBuf::new("http:a:b".to_string()).unwrap_err();
DidBuf::new("did::b".to_string()).unwrap_err();
Expand All @@ -58,7 +64,7 @@ mod new {
}

mod impl_serde {
#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;

use crate::claim_aggregation::w3c_vc::did::DidBuf;
Expand All @@ -72,7 +78,10 @@ mod impl_serde {
const SAMPLE_WITH_BAD_DID: &str = r#"{"did": "did::b"}"#;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn from_json() {
let s: Sample = serde_json::from_str(SAMPLE_WITH_DID).unwrap();
let did = s.did;
Expand All @@ -82,14 +91,20 @@ mod impl_serde {
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
#[should_panic]
fn from_json_err_invalid_did() {
let _: Sample = serde_json::from_str(SAMPLE_WITH_BAD_DID).unwrap();
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn to_json() {
let s = Sample {
did: DidBuf::new("did:method:foo".to_string()).unwrap(),
Expand Down
Loading

0 comments on commit 753f4a0

Please sign in to comment.