Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add WASI support to cawg_identity #942

Merged
merged 2 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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