From 3e515bd8b260602eaf88fe098c7bfef22abf06ba Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Thu, 31 Aug 2023 14:27:18 -0400 Subject: [PATCH 1/8] feat: Add wasi compatibility. - wasip2 will require +nightly until https://github.com/rust-lang/rust/issues/130323 is resolved and/or std::os::wasip2 is available in stable. - Support was added to rustix for version 0.38.39 https://github.com/bytecodealliance/rustix/pull/1205 - Support was added to tempfile for version 3.14 https://github.com/Stebalien/tempfile/pull/305 --- make_test_images/Cargo.toml | 2 +- sdk/Cargo.toml | 37 +- sdk/src/asset_handlers/jpeg_io.rs | 7 +- sdk/src/builder.rs | 1 + sdk/src/ingredient.rs | 1 + sdk/src/manifest.rs | 1 + sdk/src/manifest_store.rs | 1 + sdk/src/utils/test.rs | 2 +- sdk/src/wasm/wasicrypto_validator.rs | 439 +++++++++++++++++ sdk/src/wasm/wasipki_trust_handler.rs | 672 ++++++++++++++++++++++++++ 10 files changed, 1147 insertions(+), 16 deletions(-) create mode 100644 sdk/src/wasm/wasicrypto_validator.rs create mode 100644 sdk/src/wasm/wasipki_trust_handler.rs diff --git a/make_test_images/Cargo.toml b/make_test_images/Cargo.toml index 275a3ff32..72d290463 100644 --- a/make_test_images/Cargo.toml +++ b/make_test_images/Cargo.toml @@ -28,7 +28,7 @@ nom = "7.1.3" regex = "1.5.6" serde = "1.0.197" serde_json = { version = "1.0.117", features = ["preserve_order"] } -tempfile = "3.10.1" +tempfile = "3.15.0" [features] # prevents these features from being always enabled in the workspace diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 6e8d27b5c..983ce27ae 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -74,10 +74,7 @@ byteorder = { version = "1.4.3", default-features = false } byteordered = "0.6.0" c2pa-crypto = { path = "../internal/crypto", version = "0.6.2" } c2pa-status-tracker = { path = "../internal/status-tracker", version = "0.5.0" } -chrono = { version = "0.4.39", default-features = false, features = [ - "serde", - "wasmbind", -] } +chrono = { version = "0.4.39", default-features = false, features = ["serde"] } ciborium = "0.2.2" config = { version = "0.14.0", default-features = false, features = [ "json", @@ -119,30 +116,46 @@ serde_with = "3.11.0" serde-transcode = "1.1.1" sha1 = "0.10.6" sha2 = "0.10.6" -tempfile = "3.10.1" +tempfile = "3.15" thiserror = "2.0.8" treeline = "0.1.0" url = "2.5.3" -uuid = { version = "1.10.0", features = ["serde", "v4", "js"] } +uuid = { version = "1.10.0", features = ["serde", "v4"] } +x509-certificate = "0.23.1" x509-parser = "0.16.0" -x509-certificate = "0.21.0" zip = { version = "2.2.1", default-features = false } +[target.'cfg(target_arch = "wasm32")'.dependencies] +rsa = { version = "0.9.6", features = ["sha2"] } +spki = "0.7.3" + +[target.'cfg(target_env = "p2")'.dependencies] +tempfile = { version = "3.15", features = ["nightly"] } + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] ureq = "2.4.0" + +[target.'cfg(any(target_os = "wasi", not(target_arch = "wasm32")))'.dependencies] image = { version = "0.24.7", default-features = false, features = [ "jpeg", "png", ], optional = true } -[target.'cfg(target_arch = "wasm32")'.dependencies] +[target.'cfg(target_os = "wasi")'.dependencies] +getrandom = "0.2.7" + + +[target.'cfg(all(target_arch = "wasm32",not(target_os = "wasi")))'.dependencies] +chrono = { version = "0.4.39", default-features = false, features = [ + "serde", + "wasmbind", +] } console_log = { version = "1.0.0", features = ["color"] } getrandom = { version = "0.2.7", features = ["js"] } js-sys = "0.3.58" rand_core = "0.9.0-alpha.2" -rsa = { version = "0.9.6", features = ["sha2"] } serde-wasm-bindgen = "0.6.5" -spki = "0.7.3" +uuid = { version = "1.10.0", features = ["serde", "v4", "js"] } wasm-bindgen = "0.2.83" wasm-bindgen-futures = "0.4.31" web-sys = { version = "0.3.58", features = [ @@ -160,8 +173,8 @@ hex-literal = "0.4.1" jumbf = "0.4.0" mockall = "0.13.1" -[target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = "0.3.31" +[target.'cfg(all(target_arch = "wasm32",not(target_os = "wasi")))'.dev-dependencies] +wasm-bindgen-test = "0.3.45" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] actix = "0.13.1" diff --git a/sdk/src/asset_handlers/jpeg_io.rs b/sdk/src/asset_handlers/jpeg_io.rs index b48b30531..078a794bf 100644 --- a/sdk/src/asset_handlers/jpeg_io.rs +++ b/sdk/src/asset_handlers/jpeg_io.rs @@ -1120,7 +1120,7 @@ pub mod tests { use std::io::{Read, Seek}; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; use super::*; @@ -1223,7 +1223,10 @@ pub mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] async fn test_xmp_read_write_stream() { let source_bytes = include_bytes!("../../tests/fixtures/CA.jpg"); diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index b71a200c6..e16fb2665 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1108,6 +1108,7 @@ impl Builder { mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] + #![cfg(not(target_os = "wasi"))] use std::io::Cursor; use c2pa_crypto::raw_signature::SigningAlg; diff --git a/sdk/src/ingredient.rs b/sdk/src/ingredient.rs index 947a6d8e0..10ca8d54f 100644 --- a/sdk/src/ingredient.rs +++ b/sdk/src/ingredient.rs @@ -1495,6 +1495,7 @@ impl IngredientOptions for DefaultOptions { mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] + #![cfg(not(target_os = wasi))] #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 89705023c..258bda766 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1546,6 +1546,7 @@ impl SignatureInfo { pub(crate) mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] + #![cfg(not(target_os = wasi))] use std::io::Cursor; diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index 15d2882d3..ce94a8020 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -599,6 +599,7 @@ impl std::fmt::Display for ManifestStore { mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] + #![cfg(not(target_os = wasi))] use c2pa_status_tracker::OneShotStatusTracker; #[cfg(target_arch = "wasm32")] diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 80a95e02c..6d5d71005 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -428,7 +428,7 @@ impl WebCryptoSigner { } } -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] #[async_trait::async_trait(?Send)] impl AsyncSigner for WebCryptoSigner { fn alg(&self) -> SigningAlg { diff --git a/sdk/src/wasm/wasicrypto_validator.rs b/sdk/src/wasm/wasicrypto_validator.rs new file mode 100644 index 000000000..adff88fc7 --- /dev/null +++ b/sdk/src/wasm/wasicrypto_validator.rs @@ -0,0 +1,439 @@ +// Copyright 2022 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use std::convert::TryFrom; + +use async_generic::async_generic; +use spki::{DecodePublicKey, SubjectPublicKeyInfoRef}; +use x509_parser::der_parser::ber::{parse_ber_sequence, BerObject}; + +use crate::{Error, Result, SigningAlg}; + +// Conversion utility from num-bigint::BigUint (used by x509_parser) +// to num-bigint-dig::BigUint (used by rsa) +fn biguint_val(ber_object: &BerObject) -> rsa::BigUint { + ber_object + .as_biguint() + .map(|x| x.to_u32_digits()) + .map(rsa::BigUint::new) + .unwrap_or_default() +} + +// Validate an Ed25519 signature for the provided data. The pkey must +// be the raw bytes representing CompressedEdwardsY. The length must 32 bytes. +fn ed25519_validate(sig: Vec, data: Vec, pkey: Vec) -> Result { + use ed25519_dalek::{Signature, Verifier, VerifyingKey, PUBLIC_KEY_LENGTH}; + + if pkey.len() == PUBLIC_KEY_LENGTH { + let ed_sig = Signature::from_slice(&sig).map_err(|_| Error::CoseInvalidCert)?; + + // convert to VerifyingKey + let mut cert_slice: [u8; 32] = Default::default(); + cert_slice.copy_from_slice(&pkey[0..PUBLIC_KEY_LENGTH]); + + let vk = VerifyingKey::from_bytes(&cert_slice).map_err(|_| Error::CoseInvalidCert)?; + + match vk.verify(&data, &ed_sig) { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + } else { + /* + web_sys::console::debug_2( + &"Ed25519 public key incorrect length: ".into(), + &pkey.len().to_string().into(), + ); + */ + Err(Error::CoseInvalidCert) + } +} + +pub(crate) fn validate_signature( + algo: String, + hash: String, + _salt_len: u32, + pkey: Vec, + sig: Vec, + data: Vec, +) -> Result { + use rsa::{ + sha2::{Sha256, Sha384, Sha512}, + RsaPublicKey, + }; + + match algo.as_ref() { + "RSASSA-PKCS1-v1_5" => { + use rsa::{pkcs1v15::Signature, signature::Verifier}; + + // used for certificate validation + let spki = SubjectPublicKeyInfoRef::try_from(pkey.as_ref()) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + + let (_, seq) = parse_ber_sequence(spki.subject_public_key.raw_bytes()) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + + let modulus = biguint_val(&seq[0]); + let exp = biguint_val(&seq[1]); + let public_key = RsaPublicKey::new(modulus, exp) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + let normalized_hash = hash.clone().replace("-", "").to_lowercase(); + + let result = match normalized_hash.as_ref() { + "sha256" => { + let vk = rsa::pkcs1v15::VerifyingKey::::new(public_key); + let signature: Signature = sig.as_slice().try_into().map_err(|_e| { + Error::WasmRsaKeyImport("could no process RSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + "sha384" => { + let vk = rsa::pkcs1v15::VerifyingKey::::new(public_key); + let signature: Signature = sig.as_slice().try_into().map_err(|_e| { + Error::WasmRsaKeyImport("could no process RSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + "sha512" => { + let vk = rsa::pkcs1v15::VerifyingKey::::new(public_key); + let signature: Signature = sig.as_slice().try_into().map_err(|_e| { + Error::WasmRsaKeyImport("could no process RSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + _ => return Err(Error::UnknownAlgorithm), + }; + + match result { + Ok(()) => { + //web_sys::console::debug_1(&"RSA validation success:".into()); + Ok(true) + } + Err(err) => { + /* + web_sys::console::debug_2( + &"RSA validation failed:".into(), + &err.to_string().into(), + ); + */ + Ok(false) + } + } + } + "RSA-PSS" => { + use rsa::{pss::Signature, signature::Verifier}; + + let spki = SubjectPublicKeyInfoRef::try_from(pkey.as_ref()) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + + let (_, seq) = parse_ber_sequence(&spki.subject_public_key.raw_bytes()) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + + // We need to normalize this from SHA-256 (the format WebCrypto uses) to sha256 + // (the format the util function expects) so that it maps correctly + let normalized_hash = hash.clone().replace("-", "").to_lowercase(); + let modulus = biguint_val(&seq[0]); + let exp = biguint_val(&seq[1]); + let public_key = RsaPublicKey::new(modulus, exp) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + + let result = match normalized_hash.as_ref() { + "sha256" => { + let vk = rsa::pss::VerifyingKey::::new(public_key); + let signature: Signature = sig.as_slice().try_into().map_err(|_e| { + Error::WasmRsaKeyImport("could no process RSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + "sha384" => { + let vk = rsa::pss::VerifyingKey::::new(public_key); + let signature: Signature = sig.as_slice().try_into().map_err(|_e| { + Error::WasmRsaKeyImport("could no process RSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + "sha512" => { + let vk = rsa::pss::VerifyingKey::::new(public_key); + let signature: Signature = sig.as_slice().try_into().map_err(|_e| { + Error::WasmRsaKeyImport("could no process RSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + _ => return Err(Error::UnknownAlgorithm), + }; + + match result { + Ok(()) => { + //web_sys::console::debug_1(&"RSA-PSS validation success:".into()); + Ok(true) + } + Err(err) => { + /* + web_sys::console::debug_2( + &"RSA-PSS validation failed:".into(), + &err.to_string().into(), + ); + */ + Ok(false) + } + } + } + "ECDSA" => { + use ecdsa::{signature::Verifier as EcdsaVerifier, Signature as EcdsaSignature}; + use p256::ecdsa::VerifyingKey as P256VerifyingKey; + use p384::ecdsa::VerifyingKey as P384VerifyingKey; + let result = match hash.as_ref() { + "SHA-256" => { + let vk = P256VerifyingKey::from_public_key_der(&pkey) + .map_err(|_| Error::WasmRsaKeyImport("Invalid P-256 key".to_string()))?; + let signature = EcdsaSignature::from_slice(&sig).map_err(|_| { + Error::WasmRsaKeyImport("Invalid ECDSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + "SHA-384" => { + let vk = P384VerifyingKey::from_public_key_der(&pkey) + .map_err(|_| Error::WasmRsaKeyImport("Invalid P-384 key".to_string()))?; + let signature = EcdsaSignature::from_slice(&sig).map_err(|_| { + Error::WasmRsaKeyImport("Invalid ECDSA signature".to_string()) + })?; + vk.verify(&data, &signature) + } + _ => return Err(Error::UnknownAlgorithm), + }; + + match result { + Ok(_) => Ok(true), + Err(err) => { + /* + web_sys::console::debug_2( + &"ECDSA validation failed:".into(), + &err.to_string().into(), + ); + */ + Ok(false) + } + } + } + "ED25519" => { + use x509_parser::{prelude::*, public_key::PublicKey}; + + // pull out raw Ed code points + if let Ok((_, certificate_public_key)) = SubjectPublicKeyInfo::from_der(&pkey) { + match certificate_public_key.parsed() { + Ok(key) => match key { + PublicKey::Unknown(raw_key) => { + ed25519_validate(sig, data, raw_key.to_vec()) + } + _ => Err(Error::OtherError( + "could not unwrap Ed25519 public key".into(), + )), + }, + Err(_) => Err(Error::OtherError( + "could not recognize Ed25519 public key".into(), + )), + } + } else { + Err(Error::OtherError( + "could not parse Ed25519 public key".into(), + )) + } + } + _ => Err(Error::UnsupportedType), + } +} + +// This interface is called from CoseValidator. RSA validation not supported here. +#[async_generic] +pub fn validate(alg: SigningAlg, sig: &[u8], data: &[u8], pkey: &[u8]) -> Result { + //web_sys::console::debug_2(&"Validating with algorithm".into(), &alg.to_string().into()); + + match alg { + SigningAlg::Ps256 => validate_signature( + "RSA-PSS".to_string(), + "SHA-256".to_string(), + 32, + pkey.to_vec(), + sig.to_vec(), + data.to_vec(), + ), + SigningAlg::Ps384 => validate_signature( + "RSA-PSS".to_string(), + "SHA-384".to_string(), + 48, + pkey.to_vec(), + sig.to_vec(), + data.to_vec(), + ), + SigningAlg::Ps512 => validate_signature( + "RSA-PSS".to_string(), + "SHA-512".to_string(), + 64, + pkey.to_vec(), + sig.to_vec(), + data.to_vec(), + ), + // "rs256" => { + // validate_signature( + // "RSASSA-PKCS1-v1_5".to_string(), + // "SHA-256".to_string(), + // 0, + // pkey.to_vec(), + // sig.to_vec(), + // data.to_vec(), + // ) + // .await + // } + // "rs384" => { + // validate_signature( + // "RSASSA-PKCS1-v1_5".to_string(), + // "SHA-384".to_string(), + // 0, + // pkey.to_vec(), + // sig.to_vec(), + // data.to_vec(), + // ) + // .await + // } + // "rs512" => { + // validate_signature( + // "RSASSA-PKCS1-v1_5".to_string(), + // "SHA-512".to_string(), + // 0, + // pkey.to_vec(), + // sig.to_vec(), + // data.to_vec(), + // ) + // .await + // } + SigningAlg::Es256 => validate_signature( + "ECDSA".to_string(), + "SHA-256".to_string(), + 0, + pkey.to_vec(), + sig.to_vec(), + data.to_vec(), + ), + SigningAlg::Es384 => validate_signature( + "ECDSA".to_string(), + "SHA-384".to_string(), + 0, + pkey.to_vec(), + sig.to_vec(), + data.to_vec(), + ), + SigningAlg::Es512 => validate_signature( + "ECDSA".to_string(), + "SHA-512".to_string(), + 0, + pkey.to_vec(), + sig.to_vec(), + data.to_vec(), + ), + SigningAlg::Ed25519 => validate_signature( + "ED25519".to_string(), + "SHA-512".to_string(), + 0, + pkey.to_vec(), + sig.to_vec(), + data.to_vec(), + ), + } +} + +#[cfg(test)] +pub mod tests { + #![allow(clippy::unwrap_used)] + + use super::*; + use crate::SigningAlg; + + async fn test_async_verify_rsa_pss() { + // PS signatures + let sig_bytes = include_bytes!("../../tests/fixtures/sig_ps256.data"); + let data_bytes = include_bytes!("../../tests/fixtures/data_ps256.data"); + let key_bytes = include_bytes!("../../tests/fixtures/key_ps256.data"); + + let validated = validate_async(SigningAlg::Ps256, sig_bytes, data_bytes, key_bytes) + .await + .unwrap(); + + assert_eq!(validated, true); + } + + async fn test_async_verify_ecdsa() { + // EC signatures + let sig_es384_bytes = include_bytes!("../../tests/fixtures/sig_es384.data"); + let data_es384_bytes = include_bytes!("../../tests/fixtures/data_es384.data"); + let key_es384_bytes = include_bytes!("../../tests/fixtures/key_es384.data"); + + let mut validated = validate_async( + SigningAlg::Es384, + sig_es384_bytes, + data_es384_bytes, + key_es384_bytes, + ) + .await + .unwrap(); + + assert_eq!(validated, true); + + let sig_es512_bytes = include_bytes!("../../tests/fixtures/sig_es512.data"); + let data_es512_bytes = include_bytes!("../../tests/fixtures/data_es512.data"); + let key_es512_bytes = include_bytes!("../../tests/fixtures/key_es512.data"); + + validated = validate_async( + SigningAlg::Es512, + sig_es512_bytes, + data_es512_bytes, + key_es512_bytes, + ) + .await + .unwrap(); + + assert_eq!(validated, true); + + let sig_es256_bytes = include_bytes!("../../tests/fixtures/sig_es256.data"); + let data_es256_bytes = include_bytes!("../../tests/fixtures/data_es256.data"); + let key_es256_bytes = include_bytes!("../../tests/fixtures/key_es256.data"); + + let validated = validate_async( + SigningAlg::Es256, + sig_es256_bytes, + data_es256_bytes, + key_es256_bytes, + ) + .await + .unwrap(); + + assert_eq!(validated, true); + } + + #[ignore] + async fn test_async_verify_bad() { + let sig_bytes = include_bytes!("../../tests/fixtures/sig_ps256.data"); + let data_bytes = include_bytes!("../../tests/fixtures/data_ps256.data"); + let key_bytes = include_bytes!("../../tests/fixtures/key_ps256.data"); + + let mut bad_bytes = data_bytes.to_vec(); + bad_bytes[0] = b'c'; + bad_bytes[1] = b'2'; + bad_bytes[2] = b'p'; + bad_bytes[3] = b'a'; + + let validated = validate_async(SigningAlg::Ps256, sig_bytes, &bad_bytes, key_bytes) + .await + .unwrap(); + + assert_eq!(validated, false); + } +} diff --git a/sdk/src/wasm/wasipki_trust_handler.rs b/sdk/src/wasm/wasipki_trust_handler.rs new file mode 100644 index 000000000..9c2743db0 --- /dev/null +++ b/sdk/src/wasm/wasipki_trust_handler.rs @@ -0,0 +1,672 @@ +// Copyright 2023 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use std::{ + collections::HashSet, + io::{BufRead, BufReader, Cursor, Read}, + str::FromStr, +}; + +use asn1_rs::{nom::AsBytes, Any, Class, Header, Tag}; +use async_generic::async_generic; +use x509_parser::{ + der_parser::der::{parse_der_integer, parse_der_sequence_of}, + prelude::*, +}; + +use crate::{ + cose_validator::*, + error::{Error, Result}, + hash_utils::{hash_sha256, vec_compare}, + trust_handler::{ + has_allowed_oid, load_eku_configuration, load_trust_from_data, TrustHandlerConfig, + }, + utils::base64, + wasm::wasicrypto_validator::validate_signature, + SigningAlg, +}; + +// Struct to handle verification of trust chains +pub(crate) struct WasiTrustHandlerConfig { + pub trust_anchors: Vec>, + pub private_anchors: Vec>, + allowed_cert_set: HashSet, + config_store: Vec, +} + +impl std::fmt::Debug for WasiTrustHandlerConfig { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "{} trust anchors, {} private anchors.", + self.trust_anchors.len(), + self.private_anchors.len() + ) + } +} + +impl WasiTrustHandlerConfig { + pub fn load_default_trust(&mut self) -> Result<()> { + // load config store + let config = include_bytes!("./store.cfg"); + let mut config_reader = Cursor::new(config); + self.load_configuration(&mut config_reader)?; + + // load debug/test private trust anchors + if cfg!(test) { + let pa = include_bytes!("./test_cert_root_bundle.pem"); + let mut pa_reader = Cursor::new(pa); + + self.append_private_trust_data(&mut pa_reader)?; + } + + Ok(()) + } +} + +impl TrustHandlerConfig for WasiTrustHandlerConfig { + fn new() -> Self { + let mut th = WasiTrustHandlerConfig { + trust_anchors: Vec::new(), + private_anchors: Vec::new(), + allowed_cert_set: HashSet::new(), + config_store: Vec::new(), + }; + + if th.load_default_trust().is_err() { + th.clear(); // just use empty trust handler to fail automatically + } + + th + } + + // add trust anchors + fn load_trust_anchors_from_data(&mut self, trust_data_reader: &mut dyn Read) -> Result<()> { + let mut trust_data = Vec::new(); + trust_data_reader.read_to_end(&mut trust_data)?; + + let mut anchors = load_trust_from_data(&trust_data)?; + self.trust_anchors.append(&mut anchors); + Ok(()) + } + + // append private trust anchors + fn append_private_trust_data(&mut self, private_anchors_reader: &mut dyn Read) -> Result<()> { + let mut private_anchors_data = Vec::new(); + private_anchors_reader.read_to_end(&mut private_anchors_data)?; + + let mut anchors = load_trust_from_data(&private_anchors_data)?; + self.private_anchors.append(&mut anchors); + + Ok(()) + } + + fn clear(&mut self) { + self.trust_anchors = Vec::new(); + self.private_anchors = Vec::new(); + } + + // load EKU configuration + fn load_configuration(&mut self, config_data: &mut dyn Read) -> Result<()> { + config_data.read_to_end(&mut self.config_store)?; + Ok(()) + } + + // list off auxillary allowed EKU Oid + fn get_auxillary_ekus(&self) -> Vec { + let mut oids = Vec::new(); + if let Ok(oid_strings) = load_eku_configuration(&mut Cursor::new(&self.config_store)) { + for oid_str in &oid_strings { + if let Ok(oid) = asn1_rs::Oid::from_str(oid_str) { + oids.push(oid); + } + } + } + oids + } + + fn get_anchors(&self) -> Vec> { + let mut anchors = Vec::new(); + + anchors.append(&mut self.trust_anchors.clone()); + anchors.append(&mut self.private_anchors.clone()); + + anchors + } + + // add allowed list entries + fn load_allowed_list(&mut self, allowed_list: &mut dyn Read) -> Result<()> { + let mut buffer = Vec::new(); + allowed_list.read_to_end(&mut buffer)?; + + if let Ok(cert_list) = load_trust_from_data(&buffer) { + for cert_der in &cert_list { + let cert_sha256 = hash_sha256(cert_der); + let cert_hash_base64 = base64::encode(&cert_sha256); + + self.allowed_cert_set.insert(cert_hash_base64); + } + } + + // try to load the of base64 encoded encoding of the sha256 hash of the certificate DER encoding + let reader = Cursor::new(buffer); + let buf_reader = BufReader::new(reader); + + let mut inside_cert_block = false; + for l in buf_reader.lines().flatten() { + if l.contains("-----BEGIN") { + inside_cert_block = true; + } + if l.contains("-----END") { + inside_cert_block = false; + } + + // sanity check that data is base64 encoded and outside of certificate block + if !inside_cert_block && base64::decode(&l).is_ok() && !l.is_empty() { + self.allowed_cert_set.insert(l); + } + } + + Ok(()) + } + + // set of allowed cert hashes + fn get_allowed_list(&self) -> &HashSet { + &self.allowed_cert_set + } +} + +fn find_allowed_eku<'a>( + cert_der: &'a [u8], + allowed_ekus: &'a [asn1_rs::Oid<'a>], +) -> Option<&'a asn1_rs::Oid<'a>> { + if let Ok((_rem, cert)) = X509Certificate::from_der(cert_der) { + if let Ok(Some(eku)) = cert.extended_key_usage() { + if let Some(o) = has_allowed_oid(eku.value, allowed_ekus) { + return Some(o); + } + } + } + None +} +fn cert_signing_alg(cert: &x509_parser::certificate::X509Certificate) -> Option { + let cert_alg = cert.signature_algorithm.algorithm.clone(); + + let signing_alg = if cert_alg == SHA256_WITH_RSAENCRYPTION_OID { + "rsa256".to_string() + } else if cert_alg == SHA384_WITH_RSAENCRYPTION_OID { + "rsa384".to_string() + } else if cert_alg == SHA512_WITH_RSAENCRYPTION_OID { + "rsa512".to_string() + } else if cert_alg == ECDSA_WITH_SHA256_OID { + SigningAlg::Es256.to_string() + } else if cert_alg == ECDSA_WITH_SHA384_OID { + SigningAlg::Es384.to_string() + } else if cert_alg == ECDSA_WITH_SHA512_OID { + SigningAlg::Es512.to_string() + } else if cert_alg == RSASSA_PSS_OID { + if let Some(parameters) = &cert.signature_algorithm.parameters { + let seq = match parameters.as_sequence() { + Ok(s) => s, + Err(_) => return None, + }; + + let (_i, (ha_alg, mgf_ai)) = match seq.parse(|i| { + let (i, h) =
::from_der(i)?; + if h.class() != Class::ContextSpecific || h.tag() != Tag(0) { + return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); + } + + let (i, ha_alg) = AlgorithmIdentifier::from_der(i) + .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; + + let (i, h) =
::from_der(i)?; + if h.class() != Class::ContextSpecific || h.tag() != Tag(1) { + return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); + } + + let (i, mgf_ai) = AlgorithmIdentifier::from_der(i) + .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; + + // Ignore anything that follows these two parameters. + + Ok((i, (ha_alg, mgf_ai))) + }) { + Ok((ii, (h, m))) => (ii, (h, m)), + Err(_) => return None, + }; + + let mgf_ai_parameters = match mgf_ai.parameters { + Some(m) => m, + None => return None, + }; + + let mgf_ai_parameters = match mgf_ai_parameters.as_sequence() { + Ok(m) => m, + Err(_) => return None, + }; + + let (_i, mgf_ai_params_algorithm) = + match ::from_der(&mgf_ai_parameters.content) { + Ok((i, m)) => (i, m), + Err(_) => return None, + }; + + let mgf_ai_params_algorithm = match mgf_ai_params_algorithm.as_oid() { + Ok(m) => m, + Err(_) => return None, + }; + + // must be the same + if ha_alg.algorithm.to_id_string() != mgf_ai_params_algorithm.to_id_string() { + return None; + } + + // check for one of the mandatory types + if ha_alg.algorithm == SHA256_OID { + "ps256".to_string() + } else if ha_alg.algorithm == SHA384_OID { + "ps384".to_string() + } else if ha_alg.algorithm == SHA512_OID { + "ps512".to_string() + } else { + return None; + } + } else { + return None; + } + } else if cert_alg == ED25519_OID { + SigningAlg::Ed25519.to_string() + } else { + return None; + }; + + Some(signing_alg) +} + +#[async_generic] +pub(crate) fn verify_data( + cert_der: Vec, + sig_alg: Option, + sig: Vec, + data: Vec, +) -> Result { + use x509_parser::prelude::*; + + let (_, cert) = + X509Certificate::from_der(cert_der.as_bytes()).map_err(|_e| Error::CoseCertUntrusted)?; + + let certificate_public_key = cert.public_key(); + + if let Some(cert_alg_string) = sig_alg { + let (algo, hash, salt_len) = match cert_alg_string.as_str() { + "rsa256" => ("RSASSA-PKCS1-v1_5".to_string(), "SHA-256".to_string(), 0), + "rsa384" => ("RSASSA-PKCS1-v1_5".to_string(), "SHA-384".to_string(), 0), + "rsa512" => ("RSASSA-PKCS1-v1_5".to_string(), "SHA-512".to_string(), 0), + "es256" => ("ECDSA".to_string(), "SHA-256".to_string().to_string(), 0), + "es384" => ("ECDSA".to_string(), "SHA-384".to_string(), 0), + "es512" => ("ECDSA".to_string(), "SHA-512".to_string(), 0), + "ps256" => ("RSA-PSS".to_string(), "SHA-256".to_string(), 32), + "ps384" => ("RSA-PSS".to_string(), "SHA-384".to_string(), 48), + "ps512" => ("RSA-PSS".to_string(), "SHA-512".to_string(), 64), + "ed25519" => ("ED25519".to_string(), "SHA-512".to_string(), 0), + _ => return Err(Error::UnsupportedType), + }; + + let adjusted_sig = if cert_alg_string.starts_with("es") { + let parsed_alg_string: SigningAlg = cert_alg_string + .parse() + .map_err(|_| Error::UnknownAlgorithm)?; + match der_to_p1363(&sig, parsed_alg_string) { + Some(p1363) => p1363, + None => sig, + } + } else { + sig + }; + + validate_signature( + algo, + hash, + salt_len, + certificate_public_key.raw.to_vec(), + adjusted_sig, + data, + ) + } else { + Err(Error::BadParam("unknown alg processing cert".to_string())) + } +} +// convert der signatures to P1363 format: r | s +fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Option> { + // handle if this is a der sequence + if let Ok((_, bo)) = parse_der_sequence_of(parse_der_integer)(data) { + let seq = bo.as_sequence().ok()?; + + if seq.len() != 2 { + return None; + } + + let rp = seq[0].as_bigint().ok()?; + let sp = seq[1].as_bigint().ok()?; + + let mut r = rp.to_str_radix(16); + let mut s = sp.to_str_radix(16); + + let sig_len: usize = match alg { + SigningAlg::Es256 => 64, + SigningAlg::Es384 => 96, + SigningAlg::Es512 => 132, + _ => return None, + }; + + // pad or truncate as needed + let rp = if r.len() > sig_len { + // truncate + let offset = r.len() - sig_len; + &r[offset..r.len()] + } else { + // pad + while r.len() != sig_len { + r.insert(0, '0'); + } + r.as_ref() + }; + + let sp = if s.len() > sig_len { + // truncate + let offset = s.len() - sig_len; + &s[offset..s.len()] + } else { + // pad + while s.len() != sig_len { + s.insert(0, '0'); + } + s.as_ref() + }; + + if rp.len() != sig_len || rp.len() != sp.len() { + return None; + } + + // merge r and s strings + let mut new_sig = rp.to_string(); + new_sig.push_str(sp); + + // convert back from hex string to byte array + let result = (0..new_sig.len()) + .step_by(2) + .map(|i| { + u8::from_str_radix(&new_sig[i..i + 2], 16) + .map_err(|_err| crate::Error::InvalidEcdsaSignature) + }) + .collect(); + + if let Ok(p1363) = result { + Some(p1363) + } else { + None + } + } else { + Some(data.to_vec()) + } +} + +fn check_chain_order(certs: &[Vec]) -> Result<()> { + use x509_parser::prelude::*; + + let chain_length = certs.len(); + if chain_length < 2 { + return Ok(()); + } + + for i in 1..chain_length { + let (_, current_cert) = + X509Certificate::from_der(&certs[i - 1]).map_err(|_e| Error::CoseCertUntrusted)?; + + let issuer_der = certs[i].to_vec(); + let data = current_cert.tbs_certificate.as_ref(); + let sig = current_cert.signature_value.as_ref(); + + let sig_alg = cert_signing_alg(¤t_cert); + + let result = verify_data(issuer_der, sig_alg, sig.to_vec(), data.to_vec()); + + // keep going as long as it validate + match result { + Ok(b) => { + if !b { + return Err(Error::OtherError("cert chain order invalid".into())); + } + } + Err(e) => return Err(e), + } + } + Ok(()) +} + +fn on_trust_list(th: &dyn TrustHandlerConfig, certs: &[Vec], ee_der: &[u8]) -> Result { + use x509_parser::prelude::*; + + // check the cert against the allowed list first + let cert_sha256 = hash_sha256(ee_der); + let cert_hash_base64 = base64::encode(&cert_sha256); + if th.get_allowed_list().contains(&cert_hash_base64) { + return Ok(true); + } + + // add ee cert if needed to the chain + let full_chain = if !certs.is_empty() && vec_compare(ee_der, &certs[0]) { + certs.to_vec() + } else { + let mut full_chain: Vec> = Vec::new(); + full_chain.push(ee_der.to_vec()); + let mut in_chain = certs.to_vec(); + full_chain.append(&mut in_chain); + full_chain + }; + + // make sure chain is in the correct order and valid + check_chain_order(&full_chain)?; + + // build anchors and check against trust anchors, + let mut anchors: Vec = Vec::new(); + let source_anchors = th.get_anchors(); + for anchor_der in &source_anchors { + let (_, anchor) = + X509Certificate::from_der(anchor_der).map_err(|_e| Error::CoseCertUntrusted)?; + anchors.push(anchor); + } + + if anchors.is_empty() { + return Ok(false); + } + + // work back from last cert in chain against the trust anchors + for cert in certs.iter().rev() { + let (_, chain_cert) = + X509Certificate::from_der(cert).map_err(|_e| Error::CoseCertUntrusted)?; + + for anchor in &source_anchors { + let data = chain_cert.tbs_certificate.as_ref(); + let sig = chain_cert.signature_value.as_ref(); + + let sig_alg = cert_signing_alg(&chain_cert); + + let (_, anchor_cert) = + X509Certificate::from_der(anchor).map_err(|_e| Error::CoseCertUntrusted)?; + + if chain_cert.issuer() == anchor_cert.subject() { + let result = verify_data(anchor.clone(), sig_alg, sig.to_vec(), data.to_vec()); + + match result { + Ok(b) => { + if b { + return Ok(true); + } + } + Err(_) => continue, + } + } + } + } + // todo: consider (path check and names restrictions) + + Ok(false) +} + +// verify certificate and trust chain +#[async_generic] +pub(crate) fn verify_trust( + th: &dyn TrustHandlerConfig, + chain_der: &[Vec], + cert_der: &[u8], + _signing_time_epoc: Option, +) -> Result { + // check configured EKUs against end-entity cert + find_allowed_eku(cert_der, &th.get_auxillary_ekus()).ok_or(Error::CoseCertUntrusted)?; + + on_trust_list(th, chain_der, cert_der) +} + +#[cfg(test)] +pub mod tests { + #![allow(clippy::expect_used)] + #![allow(clippy::panic)] + #![allow(clippy::unwrap_used)] + + use super::*; + #[test] + async fn test_trust_store() { + let mut th = WasiTrustHandlerConfig::new(); + th.clear(); + + th.load_default_trust().unwrap(); + + // test all the certs + let ps256 = include_bytes!("../../tests/fixtures/certs/ps256.pub"); + let ps384 = include_bytes!("../../tests/fixtures/certs/ps384.pub"); + let ps512 = include_bytes!("../../tests/fixtures/certs/ps512.pub"); + let es256 = include_bytes!("../../tests/fixtures/certs/es256.pub"); + let es384 = include_bytes!("../../tests/fixtures/certs/es384.pub"); + let es512 = include_bytes!("../../tests/fixtures/certs/es512.pub"); + let ed25519 = include_bytes!("../../tests/fixtures/certs/ed25519.pub"); + + let ps256_certs = load_trust_from_data(ps256).unwrap(); + let ps384_certs = load_trust_from_data(ps384).unwrap(); + let ps512_certs = load_trust_from_data(ps512).unwrap(); + let es256_certs = load_trust_from_data(es256).unwrap(); + let es384_certs = load_trust_from_data(es384).unwrap(); + let es512_certs = load_trust_from_data(es512).unwrap(); + let ed25519_certs = load_trust_from_data(ed25519).unwrap(); + + assert!( + verify_trust_async(&th, &ps256_certs[1..], &ps256_certs[0], None) + .await + .unwrap() + ); + assert!( + verify_trust_async(&th, &ps384_certs[1..], &ps384_certs[0], None) + .await + .unwrap() + ); + assert!( + verify_trust_async(&th, &ps512_certs[1..], &ps512_certs[0], None) + .await + .unwrap() + ); + assert!( + verify_trust_async(&th, &es256_certs[1..], &es256_certs[0], None) + .await + .unwrap() + ); + + assert!( + verify_trust_async(&th, &es384_certs[1..], &es384_certs[0], None) + .await + .unwrap() + ); + assert!( + verify_trust_async(&th, &es512_certs[1..], &es512_certs[0], None) + .await + .unwrap() + ); + + assert!( + verify_trust_async(&th, &ed25519_certs[1..], &ed25519_certs[0], None) + .await + .unwrap() + ); + } + + #[test] + async fn test_broken_trust_chain() { + let mut th = WasiTrustHandlerConfig::new(); + th.clear(); + + th.load_default_trust().unwrap(); + + // test all the certs + let ps256 = include_bytes!("../../tests/fixtures/certs/ps256.pub"); + let ps384 = include_bytes!("../../tests/fixtures/certs/ps384.pub"); + let ps512 = include_bytes!("../../tests/fixtures/certs/ps512.pub"); + let es256 = include_bytes!("../../tests/fixtures/certs/es256.pub"); + let es384 = include_bytes!("../../tests/fixtures/certs/es384.pub"); + let es512 = include_bytes!("../../tests/fixtures/certs/es512.pub"); + let ed25519 = include_bytes!("../../tests/fixtures/certs/ed25519.pub"); + + let ps256_certs = load_trust_from_data(ps256).unwrap(); + let ps384_certs = load_trust_from_data(ps384).unwrap(); + let ps512_certs = load_trust_from_data(ps512).unwrap(); + let es256_certs = load_trust_from_data(es256).unwrap(); + let es384_certs = load_trust_from_data(es384).unwrap(); + let es512_certs = load_trust_from_data(es512).unwrap(); + let ed25519_certs = load_trust_from_data(ed25519).unwrap(); + + assert!( + !verify_trust_async(&th, &ps256_certs[2..], &ps256_certs[0], None) + .await + .unwrap() + ); + assert!( + !verify_trust_async(&th, &ps384_certs[2..], &ps384_certs[0], None) + .await + .unwrap() + ); + assert!( + !verify_trust_async(&th, &ps512_certs[2..], &ps512_certs[0], None) + .await + .unwrap() + ); + assert!( + !verify_trust_async(&th, &es256_certs[2..], &es256_certs[0], None) + .await + .unwrap() + ); + assert!( + !verify_trust_async(&th, &es384_certs[2..], &es384_certs[0], None) + .await + .unwrap() + ); + assert!( + !verify_trust_async(&th, &es512_certs[2..], &es512_certs[0], None) + .await + .unwrap() + ); + assert!( + !verify_trust_async(&th, &ed25519_certs[2..], &ed25519_certs[0], None) + .await + .unwrap() + ); + } +} From f73b64055e3a69b0ba5b76dee5e6a5708f0055e2 Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Thu, 23 Jan 2025 10:23:12 -0500 Subject: [PATCH 2/8] feat: Rebase on 0.41.0 Tests passing with WASI. CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime -S common --dir ." --- .github/workflows/ci.yml | 53 ++ .gitignore | 1 + internal/crypto/Cargo.toml | 19 +- sdk/src/asset_handlers/bmff_io.rs | 32 +- sdk/src/asset_handlers/jpeg_io.rs | 8 +- sdk/src/asset_handlers/mp3_io.rs | 16 +- sdk/src/asset_handlers/png_io.rs | 19 +- sdk/src/asset_handlers/riff_io.rs | 25 +- sdk/src/asset_handlers/svg_io.rs | 27 +- sdk/src/asset_handlers/tiff_io.rs | 28 +- sdk/src/builder.rs | 24 +- sdk/src/cose_sign.rs | 2 +- sdk/src/ingredient.rs | 7 +- sdk/src/manifest.rs | 38 +- sdk/src/manifest_store.rs | 22 +- sdk/src/utils/io_utils.rs | 33 +- sdk/src/utils/test.rs | 9 +- sdk/src/wasm/wasicrypto_validator.rs | 439 ----------------- sdk/src/wasm/wasipki_trust_handler.rs | 672 -------------------------- sdk/tests/test_builder.rs | 2 +- 20 files changed, 228 insertions(+), 1248 deletions(-) delete mode 100644 sdk/src/wasm/wasicrypto_validator.rs delete mode 100644 sdk/src/wasm/wasipki_trust_handler.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b993c518f..0185e5d49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -287,6 +287,59 @@ jobs: run: wasm-pack test --chrome --headless working-directory: ./cawg_identity + tests-wasi: + name: Unit tests (WASI) + if: | + github.event_name != 'pull_request' || + github.event.pull_request.author_association == 'COLLABORATOR' || + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.user.login == 'dependabot[bot]' || + contains(github.event.pull_request.labels.*.name, 'safe to test') + + runs-on: ubuntu-latest +# container: +# image: ghcr.io/webassembly/wasi-sdk + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # nightly required for testing until this issue is resolved: + # wasip2 target should not conditionally feature gate stdlib APIs rust-lang/rust#130323 https://github.com/rust-lang/rust/issues/130323 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Install wasmtime + run: | + curl https://wasmtime.dev/install.sh -sSf | bash + echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH + + - name: Install WASI SDK + run: | + if [ "${RUNNER_ARCH}" = "X64" ]; then + ARCH="x86_64"; + else + ARCH="${RUNNER_ARCH}"; + fi + wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-${ARCH}-${RUNNER_OS}.tar.gz + tar xvf wasi-sdk-25.0-${ARCH}-${RUNNER_OS}.tar.gz + ls wasi* + mv $(echo wasi-sdk-25.0-${ARCH}-${RUNNER_OS} | tr '[:upper:]' '[:lower:]') /opt/wasi-sdk + + - name: Add wasm32-wasip2 target + run: rustup target add --toolchain nightly wasm32-wasip2 + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Run WASI tests (c2pa-rs) + env: + CARGO_TARGET_WASM32_WASIP2_RUNNER: "wasmtime -S common --dir ." + 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 + test-direct-minimal-versions: name: Unit tests with minimum versions of direct dependencies if: | diff --git a/.gitignore b/.gitignore index a43edfdfa..035358b14 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target/ **/*.rs.bk +**/*.tmp* .DS_Store .idea diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 2d404c966..74da438be 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -78,7 +78,7 @@ sha2 = "0.10.6" spki = { version = "0.7.3", optional = true } thiserror = "2.0.8" web-time = "1.1" -x509-certificate = "0.21.0" +x509-certificate = "0.23.1" x509-parser = "0.16.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] @@ -89,15 +89,15 @@ url = "2.5.3" [package.metadata.cargo-udeps.ignore] normal = ["openssl"] # TEMPORARY: Remove after openssl transition complete. -[dependencies.chrono] +[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies.chrono] version = "0.4.39" default-features = false features = ["wasmbind"] -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.chrono] +[target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies.chrono] version = "0.4.39" default-features = false -features = ["now", "wasmbind"] +features = ["now"] [target.'cfg(target_arch = "wasm32")'.dependencies] async-trait = "0.1.77" @@ -108,6 +108,11 @@ num-bigint-dig = "0.8.4" pkcs1 = "0.7.5" rsa = { version = "0.9.7", features = ["pem", "sha2"] } spki = "0.7.3" + +[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] +getrandom = { version = "0.2.7", features = ["js"] } +js-sys = "0.3.58" +ring = { version = "0.17", features = ["wasm32_unknown_unknown_js"]} wasm-bindgen = "0.2.83" wasm-bindgen-futures = "0.4.31" web-sys = { version = "0.3.58", features = [ @@ -119,10 +124,6 @@ web-sys = { version = "0.3.58", features = [ "WorkerGlobalScope", ] } -[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] -getrandom = { version = "0.2.7", features = ["js"] } -js-sys = "0.3.58" - [dev-dependencies] const-oid = "0.9.6" der = "0.7.9" @@ -135,5 +136,5 @@ spki = "0.7.3" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] actix = "0.13.1" -[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" diff --git a/sdk/src/asset_handlers/bmff_io.rs b/sdk/src/asset_handlers/bmff_io.rs index 61cacb3e1..b5851f498 100644 --- a/sdk/src/asset_handlers/bmff_io.rs +++ b/sdk/src/asset_handlers/bmff_io.rs @@ -22,7 +22,6 @@ use std::{ use atree::{Arena, Token}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use conv::ValueFrom; -use tempfile::Builder; use crate::{ assertions::{BmffMerkleMap, ExclusionsMap}, @@ -33,7 +32,7 @@ use crate::{ error::{Error, Result}, utils::{ hash_utils::{vec_compare, HashRange}, - io_utils::{stream_len, ReaderUtils}, + io_utils::{stream_len, tempfile_builder, ReaderUtils}, xmp_inmemory_utils::{add_provenance, MIN_XMP}, }, }; @@ -1278,10 +1277,7 @@ impl AssetIO for BmffIO { .open(asset_path) .map_err(Error::IoError)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.write_cai(&mut input_stream, &mut temp_file, store_bytes)?; @@ -1300,10 +1296,7 @@ impl AssetIO for BmffIO { fn remove_cai_store(&self, asset_path: &Path) -> Result<()> { let mut input_file = std::fs::File::open(asset_path)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.remove_cai_store_from_stream(&mut input_file, &mut temp_file)?; @@ -1842,17 +1835,18 @@ impl RemoteRefEmbed for BmffIO { } } } + #[cfg(test)] pub mod tests { #![allow(clippy::expect_used)] #![allow(clippy::panic)] #![allow(clippy::unwrap_used)] - use tempfile::tempdir; - use super::*; - use crate::utils::test::{fixture_path, temp_dir_path}; - + use crate::utils::{ + io_utils::tempdirectory, + test::{fixture_path, temp_dir_path}, + }; #[cfg(all(feature = "openssl", feature = "file_io"))] #[test] fn test_read_mp4() { @@ -1878,7 +1872,7 @@ pub mod tests { let data = "some test data"; let source = fixture_path("video1.mp4"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "video1-out.mp4"); std::fs::copy(source, &output).unwrap(); @@ -1904,7 +1898,7 @@ pub mod tests { let source = fixture_path("video1.mp4"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "mp4_test.mp4"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -1928,7 +1922,7 @@ pub mod tests { let source = fixture_path("video1.mp4"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "mp4_test.mp4"); if let Ok(_size) = std::fs::copy(&source, &output) { @@ -1954,7 +1948,7 @@ pub mod tests { let source = fixture_path("video1.mp4"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "mp4_test.mp4"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -1981,7 +1975,7 @@ pub mod tests { fn test_remove_c2pa() { let source = fixture_path("video1.mp4"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "mp4_test.mp4"); std::fs::copy(source, &output).unwrap(); diff --git a/sdk/src/asset_handlers/jpeg_io.rs b/sdk/src/asset_handlers/jpeg_io.rs index 078a794bf..e24e8a9e8 100644 --- a/sdk/src/asset_handlers/jpeg_io.rs +++ b/sdk/src/asset_handlers/jpeg_io.rs @@ -1124,6 +1124,7 @@ pub mod tests { use wasm_bindgen_test::*; use super::*; + use crate::utils::io_utils::tempdirectory; #[test] fn test_extract_xmp() { let contents = Bytes::from_static(b"http://ns.adobe.com/xap/1.0/\0stuff"); @@ -1151,7 +1152,7 @@ pub mod tests { fn test_remove_c2pa() { let source = crate::utils::test::fixture_path("CA.jpg"); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = crate::utils::test::temp_dir_path(&temp_dir, "CA_test.jpg"); std::fs::copy(source, &output).unwrap(); @@ -1195,7 +1196,7 @@ pub mod tests { fn test_xmp_read_write() { let source = crate::utils::test::fixture_path("CA.jpg"); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = crate::utils::test::temp_dir_path(&temp_dir, "CA_test.jpg"); std::fs::copy(source, &output).unwrap(); @@ -1227,6 +1228,7 @@ pub mod tests { all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test )] + #[allow(unused)] // not run for WASI async fn test_xmp_read_write_stream() { let source_bytes = include_bytes!("../../tests/fixtures/CA.jpg"); @@ -1276,7 +1278,7 @@ pub mod tests { .unwrap(); let curr_manifest = jpeg_io.read_cai_store(&source).unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = crate::utils::test::temp_dir_path(&temp_dir, "CA_test.jpg"); std::fs::copy(source, &output).unwrap(); diff --git a/sdk/src/asset_handlers/mp3_io.rs b/sdk/src/asset_handlers/mp3_io.rs index f9c6d66bb..c37b4c11b 100644 --- a/sdk/src/asset_handlers/mp3_io.rs +++ b/sdk/src/asset_handlers/mp3_io.rs @@ -24,7 +24,6 @@ use id3::{ *, }; use memchr::memmem; -use tempfile::Builder; use crate::{ asset_io::{ @@ -34,7 +33,7 @@ use crate::{ }, error::{Error, Result}, utils::{ - io_utils::{stream_len, ReaderUtils}, + io_utils::{stream_len, tempfile_builder, ReaderUtils}, xmp_inmemory_utils::{self, MIN_XMP}, }, }; @@ -318,10 +317,7 @@ impl AssetIO for Mp3IO { .open(asset_path) .map_err(Error::IoError)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.write_cai(&mut input_stream, &mut temp_file, store_bytes)?; @@ -506,12 +502,12 @@ pub mod tests { #![allow(clippy::panic)] #![allow(clippy::unwrap_used)] - use tempfile::tempdir; use xmp_inmemory_utils::extract_provenance; use super::*; use crate::utils::{ hash_utils::vec_compare, + io_utils::tempdirectory, test::{fixture_path, temp_dir_path}, }; @@ -521,7 +517,7 @@ pub mod tests { let source = fixture_path("sample1.mp3"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1-mp3.mp3"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -544,7 +540,7 @@ pub mod tests { let source = fixture_path("sample1.mp3"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1-mp3.mp3"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -573,7 +569,7 @@ pub mod tests { fn test_remove_c2pa() { let source = fixture_path("sample1.mp3"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "sample1-mp3.mp3"); std::fs::copy(source, &output).unwrap(); diff --git a/sdk/src/asset_handlers/png_io.rs b/sdk/src/asset_handlers/png_io.rs index 3b9eae73e..590dfbadd 100644 --- a/sdk/src/asset_handlers/png_io.rs +++ b/sdk/src/asset_handlers/png_io.rs @@ -21,7 +21,6 @@ use byteorder::{BigEndian, ReadBytesExt}; use conv::ValueFrom; use png_pong::chunk::InternationalText; use serde_bytes::ByteBuf; -use tempfile::Builder; use crate::{ assertions::{BoxMap, C2PA_BOXHASH}, @@ -32,7 +31,7 @@ use crate::{ }, error::{Error, Result}, utils::{ - io_utils::ReaderUtils, + io_utils::{tempfile_builder, ReaderUtils}, xmp_inmemory_utils::{add_provenance, MIN_XMP}, }, }; @@ -479,10 +478,7 @@ impl AssetIO for PngIO { .open(asset_path) .map_err(Error::IoError)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.write_cai(&mut stream, &mut temp_file, store_bytes)?; @@ -793,7 +789,10 @@ pub mod tests { use memchr::memmem; use super::*; - use crate::utils::test::{self, temp_dir_path}; + use crate::utils::{ + io_utils::tempdirectory, + test::{self, temp_dir_path}, + }; #[test] fn test_png_xmp() { @@ -815,7 +814,7 @@ pub mod tests { let ap = test::fixture_path("libpng-test.png"); let mut source_stream = std::fs::File::open(ap).unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "out.png"); let mut output_stream = std::fs::OpenOptions::new() .read(true) @@ -981,7 +980,7 @@ pub mod tests { #[test] fn test_remove_c2pa() { let source = test::fixture_path("exp-test1.png"); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = test::temp_dir_path(&temp_dir, "exp-test1_tmp.png"); std::fs::copy(source, &output).unwrap(); @@ -1034,7 +1033,7 @@ pub mod tests { .unwrap(); let curr_manifest = png_io.read_cai_store(&source).unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = crate::utils::test::temp_dir_path(&temp_dir, "exp-test1-out.png"); std::fs::copy(source, &output).unwrap(); diff --git a/sdk/src/asset_handlers/riff_io.rs b/sdk/src/asset_handlers/riff_io.rs index b1e8dc711..1f1695a23 100644 --- a/sdk/src/asset_handlers/riff_io.rs +++ b/sdk/src/asset_handlers/riff_io.rs @@ -21,7 +21,6 @@ use std::{ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use conv::ValueFrom; use riff::*; -use tempfile::Builder; use crate::{ asset_io::{ @@ -31,7 +30,7 @@ use crate::{ }, error::{Error, Result}, utils::{ - io_utils::stream_len, + io_utils::{stream_len, tempfile_builder}, xmp_inmemory_utils::{add_provenance, MIN_XMP}, }, }; @@ -363,10 +362,7 @@ impl AssetIO for RiffIO { fn save_cai_store(&self, asset_path: &Path, store_bytes: &[u8]) -> Result<()> { let mut input_stream = File::open(asset_path)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.write_cai(&mut input_stream, &mut temp_file, store_bytes)?; @@ -632,11 +628,10 @@ pub mod tests { use std::panic; - use tempfile::tempdir; - use super::*; use crate::utils::{ hash_utils::vec_compare, + io_utils::tempdirectory, test::{fixture_path, temp_dir_path}, xmp_inmemory_utils::extract_provenance, }; @@ -647,7 +642,7 @@ pub mod tests { let source = fixture_path("sample1.wav"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1-wav.wav"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -685,7 +680,7 @@ pub mod tests { let mut source = File::open(fixture_path("sample1.wav")).unwrap(); let riff_io = RiffIO::new("wav"); - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1-wav.wav"); let mut output_stream = File::create(&output).unwrap(); @@ -706,7 +701,7 @@ pub mod tests { let source = fixture_path("sample1.wav"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1-wav.wav"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -735,7 +730,7 @@ pub mod tests { fn test_remove_c2pa() { let source = fixture_path("sample1.wav"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "sample1-wav.wav"); std::fs::copy(source, &output).unwrap(); @@ -767,7 +762,7 @@ pub mod tests { let source = fixture_path("test_xmp.webp"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "test_xmp.webp"); std::fs::copy(source, &output).unwrap(); @@ -803,7 +798,7 @@ pub mod tests { let source = fixture_path("test.webp"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "test.webp"); std::fs::copy(source, &output).unwrap(); @@ -839,7 +834,7 @@ pub mod tests { let source = fixture_path("test_lossless.webp"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "test_lossless.webp"); std::fs::copy(source, &output).unwrap(); diff --git a/sdk/src/asset_handlers/svg_io.rs b/sdk/src/asset_handlers/svg_io.rs index 3f0d7fe88..447250dbb 100644 --- a/sdk/src/asset_handlers/svg_io.rs +++ b/sdk/src/asset_handlers/svg_io.rs @@ -24,7 +24,6 @@ use quick_xml::{ events::{BytesText, Event}, Reader, Writer, }; -use tempfile::Builder; use crate::{ asset_io::{ @@ -43,7 +42,7 @@ use crate::{ }, error::{Error, Result}, utils::{ - io_utils::{patch_stream, stream_len, ReaderUtils}, + io_utils::{patch_stream, stream_len, tempfile_builder, ReaderUtils}, xmp_inmemory_utils::{self, MIN_XMP}, }, }; @@ -125,10 +124,7 @@ impl AssetIO for SvgIO { .open(asset_path) .map_err(Error::IoError)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.write_cai(&mut input_stream, &mut temp_file, store_bytes)?; @@ -149,10 +145,7 @@ impl AssetIO for SvgIO { fn remove_cai_store(&self, asset_path: &Path) -> Result<()> { let mut input_file = File::open(asset_path)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.remove_cai_store_from_stream(&mut input_file, &mut temp_file)?; @@ -752,12 +745,12 @@ pub mod tests { use std::io::Read; - use tempfile::tempdir; use xmp_inmemory_utils::extract_provenance; use super::*; use crate::utils::{ hash_utils::vec_compare, + io_utils::tempdirectory, test::{fixture_path, temp_dir_path}, }; @@ -767,7 +760,7 @@ pub mod tests { let source = fixture_path("sample1.svg"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1.svg"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -790,7 +783,7 @@ pub mod tests { let source = fixture_path("sample2.svg"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample2.svg"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -813,7 +806,7 @@ pub mod tests { let source = fixture_path("sample3.svg"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample3.svg"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -836,7 +829,7 @@ pub mod tests { let source = fixture_path("sample1.svg"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1.svg"); if let Ok(_size) = std::fs::copy(source, &output) { @@ -865,7 +858,7 @@ pub mod tests { fn test_remove_c2pa() { let source = fixture_path("sample4.svg"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "sample4.svg"); std::fs::copy(source, &output).unwrap(); @@ -886,7 +879,7 @@ pub mod tests { let source = fixture_path("sample1.svg"); let mut success = false; - if let Ok(temp_dir) = tempdir() { + if let Ok(temp_dir) = tempdirectory() { let output = temp_dir_path(&temp_dir, "sample1.svg"); if let Ok(_size) = std::fs::copy(source, &output) { diff --git a/sdk/src/asset_handlers/tiff_io.rs b/sdk/src/asset_handlers/tiff_io.rs index ba60370e3..c5b2cdb9b 100644 --- a/sdk/src/asset_handlers/tiff_io.rs +++ b/sdk/src/asset_handlers/tiff_io.rs @@ -23,7 +23,6 @@ use atree::{Arena, Token}; use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; use byteordered::{with_order, ByteOrdered, Endianness}; use conv::ValueFrom; -use tempfile::Builder; use crate::{ asset_io::{ @@ -33,7 +32,7 @@ use crate::{ }, error::{Error, Result}, utils::{ - io_utils::{safe_vec, stream_len, ReaderUtils}, + io_utils::{safe_vec, stream_len, tempfile_builder, ReaderUtils}, xmp_inmemory_utils::{add_provenance, MIN_XMP}, }, }; @@ -1369,10 +1368,7 @@ impl AssetIO for TiffIO { .open(asset_path) .map_err(Error::IoError)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.write_cai(&mut input_stream, &mut temp_file, store_bytes)?; @@ -1393,10 +1389,7 @@ impl AssetIO for TiffIO { fn remove_cai_store(&self, asset_path: &std::path::Path) -> Result<()> { let mut input_file = std::fs::File::open(asset_path)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.remove_cai_store_from_stream(&mut input_file, &mut temp_file)?; @@ -1637,18 +1630,15 @@ pub mod tests { use core::panic; - use tempfile::tempdir; - use super::*; - use crate::utils::test::temp_dir_path; - + use crate::utils::{io_utils::tempdirectory, test::temp_dir_path}; #[test] fn test_read_write_manifest() { let data = "some data"; let source = crate::utils::test::fixture_path("TUSCANY.TIF"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "test.tif"); std::fs::copy(source, &output).unwrap(); @@ -1670,7 +1660,7 @@ pub mod tests { let source = crate::utils::test::fixture_path("TUSCANY.TIF"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "test.tif"); std::fs::copy(source, &output).unwrap(); @@ -1696,7 +1686,7 @@ pub mod tests { let source = crate::utils::test::fixture_path("TUSCANY.TIF"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "test.tif"); std::fs::copy(source, &output).unwrap(); @@ -1728,7 +1718,7 @@ pub mod tests { let source = crate::utils::test::fixture_path("TUSCANY.TIF"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "test.tif"); std::fs::copy(source, &output).unwrap(); @@ -1810,7 +1800,7 @@ pub mod tests { let source = crate::utils::test::fixture_path("test.DNG"); //let source = crate::utils::test::fixture_path("sample1.dng"); - let temp_dir = tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "test.DNG"); std::fs::copy(&source, &output).unwrap(); diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index e16fb2665..c4bf0c6d0 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1108,16 +1108,18 @@ impl Builder { mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] - #![cfg(not(target_os = "wasi"))] use std::io::Cursor; use c2pa_crypto::raw_signature::SigningAlg; use serde_json::json; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; use super::*; - #[cfg(any(feature = "openssl_sign", target_arch = "wasm32"))] + #[cfg(any( + feature = "openssl_sign", + all(target_arch = "wasm32", not(target_os = "wasi")) + ))] use crate::{assertions::BoxHash, asset_handlers::jpeg_io::JpegIO}; use crate::{ hash_stream_by_alg, @@ -1126,7 +1128,7 @@ mod tests { Reader, }; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); fn parent_json() -> String { @@ -1330,6 +1332,7 @@ mod tests { builder.to_archive(&mut zipped).unwrap(); // write the zipped stream to a file for debugging + #[cfg(not(target_os = "wasi"))] // target directory is outside of sandbox std::fs::write("../target/test.zip", zipped.get_ref()).unwrap(); // unzip the manifest builder from the zipped stream @@ -1453,8 +1456,12 @@ mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] + #[cfg_attr(target_os = "wasi", allow(unused))] async fn test_builder_remote_sign() { let format = "image/jpeg"; let mut source = Cursor::new(TEST_IMAGE); @@ -1585,9 +1592,12 @@ mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] #[cfg(any( - target_arch = "wasm32", + all(target_arch = "wasm32", not(target_os = "wasi")), all(feature = "openssl_sign", feature = "file_io") ))] async fn test_builder_box_hashed_embeddable() { diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 7e53de81e..d991f8ecc 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -289,7 +289,7 @@ mod tests { } #[cfg(all(feature = "openssl_sign", feature = "file_io"))] - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] async fn test_sign_claim_async() { use c2pa_crypto::raw_signature::SigningAlg; diff --git a/sdk/src/ingredient.rs b/sdk/src/ingredient.rs index 10ca8d54f..a2f617d3b 100644 --- a/sdk/src/ingredient.rs +++ b/sdk/src/ingredient.rs @@ -1491,11 +1491,10 @@ impl IngredientOptions for DefaultOptions { } } -#[cfg(test)] +#[cfg(all(test, not(target_os = "wasi")))] mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] - #![cfg(not(target_os = wasi))] #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -1703,12 +1702,12 @@ mod tests_file_io { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; use super::*; use crate::utils::test::fixture_path; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); const NO_MANIFEST_JPEG: &str = "earth_apollo17.jpg"; diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 258bda766..2f4994b0f 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1546,7 +1546,6 @@ impl SignatureInfo { pub(crate) mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] - #![cfg(not(target_os = wasi))] use std::io::Cursor; @@ -1555,10 +1554,10 @@ pub(crate) mod tests { use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; #[cfg(feature = "file_io")] use tempfile::tempdir; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[allow(unused_imports)] @@ -1937,7 +1936,7 @@ pub(crate) mod tests { } #[cfg(all(feature = "file_io", feature = "openssl_sign"))] - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] #[allow(deprecated)] async fn test_embed_async_sign() { let temp_dir = tempdir().expect("temp dir"); @@ -1958,7 +1957,7 @@ pub(crate) mod tests { } #[cfg(all(feature = "file_io", feature = "openssl_sign"))] - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] #[allow(deprecated)] async fn test_embed_remote_sign() { let temp_dir = tempdir().expect("temp dir"); @@ -2028,9 +2027,13 @@ pub(crate) mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] #[allow(deprecated)] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] + #[cfg_attr(target_os = "wasi", allow(unused))] async fn test_embed_jpeg_stream_wasm() { use crate::assertions::User; let image = include_bytes!("../tests/fixtures/earth_apollo17.jpg"); @@ -2069,9 +2072,13 @@ pub(crate) mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] #[allow(deprecated)] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] + #[cfg_attr(target_os = "wasi", allow(unused))] async fn test_embed_png_stream_wasm() { use crate::assertions::User; let image = include_bytes!("../tests/fixtures/libpng-test.png"); @@ -2103,9 +2110,13 @@ pub(crate) mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] #[allow(deprecated)] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] + #[cfg_attr(target_os = "wasi", allow(unused))] async fn test_embed_webp_stream_wasm() { use crate::assertions::User; let image = include_bytes!("../tests/fixtures/mars.webp"); @@ -2175,9 +2186,12 @@ pub(crate) mod tests { } #[cfg_attr(feature = "openssl_sign", actix::test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] #[cfg(any( - target_arch = "wasm32", + all(target_arch = "wasm32", not(target_os = "wasi")), all(feature = "openssl_sign", feature = "file_io") ))] async fn test_embed_from_memory_async() { @@ -2220,7 +2234,7 @@ pub(crate) mod tests { } #[cfg(feature = "file_io")] - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] #[allow(deprecated)] /// Verify that an ingredient with error is reported on the ingredient and not on the manifest_store async fn test_embed_with_ingredient_error() { @@ -2779,7 +2793,7 @@ pub(crate) mod tests { } #[cfg(all(feature = "file_io", feature = "openssl_sign"))] - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] #[allow(deprecated)] async fn test_data_hash_embeddable_manifest_remote_signed() { let ap = fixture_path("cloud.jpg"); diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index ce94a8020..874cfba7a 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -599,21 +599,23 @@ impl std::fmt::Display for ManifestStore { mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] - #![cfg(not(target_os = wasi))] use c2pa_status_tracker::OneShotStatusTracker; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; use super::*; use crate::utils::test::create_test_store; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); // #[cfg_attr(not(target_arch = "wasm32"), test)] // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] #[test] fn manifest_report() { let store = create_test_store().expect("creating test store"); @@ -652,7 +654,11 @@ mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::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", allow(unused))] #[cfg(feature = "v1_api")] #[allow(deprecated)] async fn manifest_report_image_async() { @@ -692,7 +698,11 @@ mod tests { } #[cfg_attr(not(target_arch = "wasm32"), actix::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", allow(unused))] #[allow(deprecated)] #[cfg(feature = "v1_api")] async fn manifest_report_from_manifest_and_asset_bytes_async() { diff --git a/sdk/src/utils/io_utils.rs b/sdk/src/utils/io_utils.rs index 4e89c4268..94984ec09 100644 --- a/sdk/src/utils/io_utils.rs +++ b/sdk/src/utils/io_utils.rs @@ -11,7 +11,13 @@ // specific language governing permissions and limitations under // each license. -use std::io::{Read, Seek, SeekFrom, Write}; +use std::{ + ffi::OsStr, + io::{Read, Seek, SeekFrom, Write}, +}; + +#[allow(unused)] // different code path for WASI +use tempfile::{tempdir, Builder, NamedTempFile, TempDir}; use crate::{Error, Result}; @@ -141,6 +147,31 @@ impl ReaderUtils for R { } } +pub(crate) fn tempfile_builder + Sized>(prefix: T) -> Result { + #[cfg(target_os = "wasi")] + return Builder::new() + .prefix(&prefix) + .rand_bytes(5) + .tempfile_in("/") + .map_err(Error::IoError); + + #[cfg(not(target_os = "wasi"))] + return Builder::new() + .prefix(&prefix) + .rand_bytes(5) + .tempfile() + .map_err(Error::IoError); +} + +#[allow(dead_code)] // used in tests +pub(crate) fn tempdirectory() -> Result { + #[cfg(target_os = "wasi")] + return TempDir::new_in("/").map_err(|e| Error::IoError(e.into())); + + #[cfg(not(target_os = "wasi"))] + return tempdir().map_err(Error::IoError); +} + #[cfg(test)] mod tests { #![allow(clippy::expect_used)] diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 6d5d71005..c1f904fd2 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -21,10 +21,8 @@ use std::{ }; use async_trait::async_trait; -#[cfg(any(feature = "openssl_sign", target_arch = "wasm32"))] -use c2pa_crypto::cose::TimeStampStorage; use c2pa_crypto::{ - cose::CertificateTrustPolicy, + cose::{CertificateTrustPolicy, TimeStampStorage}, raw_signature::{AsyncRawSigner, RawSignerError, SigningAlg}, time_stamp::{AsyncTimeStampProvider, TimeStampError}, }; @@ -190,6 +188,11 @@ pub fn create_test_store() -> Result { /// returns a path to a file in the fixtures folder pub fn fixture_path(file_name: &str) -> PathBuf { + // File paths are relative to directory specified in dir argument. + // This assumes `wasmtime --dir .` + #[cfg(target_os = "wasi")] + let mut path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); path.push("tests/fixtures"); path.push(file_name); diff --git a/sdk/src/wasm/wasicrypto_validator.rs b/sdk/src/wasm/wasicrypto_validator.rs deleted file mode 100644 index adff88fc7..000000000 --- a/sdk/src/wasm/wasicrypto_validator.rs +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2022 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use std::convert::TryFrom; - -use async_generic::async_generic; -use spki::{DecodePublicKey, SubjectPublicKeyInfoRef}; -use x509_parser::der_parser::ber::{parse_ber_sequence, BerObject}; - -use crate::{Error, Result, SigningAlg}; - -// Conversion utility from num-bigint::BigUint (used by x509_parser) -// to num-bigint-dig::BigUint (used by rsa) -fn biguint_val(ber_object: &BerObject) -> rsa::BigUint { - ber_object - .as_biguint() - .map(|x| x.to_u32_digits()) - .map(rsa::BigUint::new) - .unwrap_or_default() -} - -// Validate an Ed25519 signature for the provided data. The pkey must -// be the raw bytes representing CompressedEdwardsY. The length must 32 bytes. -fn ed25519_validate(sig: Vec, data: Vec, pkey: Vec) -> Result { - use ed25519_dalek::{Signature, Verifier, VerifyingKey, PUBLIC_KEY_LENGTH}; - - if pkey.len() == PUBLIC_KEY_LENGTH { - let ed_sig = Signature::from_slice(&sig).map_err(|_| Error::CoseInvalidCert)?; - - // convert to VerifyingKey - let mut cert_slice: [u8; 32] = Default::default(); - cert_slice.copy_from_slice(&pkey[0..PUBLIC_KEY_LENGTH]); - - let vk = VerifyingKey::from_bytes(&cert_slice).map_err(|_| Error::CoseInvalidCert)?; - - match vk.verify(&data, &ed_sig) { - Ok(_) => Ok(true), - Err(_) => Ok(false), - } - } else { - /* - web_sys::console::debug_2( - &"Ed25519 public key incorrect length: ".into(), - &pkey.len().to_string().into(), - ); - */ - Err(Error::CoseInvalidCert) - } -} - -pub(crate) fn validate_signature( - algo: String, - hash: String, - _salt_len: u32, - pkey: Vec, - sig: Vec, - data: Vec, -) -> Result { - use rsa::{ - sha2::{Sha256, Sha384, Sha512}, - RsaPublicKey, - }; - - match algo.as_ref() { - "RSASSA-PKCS1-v1_5" => { - use rsa::{pkcs1v15::Signature, signature::Verifier}; - - // used for certificate validation - let spki = SubjectPublicKeyInfoRef::try_from(pkey.as_ref()) - .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; - - let (_, seq) = parse_ber_sequence(spki.subject_public_key.raw_bytes()) - .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; - - let modulus = biguint_val(&seq[0]); - let exp = biguint_val(&seq[1]); - let public_key = RsaPublicKey::new(modulus, exp) - .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; - let normalized_hash = hash.clone().replace("-", "").to_lowercase(); - - let result = match normalized_hash.as_ref() { - "sha256" => { - let vk = rsa::pkcs1v15::VerifyingKey::::new(public_key); - let signature: Signature = sig.as_slice().try_into().map_err(|_e| { - Error::WasmRsaKeyImport("could no process RSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - "sha384" => { - let vk = rsa::pkcs1v15::VerifyingKey::::new(public_key); - let signature: Signature = sig.as_slice().try_into().map_err(|_e| { - Error::WasmRsaKeyImport("could no process RSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - "sha512" => { - let vk = rsa::pkcs1v15::VerifyingKey::::new(public_key); - let signature: Signature = sig.as_slice().try_into().map_err(|_e| { - Error::WasmRsaKeyImport("could no process RSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - _ => return Err(Error::UnknownAlgorithm), - }; - - match result { - Ok(()) => { - //web_sys::console::debug_1(&"RSA validation success:".into()); - Ok(true) - } - Err(err) => { - /* - web_sys::console::debug_2( - &"RSA validation failed:".into(), - &err.to_string().into(), - ); - */ - Ok(false) - } - } - } - "RSA-PSS" => { - use rsa::{pss::Signature, signature::Verifier}; - - let spki = SubjectPublicKeyInfoRef::try_from(pkey.as_ref()) - .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; - - let (_, seq) = parse_ber_sequence(&spki.subject_public_key.raw_bytes()) - .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; - - // We need to normalize this from SHA-256 (the format WebCrypto uses) to sha256 - // (the format the util function expects) so that it maps correctly - let normalized_hash = hash.clone().replace("-", "").to_lowercase(); - let modulus = biguint_val(&seq[0]); - let exp = biguint_val(&seq[1]); - let public_key = RsaPublicKey::new(modulus, exp) - .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; - - let result = match normalized_hash.as_ref() { - "sha256" => { - let vk = rsa::pss::VerifyingKey::::new(public_key); - let signature: Signature = sig.as_slice().try_into().map_err(|_e| { - Error::WasmRsaKeyImport("could no process RSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - "sha384" => { - let vk = rsa::pss::VerifyingKey::::new(public_key); - let signature: Signature = sig.as_slice().try_into().map_err(|_e| { - Error::WasmRsaKeyImport("could no process RSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - "sha512" => { - let vk = rsa::pss::VerifyingKey::::new(public_key); - let signature: Signature = sig.as_slice().try_into().map_err(|_e| { - Error::WasmRsaKeyImport("could no process RSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - _ => return Err(Error::UnknownAlgorithm), - }; - - match result { - Ok(()) => { - //web_sys::console::debug_1(&"RSA-PSS validation success:".into()); - Ok(true) - } - Err(err) => { - /* - web_sys::console::debug_2( - &"RSA-PSS validation failed:".into(), - &err.to_string().into(), - ); - */ - Ok(false) - } - } - } - "ECDSA" => { - use ecdsa::{signature::Verifier as EcdsaVerifier, Signature as EcdsaSignature}; - use p256::ecdsa::VerifyingKey as P256VerifyingKey; - use p384::ecdsa::VerifyingKey as P384VerifyingKey; - let result = match hash.as_ref() { - "SHA-256" => { - let vk = P256VerifyingKey::from_public_key_der(&pkey) - .map_err(|_| Error::WasmRsaKeyImport("Invalid P-256 key".to_string()))?; - let signature = EcdsaSignature::from_slice(&sig).map_err(|_| { - Error::WasmRsaKeyImport("Invalid ECDSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - "SHA-384" => { - let vk = P384VerifyingKey::from_public_key_der(&pkey) - .map_err(|_| Error::WasmRsaKeyImport("Invalid P-384 key".to_string()))?; - let signature = EcdsaSignature::from_slice(&sig).map_err(|_| { - Error::WasmRsaKeyImport("Invalid ECDSA signature".to_string()) - })?; - vk.verify(&data, &signature) - } - _ => return Err(Error::UnknownAlgorithm), - }; - - match result { - Ok(_) => Ok(true), - Err(err) => { - /* - web_sys::console::debug_2( - &"ECDSA validation failed:".into(), - &err.to_string().into(), - ); - */ - Ok(false) - } - } - } - "ED25519" => { - use x509_parser::{prelude::*, public_key::PublicKey}; - - // pull out raw Ed code points - if let Ok((_, certificate_public_key)) = SubjectPublicKeyInfo::from_der(&pkey) { - match certificate_public_key.parsed() { - Ok(key) => match key { - PublicKey::Unknown(raw_key) => { - ed25519_validate(sig, data, raw_key.to_vec()) - } - _ => Err(Error::OtherError( - "could not unwrap Ed25519 public key".into(), - )), - }, - Err(_) => Err(Error::OtherError( - "could not recognize Ed25519 public key".into(), - )), - } - } else { - Err(Error::OtherError( - "could not parse Ed25519 public key".into(), - )) - } - } - _ => Err(Error::UnsupportedType), - } -} - -// This interface is called from CoseValidator. RSA validation not supported here. -#[async_generic] -pub fn validate(alg: SigningAlg, sig: &[u8], data: &[u8], pkey: &[u8]) -> Result { - //web_sys::console::debug_2(&"Validating with algorithm".into(), &alg.to_string().into()); - - match alg { - SigningAlg::Ps256 => validate_signature( - "RSA-PSS".to_string(), - "SHA-256".to_string(), - 32, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ), - SigningAlg::Ps384 => validate_signature( - "RSA-PSS".to_string(), - "SHA-384".to_string(), - 48, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ), - SigningAlg::Ps512 => validate_signature( - "RSA-PSS".to_string(), - "SHA-512".to_string(), - 64, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ), - // "rs256" => { - // validate_signature( - // "RSASSA-PKCS1-v1_5".to_string(), - // "SHA-256".to_string(), - // 0, - // pkey.to_vec(), - // sig.to_vec(), - // data.to_vec(), - // ) - // .await - // } - // "rs384" => { - // validate_signature( - // "RSASSA-PKCS1-v1_5".to_string(), - // "SHA-384".to_string(), - // 0, - // pkey.to_vec(), - // sig.to_vec(), - // data.to_vec(), - // ) - // .await - // } - // "rs512" => { - // validate_signature( - // "RSASSA-PKCS1-v1_5".to_string(), - // "SHA-512".to_string(), - // 0, - // pkey.to_vec(), - // sig.to_vec(), - // data.to_vec(), - // ) - // .await - // } - SigningAlg::Es256 => validate_signature( - "ECDSA".to_string(), - "SHA-256".to_string(), - 0, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ), - SigningAlg::Es384 => validate_signature( - "ECDSA".to_string(), - "SHA-384".to_string(), - 0, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ), - SigningAlg::Es512 => validate_signature( - "ECDSA".to_string(), - "SHA-512".to_string(), - 0, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ), - SigningAlg::Ed25519 => validate_signature( - "ED25519".to_string(), - "SHA-512".to_string(), - 0, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ), - } -} - -#[cfg(test)] -pub mod tests { - #![allow(clippy::unwrap_used)] - - use super::*; - use crate::SigningAlg; - - async fn test_async_verify_rsa_pss() { - // PS signatures - let sig_bytes = include_bytes!("../../tests/fixtures/sig_ps256.data"); - let data_bytes = include_bytes!("../../tests/fixtures/data_ps256.data"); - let key_bytes = include_bytes!("../../tests/fixtures/key_ps256.data"); - - let validated = validate_async(SigningAlg::Ps256, sig_bytes, data_bytes, key_bytes) - .await - .unwrap(); - - assert_eq!(validated, true); - } - - async fn test_async_verify_ecdsa() { - // EC signatures - let sig_es384_bytes = include_bytes!("../../tests/fixtures/sig_es384.data"); - let data_es384_bytes = include_bytes!("../../tests/fixtures/data_es384.data"); - let key_es384_bytes = include_bytes!("../../tests/fixtures/key_es384.data"); - - let mut validated = validate_async( - SigningAlg::Es384, - sig_es384_bytes, - data_es384_bytes, - key_es384_bytes, - ) - .await - .unwrap(); - - assert_eq!(validated, true); - - let sig_es512_bytes = include_bytes!("../../tests/fixtures/sig_es512.data"); - let data_es512_bytes = include_bytes!("../../tests/fixtures/data_es512.data"); - let key_es512_bytes = include_bytes!("../../tests/fixtures/key_es512.data"); - - validated = validate_async( - SigningAlg::Es512, - sig_es512_bytes, - data_es512_bytes, - key_es512_bytes, - ) - .await - .unwrap(); - - assert_eq!(validated, true); - - let sig_es256_bytes = include_bytes!("../../tests/fixtures/sig_es256.data"); - let data_es256_bytes = include_bytes!("../../tests/fixtures/data_es256.data"); - let key_es256_bytes = include_bytes!("../../tests/fixtures/key_es256.data"); - - let validated = validate_async( - SigningAlg::Es256, - sig_es256_bytes, - data_es256_bytes, - key_es256_bytes, - ) - .await - .unwrap(); - - assert_eq!(validated, true); - } - - #[ignore] - async fn test_async_verify_bad() { - let sig_bytes = include_bytes!("../../tests/fixtures/sig_ps256.data"); - let data_bytes = include_bytes!("../../tests/fixtures/data_ps256.data"); - let key_bytes = include_bytes!("../../tests/fixtures/key_ps256.data"); - - let mut bad_bytes = data_bytes.to_vec(); - bad_bytes[0] = b'c'; - bad_bytes[1] = b'2'; - bad_bytes[2] = b'p'; - bad_bytes[3] = b'a'; - - let validated = validate_async(SigningAlg::Ps256, sig_bytes, &bad_bytes, key_bytes) - .await - .unwrap(); - - assert_eq!(validated, false); - } -} diff --git a/sdk/src/wasm/wasipki_trust_handler.rs b/sdk/src/wasm/wasipki_trust_handler.rs deleted file mode 100644 index 9c2743db0..000000000 --- a/sdk/src/wasm/wasipki_trust_handler.rs +++ /dev/null @@ -1,672 +0,0 @@ -// Copyright 2023 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use std::{ - collections::HashSet, - io::{BufRead, BufReader, Cursor, Read}, - str::FromStr, -}; - -use asn1_rs::{nom::AsBytes, Any, Class, Header, Tag}; -use async_generic::async_generic; -use x509_parser::{ - der_parser::der::{parse_der_integer, parse_der_sequence_of}, - prelude::*, -}; - -use crate::{ - cose_validator::*, - error::{Error, Result}, - hash_utils::{hash_sha256, vec_compare}, - trust_handler::{ - has_allowed_oid, load_eku_configuration, load_trust_from_data, TrustHandlerConfig, - }, - utils::base64, - wasm::wasicrypto_validator::validate_signature, - SigningAlg, -}; - -// Struct to handle verification of trust chains -pub(crate) struct WasiTrustHandlerConfig { - pub trust_anchors: Vec>, - pub private_anchors: Vec>, - allowed_cert_set: HashSet, - config_store: Vec, -} - -impl std::fmt::Debug for WasiTrustHandlerConfig { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "{} trust anchors, {} private anchors.", - self.trust_anchors.len(), - self.private_anchors.len() - ) - } -} - -impl WasiTrustHandlerConfig { - pub fn load_default_trust(&mut self) -> Result<()> { - // load config store - let config = include_bytes!("./store.cfg"); - let mut config_reader = Cursor::new(config); - self.load_configuration(&mut config_reader)?; - - // load debug/test private trust anchors - if cfg!(test) { - let pa = include_bytes!("./test_cert_root_bundle.pem"); - let mut pa_reader = Cursor::new(pa); - - self.append_private_trust_data(&mut pa_reader)?; - } - - Ok(()) - } -} - -impl TrustHandlerConfig for WasiTrustHandlerConfig { - fn new() -> Self { - let mut th = WasiTrustHandlerConfig { - trust_anchors: Vec::new(), - private_anchors: Vec::new(), - allowed_cert_set: HashSet::new(), - config_store: Vec::new(), - }; - - if th.load_default_trust().is_err() { - th.clear(); // just use empty trust handler to fail automatically - } - - th - } - - // add trust anchors - fn load_trust_anchors_from_data(&mut self, trust_data_reader: &mut dyn Read) -> Result<()> { - let mut trust_data = Vec::new(); - trust_data_reader.read_to_end(&mut trust_data)?; - - let mut anchors = load_trust_from_data(&trust_data)?; - self.trust_anchors.append(&mut anchors); - Ok(()) - } - - // append private trust anchors - fn append_private_trust_data(&mut self, private_anchors_reader: &mut dyn Read) -> Result<()> { - let mut private_anchors_data = Vec::new(); - private_anchors_reader.read_to_end(&mut private_anchors_data)?; - - let mut anchors = load_trust_from_data(&private_anchors_data)?; - self.private_anchors.append(&mut anchors); - - Ok(()) - } - - fn clear(&mut self) { - self.trust_anchors = Vec::new(); - self.private_anchors = Vec::new(); - } - - // load EKU configuration - fn load_configuration(&mut self, config_data: &mut dyn Read) -> Result<()> { - config_data.read_to_end(&mut self.config_store)?; - Ok(()) - } - - // list off auxillary allowed EKU Oid - fn get_auxillary_ekus(&self) -> Vec { - let mut oids = Vec::new(); - if let Ok(oid_strings) = load_eku_configuration(&mut Cursor::new(&self.config_store)) { - for oid_str in &oid_strings { - if let Ok(oid) = asn1_rs::Oid::from_str(oid_str) { - oids.push(oid); - } - } - } - oids - } - - fn get_anchors(&self) -> Vec> { - let mut anchors = Vec::new(); - - anchors.append(&mut self.trust_anchors.clone()); - anchors.append(&mut self.private_anchors.clone()); - - anchors - } - - // add allowed list entries - fn load_allowed_list(&mut self, allowed_list: &mut dyn Read) -> Result<()> { - let mut buffer = Vec::new(); - allowed_list.read_to_end(&mut buffer)?; - - if let Ok(cert_list) = load_trust_from_data(&buffer) { - for cert_der in &cert_list { - let cert_sha256 = hash_sha256(cert_der); - let cert_hash_base64 = base64::encode(&cert_sha256); - - self.allowed_cert_set.insert(cert_hash_base64); - } - } - - // try to load the of base64 encoded encoding of the sha256 hash of the certificate DER encoding - let reader = Cursor::new(buffer); - let buf_reader = BufReader::new(reader); - - let mut inside_cert_block = false; - for l in buf_reader.lines().flatten() { - if l.contains("-----BEGIN") { - inside_cert_block = true; - } - if l.contains("-----END") { - inside_cert_block = false; - } - - // sanity check that data is base64 encoded and outside of certificate block - if !inside_cert_block && base64::decode(&l).is_ok() && !l.is_empty() { - self.allowed_cert_set.insert(l); - } - } - - Ok(()) - } - - // set of allowed cert hashes - fn get_allowed_list(&self) -> &HashSet { - &self.allowed_cert_set - } -} - -fn find_allowed_eku<'a>( - cert_der: &'a [u8], - allowed_ekus: &'a [asn1_rs::Oid<'a>], -) -> Option<&'a asn1_rs::Oid<'a>> { - if let Ok((_rem, cert)) = X509Certificate::from_der(cert_der) { - if let Ok(Some(eku)) = cert.extended_key_usage() { - if let Some(o) = has_allowed_oid(eku.value, allowed_ekus) { - return Some(o); - } - } - } - None -} -fn cert_signing_alg(cert: &x509_parser::certificate::X509Certificate) -> Option { - let cert_alg = cert.signature_algorithm.algorithm.clone(); - - let signing_alg = if cert_alg == SHA256_WITH_RSAENCRYPTION_OID { - "rsa256".to_string() - } else if cert_alg == SHA384_WITH_RSAENCRYPTION_OID { - "rsa384".to_string() - } else if cert_alg == SHA512_WITH_RSAENCRYPTION_OID { - "rsa512".to_string() - } else if cert_alg == ECDSA_WITH_SHA256_OID { - SigningAlg::Es256.to_string() - } else if cert_alg == ECDSA_WITH_SHA384_OID { - SigningAlg::Es384.to_string() - } else if cert_alg == ECDSA_WITH_SHA512_OID { - SigningAlg::Es512.to_string() - } else if cert_alg == RSASSA_PSS_OID { - if let Some(parameters) = &cert.signature_algorithm.parameters { - let seq = match parameters.as_sequence() { - Ok(s) => s, - Err(_) => return None, - }; - - let (_i, (ha_alg, mgf_ai)) = match seq.parse(|i| { - let (i, h) =
::from_der(i)?; - if h.class() != Class::ContextSpecific || h.tag() != Tag(0) { - return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); - } - - let (i, ha_alg) = AlgorithmIdentifier::from_der(i) - .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; - - let (i, h) =
::from_der(i)?; - if h.class() != Class::ContextSpecific || h.tag() != Tag(1) { - return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); - } - - let (i, mgf_ai) = AlgorithmIdentifier::from_der(i) - .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; - - // Ignore anything that follows these two parameters. - - Ok((i, (ha_alg, mgf_ai))) - }) { - Ok((ii, (h, m))) => (ii, (h, m)), - Err(_) => return None, - }; - - let mgf_ai_parameters = match mgf_ai.parameters { - Some(m) => m, - None => return None, - }; - - let mgf_ai_parameters = match mgf_ai_parameters.as_sequence() { - Ok(m) => m, - Err(_) => return None, - }; - - let (_i, mgf_ai_params_algorithm) = - match ::from_der(&mgf_ai_parameters.content) { - Ok((i, m)) => (i, m), - Err(_) => return None, - }; - - let mgf_ai_params_algorithm = match mgf_ai_params_algorithm.as_oid() { - Ok(m) => m, - Err(_) => return None, - }; - - // must be the same - if ha_alg.algorithm.to_id_string() != mgf_ai_params_algorithm.to_id_string() { - return None; - } - - // check for one of the mandatory types - if ha_alg.algorithm == SHA256_OID { - "ps256".to_string() - } else if ha_alg.algorithm == SHA384_OID { - "ps384".to_string() - } else if ha_alg.algorithm == SHA512_OID { - "ps512".to_string() - } else { - return None; - } - } else { - return None; - } - } else if cert_alg == ED25519_OID { - SigningAlg::Ed25519.to_string() - } else { - return None; - }; - - Some(signing_alg) -} - -#[async_generic] -pub(crate) fn verify_data( - cert_der: Vec, - sig_alg: Option, - sig: Vec, - data: Vec, -) -> Result { - use x509_parser::prelude::*; - - let (_, cert) = - X509Certificate::from_der(cert_der.as_bytes()).map_err(|_e| Error::CoseCertUntrusted)?; - - let certificate_public_key = cert.public_key(); - - if let Some(cert_alg_string) = sig_alg { - let (algo, hash, salt_len) = match cert_alg_string.as_str() { - "rsa256" => ("RSASSA-PKCS1-v1_5".to_string(), "SHA-256".to_string(), 0), - "rsa384" => ("RSASSA-PKCS1-v1_5".to_string(), "SHA-384".to_string(), 0), - "rsa512" => ("RSASSA-PKCS1-v1_5".to_string(), "SHA-512".to_string(), 0), - "es256" => ("ECDSA".to_string(), "SHA-256".to_string().to_string(), 0), - "es384" => ("ECDSA".to_string(), "SHA-384".to_string(), 0), - "es512" => ("ECDSA".to_string(), "SHA-512".to_string(), 0), - "ps256" => ("RSA-PSS".to_string(), "SHA-256".to_string(), 32), - "ps384" => ("RSA-PSS".to_string(), "SHA-384".to_string(), 48), - "ps512" => ("RSA-PSS".to_string(), "SHA-512".to_string(), 64), - "ed25519" => ("ED25519".to_string(), "SHA-512".to_string(), 0), - _ => return Err(Error::UnsupportedType), - }; - - let adjusted_sig = if cert_alg_string.starts_with("es") { - let parsed_alg_string: SigningAlg = cert_alg_string - .parse() - .map_err(|_| Error::UnknownAlgorithm)?; - match der_to_p1363(&sig, parsed_alg_string) { - Some(p1363) => p1363, - None => sig, - } - } else { - sig - }; - - validate_signature( - algo, - hash, - salt_len, - certificate_public_key.raw.to_vec(), - adjusted_sig, - data, - ) - } else { - Err(Error::BadParam("unknown alg processing cert".to_string())) - } -} -// convert der signatures to P1363 format: r | s -fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Option> { - // handle if this is a der sequence - if let Ok((_, bo)) = parse_der_sequence_of(parse_der_integer)(data) { - let seq = bo.as_sequence().ok()?; - - if seq.len() != 2 { - return None; - } - - let rp = seq[0].as_bigint().ok()?; - let sp = seq[1].as_bigint().ok()?; - - let mut r = rp.to_str_radix(16); - let mut s = sp.to_str_radix(16); - - let sig_len: usize = match alg { - SigningAlg::Es256 => 64, - SigningAlg::Es384 => 96, - SigningAlg::Es512 => 132, - _ => return None, - }; - - // pad or truncate as needed - let rp = if r.len() > sig_len { - // truncate - let offset = r.len() - sig_len; - &r[offset..r.len()] - } else { - // pad - while r.len() != sig_len { - r.insert(0, '0'); - } - r.as_ref() - }; - - let sp = if s.len() > sig_len { - // truncate - let offset = s.len() - sig_len; - &s[offset..s.len()] - } else { - // pad - while s.len() != sig_len { - s.insert(0, '0'); - } - s.as_ref() - }; - - if rp.len() != sig_len || rp.len() != sp.len() { - return None; - } - - // merge r and s strings - let mut new_sig = rp.to_string(); - new_sig.push_str(sp); - - // convert back from hex string to byte array - let result = (0..new_sig.len()) - .step_by(2) - .map(|i| { - u8::from_str_radix(&new_sig[i..i + 2], 16) - .map_err(|_err| crate::Error::InvalidEcdsaSignature) - }) - .collect(); - - if let Ok(p1363) = result { - Some(p1363) - } else { - None - } - } else { - Some(data.to_vec()) - } -} - -fn check_chain_order(certs: &[Vec]) -> Result<()> { - use x509_parser::prelude::*; - - let chain_length = certs.len(); - if chain_length < 2 { - return Ok(()); - } - - for i in 1..chain_length { - let (_, current_cert) = - X509Certificate::from_der(&certs[i - 1]).map_err(|_e| Error::CoseCertUntrusted)?; - - let issuer_der = certs[i].to_vec(); - let data = current_cert.tbs_certificate.as_ref(); - let sig = current_cert.signature_value.as_ref(); - - let sig_alg = cert_signing_alg(¤t_cert); - - let result = verify_data(issuer_der, sig_alg, sig.to_vec(), data.to_vec()); - - // keep going as long as it validate - match result { - Ok(b) => { - if !b { - return Err(Error::OtherError("cert chain order invalid".into())); - } - } - Err(e) => return Err(e), - } - } - Ok(()) -} - -fn on_trust_list(th: &dyn TrustHandlerConfig, certs: &[Vec], ee_der: &[u8]) -> Result { - use x509_parser::prelude::*; - - // check the cert against the allowed list first - let cert_sha256 = hash_sha256(ee_der); - let cert_hash_base64 = base64::encode(&cert_sha256); - if th.get_allowed_list().contains(&cert_hash_base64) { - return Ok(true); - } - - // add ee cert if needed to the chain - let full_chain = if !certs.is_empty() && vec_compare(ee_der, &certs[0]) { - certs.to_vec() - } else { - let mut full_chain: Vec> = Vec::new(); - full_chain.push(ee_der.to_vec()); - let mut in_chain = certs.to_vec(); - full_chain.append(&mut in_chain); - full_chain - }; - - // make sure chain is in the correct order and valid - check_chain_order(&full_chain)?; - - // build anchors and check against trust anchors, - let mut anchors: Vec = Vec::new(); - let source_anchors = th.get_anchors(); - for anchor_der in &source_anchors { - let (_, anchor) = - X509Certificate::from_der(anchor_der).map_err(|_e| Error::CoseCertUntrusted)?; - anchors.push(anchor); - } - - if anchors.is_empty() { - return Ok(false); - } - - // work back from last cert in chain against the trust anchors - for cert in certs.iter().rev() { - let (_, chain_cert) = - X509Certificate::from_der(cert).map_err(|_e| Error::CoseCertUntrusted)?; - - for anchor in &source_anchors { - let data = chain_cert.tbs_certificate.as_ref(); - let sig = chain_cert.signature_value.as_ref(); - - let sig_alg = cert_signing_alg(&chain_cert); - - let (_, anchor_cert) = - X509Certificate::from_der(anchor).map_err(|_e| Error::CoseCertUntrusted)?; - - if chain_cert.issuer() == anchor_cert.subject() { - let result = verify_data(anchor.clone(), sig_alg, sig.to_vec(), data.to_vec()); - - match result { - Ok(b) => { - if b { - return Ok(true); - } - } - Err(_) => continue, - } - } - } - } - // todo: consider (path check and names restrictions) - - Ok(false) -} - -// verify certificate and trust chain -#[async_generic] -pub(crate) fn verify_trust( - th: &dyn TrustHandlerConfig, - chain_der: &[Vec], - cert_der: &[u8], - _signing_time_epoc: Option, -) -> Result { - // check configured EKUs against end-entity cert - find_allowed_eku(cert_der, &th.get_auxillary_ekus()).ok_or(Error::CoseCertUntrusted)?; - - on_trust_list(th, chain_der, cert_der) -} - -#[cfg(test)] -pub mod tests { - #![allow(clippy::expect_used)] - #![allow(clippy::panic)] - #![allow(clippy::unwrap_used)] - - use super::*; - #[test] - async fn test_trust_store() { - let mut th = WasiTrustHandlerConfig::new(); - th.clear(); - - th.load_default_trust().unwrap(); - - // test all the certs - let ps256 = include_bytes!("../../tests/fixtures/certs/ps256.pub"); - let ps384 = include_bytes!("../../tests/fixtures/certs/ps384.pub"); - let ps512 = include_bytes!("../../tests/fixtures/certs/ps512.pub"); - let es256 = include_bytes!("../../tests/fixtures/certs/es256.pub"); - let es384 = include_bytes!("../../tests/fixtures/certs/es384.pub"); - let es512 = include_bytes!("../../tests/fixtures/certs/es512.pub"); - let ed25519 = include_bytes!("../../tests/fixtures/certs/ed25519.pub"); - - let ps256_certs = load_trust_from_data(ps256).unwrap(); - let ps384_certs = load_trust_from_data(ps384).unwrap(); - let ps512_certs = load_trust_from_data(ps512).unwrap(); - let es256_certs = load_trust_from_data(es256).unwrap(); - let es384_certs = load_trust_from_data(es384).unwrap(); - let es512_certs = load_trust_from_data(es512).unwrap(); - let ed25519_certs = load_trust_from_data(ed25519).unwrap(); - - assert!( - verify_trust_async(&th, &ps256_certs[1..], &ps256_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&th, &ps384_certs[1..], &ps384_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&th, &ps512_certs[1..], &ps512_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&th, &es256_certs[1..], &es256_certs[0], None) - .await - .unwrap() - ); - - assert!( - verify_trust_async(&th, &es384_certs[1..], &es384_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&th, &es512_certs[1..], &es512_certs[0], None) - .await - .unwrap() - ); - - assert!( - verify_trust_async(&th, &ed25519_certs[1..], &ed25519_certs[0], None) - .await - .unwrap() - ); - } - - #[test] - async fn test_broken_trust_chain() { - let mut th = WasiTrustHandlerConfig::new(); - th.clear(); - - th.load_default_trust().unwrap(); - - // test all the certs - let ps256 = include_bytes!("../../tests/fixtures/certs/ps256.pub"); - let ps384 = include_bytes!("../../tests/fixtures/certs/ps384.pub"); - let ps512 = include_bytes!("../../tests/fixtures/certs/ps512.pub"); - let es256 = include_bytes!("../../tests/fixtures/certs/es256.pub"); - let es384 = include_bytes!("../../tests/fixtures/certs/es384.pub"); - let es512 = include_bytes!("../../tests/fixtures/certs/es512.pub"); - let ed25519 = include_bytes!("../../tests/fixtures/certs/ed25519.pub"); - - let ps256_certs = load_trust_from_data(ps256).unwrap(); - let ps384_certs = load_trust_from_data(ps384).unwrap(); - let ps512_certs = load_trust_from_data(ps512).unwrap(); - let es256_certs = load_trust_from_data(es256).unwrap(); - let es384_certs = load_trust_from_data(es384).unwrap(); - let es512_certs = load_trust_from_data(es512).unwrap(); - let ed25519_certs = load_trust_from_data(ed25519).unwrap(); - - assert!( - !verify_trust_async(&th, &ps256_certs[2..], &ps256_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&th, &ps384_certs[2..], &ps384_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&th, &ps512_certs[2..], &ps512_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&th, &es256_certs[2..], &es256_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&th, &es384_certs[2..], &es384_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&th, &es512_certs[2..], &es512_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&th, &ed25519_certs[2..], &ed25519_certs[0], None) - .await - .unwrap() - ); - } -} diff --git a/sdk/tests/test_builder.rs b/sdk/tests/test_builder.rs index 5616c6863..19ea91cdf 100644 --- a/sdk/tests/test_builder.rs +++ b/sdk/tests/test_builder.rs @@ -21,7 +21,7 @@ mod common; use common::{compare_stream_to_known_good, fixtures_path, test_signer}; #[test] -#[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl")), ignore)] +#[ignore] // TODO: Test does not pass in WASI or native fn test_builder_ca_jpg() -> Result<()> { let manifest_def = std::fs::read_to_string(fixtures_path("simple_manifest.json"))?; let mut builder = Builder::from_json(&manifest_def)?; From 37b43f40f5561d5f43cfe27e743bbdfc6ef4dece Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Tue, 4 Feb 2025 20:12:01 -0500 Subject: [PATCH 3/8] Restrict wasip1 from attempting to create a tempfile. Tempfile is not supported in wasip1 --- sdk/src/utils/io_utils.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdk/src/utils/io_utils.rs b/sdk/src/utils/io_utils.rs index 94984ec09..d881e4aee 100644 --- a/sdk/src/utils/io_utils.rs +++ b/sdk/src/utils/io_utils.rs @@ -148,7 +148,10 @@ impl ReaderUtils for R { } pub(crate) fn tempfile_builder + Sized>(prefix: T) -> Result { - #[cfg(target_os = "wasi")] + #[cfg(all(target_os = "wasi", target_env = "p1"))] + return Error::NotImplemented("tempfile_builder requires wasip2 or later".to_string()); + + #[cfg(all(target_os = "wasi", not(target_env = "p1")))] return Builder::new() .prefix(&prefix) .rand_bytes(5) From e2657e99184e694cbec6d7f252022a164090547d Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Thu, 6 Feb 2025 14:58:49 -0500 Subject: [PATCH 4/8] feat: Tests for c2pa-crypto --- .github/workflows/ci.yml | 4 +- Makefile | 5 + internal/crypto/Cargo.toml | 3 + internal/crypto/src/tests/base64.rs | 12 ++- .../src/tests/cose/certificate_profile.rs | 12 ++- .../tests/cose/certificate_trust_policy.rs | 98 ++++++++++++++++--- internal/crypto/src/tests/hash.rs | 12 ++- internal/crypto/src/tests/internal/time.rs | 7 +- internal/crypto/src/tests/mod.rs | 2 +- internal/crypto/src/tests/ocsp.rs | 12 ++- .../src/tests/raw_signature/async_signers.rs | 11 ++- .../tests/raw_signature/async_validators.rs | 31 +++++- .../raw_signature/rust_native/signers.rs | 28 ++++-- .../raw_signature/rust_native/validators.rs | 89 +++++++++++++---- .../crypto/src/tests/raw_signature/signers.rs | 22 +++-- .../src/tests/raw_signature/validators.rs | 93 ++++++++++++++---- internal/crypto/src/tests/signing_alg.rs | 17 +++- internal/crypto/src/time_stamp/verify.rs | 16 ++- sdk/Cargo.toml | 3 + sdk/src/builder.rs | 2 +- sdk/src/manifest.rs | 6 +- sdk/src/manifest_store.rs | 4 +- sdk/src/utils/io_utils.rs | 2 +- 23 files changed, 377 insertions(+), 114 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0185e5d49..e967fd31a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -297,8 +297,6 @@ jobs: contains(github.event.pull_request.labels.*.name, 'safe to test') runs-on: ubuntu-latest -# container: -# image: ghcr.io/webassembly/wasi-sdk steps: - name: Checkout repository @@ -338,7 +336,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 + run: cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto test-direct-minimal-versions: name: Unit tests with minimum versions of direct dependencies diff --git a/Makefile b/Makefile index e3f8b0a81..85c41b35c 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,11 @@ test-wasm: test-wasm-web: cd sdk && wasm-pack test --chrome --headless -- --features="serialize_thumbnails" +# WASI testing requires the WASI SDK https://github.com/WebAssembly/wasi-sdk installed in /opt, +# wasmtime, and the target wasm32-wasip2 on the nightly toolchain +test-wasi: + CC=/opt/wasi-sdk/bin/clang CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime -S common --dir ." cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto + # Full local validation, build and test all features including wasm # Run this before pushing a PR to pre-validate test: check-format check-docs clippy test-local test-wasm-web diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 74da438be..ae75d2639 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -138,3 +138,6 @@ actix = "0.13.1" [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies] wasm-bindgen-test = "0.3.31" + +[target.'cfg(target_os = "wasi")'.dev-dependencies] +wstd = "0.5" diff --git a/internal/crypto/src/tests/base64.rs b/internal/crypto/src/tests/base64.rs index 1ef58fce5..4dd937890 100644 --- a/internal/crypto/src/tests/base64.rs +++ b/internal/crypto/src/tests/base64.rs @@ -11,19 +11,25 @@ // 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::base64; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn encode() { assert_eq!(base64::encode(b"Hello, world"), "SGVsbG8sIHdvcmxk"); } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn decode() { assert_eq!( base64::decode("SGVsbG8sIHdvcmxk"), diff --git a/internal/crypto/src/tests/cose/certificate_profile.rs b/internal/crypto/src/tests/cose/certificate_profile.rs index 9af841ad8..094821955 100644 --- a/internal/crypto/src/tests/cose/certificate_profile.rs +++ b/internal/crypto/src/tests/cose/certificate_profile.rs @@ -14,14 +14,17 @@ use c2pa_status_tracker::{ validation_codes::SIGNING_CREDENTIAL_EXPIRED, DetailedStatusTracker, StatusTracker, }; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; use x509_parser::pem::Pem; use crate::cose::{check_certificate_profile, CertificateTrustPolicy}; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn expired_cert() { let ctp = CertificateTrustPolicy::default(); let mut validation_log = DetailedStatusTracker::default(); @@ -41,7 +44,10 @@ fn expired_cert() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn cert_algorithms() { let ctp = CertificateTrustPolicy::default(); diff --git a/internal/crypto/src/tests/cose/certificate_trust_policy.rs b/internal/crypto/src/tests/cose/certificate_trust_policy.rs index d2ec2b860..9daad1f60 100644 --- a/internal/crypto/src/tests/cose/certificate_trust_policy.rs +++ b/internal/crypto/src/tests/cose/certificate_trust_policy.rs @@ -12,7 +12,7 @@ // each license. use asn1_rs::{oid, Oid}; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; use x509_parser::{extensions::ExtendedKeyUsage, pem::Pem}; @@ -22,14 +22,20 @@ use crate::{ }; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn impl_debug() { let ctp = CertificateTrustPolicy::new(); let _ = format!("{ctp:#?}"); } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn new() { let ctp = CertificateTrustPolicy::new(); @@ -52,7 +58,10 @@ fn new() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn default() { let ctp = CertificateTrustPolicy::default(); @@ -78,7 +87,10 @@ fn default() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn clear() { let mut ctp = CertificateTrustPolicy::default(); ctp.clear(); @@ -102,7 +114,10 @@ fn clear() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn add_valid_ekus_err_bad_utf8() { let mut ctp = CertificateTrustPolicy::new(); ctp.add_valid_ekus(&[128, 0]); @@ -126,28 +141,40 @@ fn add_valid_ekus_err_bad_utf8() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn add_trust_anchors_err_bad_pem() { let mut ctp = CertificateTrustPolicy::new(); assert!(ctp.add_trust_anchors(BAD_PEM.as_bytes()).is_err()); } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn add_end_entity_credentials_err_bad_pem() { let mut ctp = CertificateTrustPolicy::new(); assert!(ctp.add_end_entity_credentials(BAD_PEM.as_bytes()).is_err()); } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn err_to_string() { let ice = InvalidCertificateError("foo".to_string()); assert_eq!(ice.to_string(), "Unable to parse certificate list: foo"); } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn err_debug() { let ice = InvalidCertificateError("foo".to_string()); assert_eq!( @@ -237,16 +264,22 @@ fn test_trust_store() { let ps256 = test_signer(SigningAlg::Ps256); let ps384 = test_signer(SigningAlg::Ps384); let ps512 = test_signer(SigningAlg::Ps512); + #[cfg(not(target_arch = "wasm32"))] let es256 = test_signer(SigningAlg::Es256); + #[cfg(not(target_arch = "wasm32"))] let es384 = test_signer(SigningAlg::Es384); + #[cfg(not(target_arch = "wasm32"))] let es512 = test_signer(SigningAlg::Es512); let ed25519 = test_signer(SigningAlg::Ed25519); let ps256_certs = ps256.cert_chain().unwrap(); let ps384_certs = ps384.cert_chain().unwrap(); let ps512_certs = ps512.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es256_certs = es256.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es384_certs = es384.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es512_certs = es512.cert_chain().unwrap(); let ed25519_certs = ed25519.cert_chain().unwrap(); @@ -256,10 +289,13 @@ fn test_trust_store() { .unwrap(); ctp.check_certificate_trust(&ps512_certs[1..], &ps512_certs[0], None) .unwrap(); + #[cfg(not(target_arch = "wasm32"))] ctp.check_certificate_trust(&es256_certs[1..], &es256_certs[0], None) .unwrap(); + #[cfg(not(target_arch = "wasm32"))] ctp.check_certificate_trust(&es384_certs[1..], &es384_certs[0], None) .unwrap(); + #[cfg(not(target_arch = "wasm32"))] ctp.check_certificate_trust(&es512_certs[1..], &es512_certs[0], None) .unwrap(); ctp.check_certificate_trust(&ed25519_certs[1..], &ed25519_certs[0], None) @@ -267,7 +303,11 @@ fn test_trust_store() { } #[cfg_attr(not(target_arch = "wasm32"), actix::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 test_trust_store_async() { let ctp = CertificateTrustPolicy::default(); @@ -309,16 +349,22 @@ fn test_broken_trust_chain() { let ps256 = test_signer(SigningAlg::Ps256); let ps384 = test_signer(SigningAlg::Ps384); let ps512 = test_signer(SigningAlg::Ps512); + #[cfg(not(target_arch = "wasm32"))] let es256 = test_signer(SigningAlg::Es256); + #[cfg(not(target_arch = "wasm32"))] let es384 = test_signer(SigningAlg::Es384); + #[cfg(not(target_arch = "wasm32"))] let es512 = test_signer(SigningAlg::Es512); let ed25519 = test_signer(SigningAlg::Ed25519); let ps256_certs = ps256.cert_chain().unwrap(); let ps384_certs = ps384.cert_chain().unwrap(); let ps512_certs = ps512.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es256_certs = es256.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es384_certs = es384.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es512_certs = es512.cert_chain().unwrap(); let ed25519_certs = ed25519.cert_chain().unwrap(); @@ -347,18 +393,21 @@ fn test_broken_trust_chain() { CertificateTrustError::CertificateNotTrusted ); + #[cfg(not(target_arch = "wasm32"))] assert_eq!( ctp.check_certificate_trust(&es256_certs[2..], &es256_certs[0], None) .unwrap_err(), CertificateTrustError::CertificateNotTrusted ); + #[cfg(not(target_arch = "wasm32"))] assert_eq!( ctp.check_certificate_trust(&es384_certs[2..], &es384_certs[0], None) .unwrap_err(), CertificateTrustError::CertificateNotTrusted ); + #[cfg(not(target_arch = "wasm32"))] assert_eq!( ctp.check_certificate_trust(&es512_certs[2..], &es512_certs[0], None) .unwrap_err(), @@ -373,7 +422,11 @@ fn test_broken_trust_chain() { } #[cfg_attr(not(target_arch = "wasm32"), actix::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 test_broken_trust_chain_async() { let ctp = CertificateTrustPolicy::default(); @@ -465,16 +518,22 @@ fn test_allowed_list() { let ps256 = test_signer(SigningAlg::Ps256); let ps384 = test_signer(SigningAlg::Ps384); let ps512 = test_signer(SigningAlg::Ps512); + #[cfg(not(target_arch = "wasm32"))] let es256 = test_signer(SigningAlg::Es256); + #[cfg(not(target_arch = "wasm32"))] let es384 = test_signer(SigningAlg::Es384); + #[cfg(not(target_arch = "wasm32"))] let es512 = test_signer(SigningAlg::Es512); let ed25519 = test_signer(SigningAlg::Ed25519); let ps256_certs = ps256.cert_chain().unwrap(); let ps384_certs = ps384.cert_chain().unwrap(); let ps512_certs = ps512.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es256_certs = es256.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es384_certs = es384.cert_chain().unwrap(); + #[cfg(not(target_arch = "wasm32"))] let es512_certs = es512.cert_chain().unwrap(); let ed25519_certs = ed25519.cert_chain().unwrap(); @@ -484,10 +543,13 @@ fn test_allowed_list() { .unwrap(); ctp.check_certificate_trust(&ps512_certs[1..], &ps512_certs[0], None) .unwrap(); + #[cfg(not(target_arch = "wasm32"))] ctp.check_certificate_trust(&es256_certs[1..], &es256_certs[0], None) .unwrap(); + #[cfg(not(target_arch = "wasm32"))] ctp.check_certificate_trust(&es384_certs[1..], &es384_certs[0], None) .unwrap(); + #[cfg(not(target_arch = "wasm32"))] ctp.check_certificate_trust(&es512_certs[1..], &es512_certs[0], None) .unwrap(); ctp.check_certificate_trust(&ed25519_certs[1..], &ed25519_certs[0], None) @@ -495,7 +557,11 @@ fn test_allowed_list() { } #[cfg_attr(not(target_arch = "wasm32"), actix::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 test_allowed_list_async() { let mut ctp = CertificateTrustPolicy::new(); @@ -579,7 +645,11 @@ fn test_allowed_list_hashes() { } #[cfg_attr(not(target_arch = "wasm32"), actix::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 test_allowed_list_hashes_async() { let mut ctp = CertificateTrustPolicy::new(); diff --git a/internal/crypto/src/tests/hash.rs b/internal/crypto/src/tests/hash.rs index e80ff1b9d..47c051279 100644 --- a/internal/crypto/src/tests/hash.rs +++ b/internal/crypto/src/tests/hash.rs @@ -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::hash::{sha1, sha256}; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn test_sha1() { let hash = sha1(b"test message"); assert_eq!( @@ -30,7 +33,10 @@ fn test_sha1() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn test_sha256() { let hash = sha256(b"test message"); assert_eq!( diff --git a/internal/crypto/src/tests/internal/time.rs b/internal/crypto/src/tests/internal/time.rs index c6f3eaba7..21f578529 100644 --- a/internal/crypto/src/tests/internal/time.rs +++ b/internal/crypto/src/tests/internal/time.rs @@ -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::internal::time; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn now() { let time_now = time::utc_now(); let unix_ts = time_now.timestamp(); diff --git a/internal/crypto/src/tests/mod.rs b/internal/crypto/src/tests/mod.rs index 4b3430e67..93679da97 100644 --- a/internal/crypto/src/tests/mod.rs +++ b/internal/crypto/src/tests/mod.rs @@ -18,7 +18,7 @@ #![allow(clippy::panic)] #![allow(clippy::unwrap_used)] -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); mod base64; diff --git a/internal/crypto/src/tests/ocsp.rs b/internal/crypto/src/tests/ocsp.rs index 2167161cd..7edc6bbf5 100644 --- a/internal/crypto/src/tests/ocsp.rs +++ b/internal/crypto/src/tests/ocsp.rs @@ -13,13 +13,16 @@ use c2pa_status_tracker::DetailedStatusTracker; use chrono::{TimeZone, Utc}; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; use crate::ocsp::OcspResponse; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn good() { let rsp_data = include_bytes!("fixtures/ocsp/good.data"); @@ -35,7 +38,10 @@ fn good() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn revoked() { let rsp_data = include_bytes!("fixtures/ocsp/revoked.data"); diff --git a/internal/crypto/src/tests/raw_signature/async_signers.rs b/internal/crypto/src/tests/raw_signature/async_signers.rs index f165cd0ef..5bc4c7b20 100644 --- a/internal/crypto/src/tests/raw_signature/async_signers.rs +++ b/internal/crypto/src/tests/raw_signature/async_signers.rs @@ -13,7 +13,7 @@ #![allow(unused)] // test fns appear unused on WASM -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; use crate::raw_signature::{ @@ -99,7 +99,11 @@ async fn es512() { } #[cfg_attr(not(target_arch = "wasm32"), actix::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 ed25519() { let cert_chain = include_bytes!("../fixtures/raw_signature/ed25519.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ed25519.priv"); @@ -126,6 +130,7 @@ async fn ed25519() { #[cfg_attr(not(target_arch = "wasm32"), actix::test)] // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr(target_os = "wasi", wstd::test)] async fn ps256() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps256.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps256.priv"); @@ -152,6 +157,7 @@ async fn ps256() { #[cfg_attr(not(target_arch = "wasm32"), actix::test)] // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr(target_os = "wasi", wstd::test)] async fn ps384() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps384.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps384.priv"); @@ -178,6 +184,7 @@ async fn ps384() { #[cfg_attr(not(target_arch = "wasm32"), actix::test)] // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr(target_os = "wasi", wstd::test)] async fn ps512() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps512.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps512.priv"); diff --git a/internal/crypto/src/tests/raw_signature/async_validators.rs b/internal/crypto/src/tests/raw_signature/async_validators.rs index 032a2dccc..a3359f7e8 100644 --- a/internal/crypto/src/tests/raw_signature/async_validators.rs +++ b/internal/crypto/src/tests/raw_signature/async_validators.rs @@ -11,6 +11,7 @@ // specific language governing permissions and limitations under // each license. +#[cfg(not(target_os = "wasi"))] use wasm_bindgen_test::wasm_bindgen_test; use crate::raw_signature::{ @@ -19,7 +20,11 @@ use crate::raw_signature::{ const SAMPLE_DATA: &[u8] = b"some sample content to sign"; -#[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 es256() { let signature = include_bytes!("../fixtures/raw_signature/es256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es256.pub_key"); @@ -32,7 +37,11 @@ async fn es256() { .unwrap(); } -#[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 es256_bad_signature() { let mut signature = include_bytes!("../fixtures/raw_signature/es256.raw_sig").to_vec(); assert_ne!(signature[10], 10); @@ -51,7 +60,11 @@ async fn es256_bad_signature() { ); } -#[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 es256_bad_data() { let signature = include_bytes!("../fixtures/raw_signature/es256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es256.pub_key"); @@ -70,7 +83,11 @@ async fn es256_bad_data() { ); } -#[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 es384() { let signature = include_bytes!("../fixtures/raw_signature/es384.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es384.pub_key"); @@ -83,7 +100,11 @@ async fn es384() { .unwrap(); } -#[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 es512() { let signature = include_bytes!("../fixtures/raw_signature/es512.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es512.pub_key"); diff --git a/internal/crypto/src/tests/raw_signature/rust_native/signers.rs b/internal/crypto/src/tests/raw_signature/rust_native/signers.rs index 1ce036e46..fbe06bbeb 100644 --- a/internal/crypto/src/tests/raw_signature/rust_native/signers.rs +++ b/internal/crypto/src/tests/raw_signature/rust_native/signers.rs @@ -11,14 +11,14 @@ // 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::raw_signature::{rust_native, SigningAlg}; /* Not implemented in rust_native yet. #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] fn es256() { let cert_chain = include_bytes!("../../fixtures/raw_signature/es256.pub"); let private_key = include_bytes!("../../fixtures/raw_signature/es256.priv"); @@ -40,7 +40,7 @@ fn es256() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] fn es384() { let cert_chain = include_bytes!("../../fixtures/raw_signature/es384.pub"); let private_key = include_bytes!("../../fixtures/raw_signature/es384.priv"); @@ -62,7 +62,7 @@ fn es384() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] fn es512() { let cert_chain = include_bytes!("../../fixtures/raw_signature/es512.pub"); let private_key = include_bytes!("../../fixtures/raw_signature/es512.priv"); @@ -85,7 +85,10 @@ fn es512() { */ #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ed25519() { let cert_chain = include_bytes!("../../fixtures/raw_signature/ed25519.pub"); let private_key = include_bytes!("../../fixtures/raw_signature/ed25519.priv"); @@ -112,7 +115,10 @@ fn ed25519() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps256() { let cert_chain = include_bytes!("../../fixtures/raw_signature/ps256.pub"); let private_key = include_bytes!("../../fixtures/raw_signature/ps256.priv"); @@ -138,7 +144,10 @@ fn ps256() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps384() { let cert_chain = include_bytes!("../../fixtures/raw_signature/ps384.pub"); let private_key = include_bytes!("../../fixtures/raw_signature/ps384.priv"); @@ -164,7 +173,10 @@ fn ps384() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps512() { let cert_chain = include_bytes!("../../fixtures/raw_signature/ps512.pub"); let private_key = include_bytes!("../../fixtures/raw_signature/ps512.priv"); diff --git a/internal/crypto/src/tests/raw_signature/rust_native/validators.rs b/internal/crypto/src/tests/raw_signature/rust_native/validators.rs index f9ac4be4c..a9161f629 100644 --- a/internal/crypto/src/tests/raw_signature/rust_native/validators.rs +++ b/internal/crypto/src/tests/raw_signature/rust_native/validators.rs @@ -13,7 +13,7 @@ use bcder::Oid; use rasn::types::OctetString; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; use crate::raw_signature::{rust_native, RawSignatureValidationError, SigningAlg}; @@ -21,7 +21,10 @@ use crate::raw_signature::{rust_native, RawSignatureValidationError, SigningAlg} const SAMPLE_DATA: &[u8] = b"some sample content to sign"; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es256() { let signature = include_bytes!("../../fixtures/raw_signature/es256.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/es256.pub_key"); @@ -32,7 +35,10 @@ fn es256() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es256_bad_signature() { let mut signature = include_bytes!("../../fixtures/raw_signature/es256.raw_sig").to_vec(); assert_ne!(signature[10], 10); @@ -51,7 +57,10 @@ fn es256_bad_signature() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es256_bad_data() { let signature = include_bytes!("../../fixtures/raw_signature/es256.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/es256.pub_key"); @@ -68,7 +77,10 @@ fn es256_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es384() { let signature = include_bytes!("../../fixtures/raw_signature/es384.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/es384.pub_key"); @@ -79,7 +91,10 @@ fn es384() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es512() { let signature = include_bytes!("../../fixtures/raw_signature/es512.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/es512.pub_key"); @@ -90,7 +105,10 @@ fn es512() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ed25519() { let signature = include_bytes!("../../fixtures/raw_signature/ed25519.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/ed25519.pub_key"); @@ -102,7 +120,10 @@ fn ed25519() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ed25519_bad_data() { let signature = include_bytes!("../../fixtures/raw_signature/ed25519.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/ed25519.pub_key"); @@ -121,7 +142,10 @@ fn ed25519_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps256() { let signature = include_bytes!("../../fixtures/raw_signature/ps256.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/ps256.pub_key"); @@ -132,7 +156,10 @@ fn ps256() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps256_bad_signature() { let mut signature = include_bytes!("../../fixtures/raw_signature/ps256.raw_sig").to_vec(); assert_ne!(signature[10], 10); @@ -151,7 +178,10 @@ fn ps256_bad_signature() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps256_bad_data() { let signature = include_bytes!("../../fixtures/raw_signature/ps256.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/ps256.pub_key"); @@ -168,7 +198,10 @@ fn ps256_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps384() { let signature = include_bytes!("../../fixtures/raw_signature/ps384.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/ps384.pub_key"); @@ -179,7 +212,10 @@ fn ps384() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps512() { let signature = include_bytes!("../../fixtures/raw_signature/ps512.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/ps512.pub_key"); @@ -202,7 +238,10 @@ const SHA384_OID: Oid = bcder::Oid(OctetString::from_static(&[96, 134, 72, 1, 10 const SHA512_OID: Oid = bcder::Oid(OctetString::from_static(&[96, 134, 72, 1, 101, 3, 4, 2, 3])); #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn legacy_rs256() { let signature = include_bytes!("../../fixtures/raw_signature/legacy/rs256.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/legacy/rs256.pub_key"); @@ -214,7 +253,10 @@ fn legacy_rs256() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn legacy_rs256_bad_signature() { let mut signature = include_bytes!("../../fixtures/raw_signature/legacy/rs256.raw_sig").to_vec(); @@ -235,7 +277,10 @@ fn legacy_rs256_bad_signature() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn legacy_rs256_bad_data() { let signature = include_bytes!("../../fixtures/raw_signature/legacy/rs256.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/legacy/rs256.pub_key"); @@ -253,7 +298,10 @@ fn legacy_rs256_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn rs384() { let signature = include_bytes!("../../fixtures/raw_signature/legacy/rs384.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/legacy/rs384.pub_key"); @@ -265,7 +313,10 @@ fn rs384() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn rs512() { let signature = include_bytes!("../../fixtures/raw_signature/legacy/rs512.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/legacy/rs512.pub_key"); @@ -280,7 +331,7 @@ fn rs512() { const SHA1_OID: Oid = bcder::Oid(OctetString::from_static(&[43, 14, 3, 2, 26])); #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] fn sha1() { let signature = include_bytes!("../../fixtures/raw_signature/legacy/sha1.raw_sig"); let pub_key = include_bytes!("../../fixtures/raw_signature/legacy/sha1.pub_key"); diff --git a/internal/crypto/src/tests/raw_signature/signers.rs b/internal/crypto/src/tests/raw_signature/signers.rs index 1ddd0b782..846b6f79f 100644 --- a/internal/crypto/src/tests/raw_signature/signers.rs +++ b/internal/crypto/src/tests/raw_signature/signers.rs @@ -11,7 +11,7 @@ // 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::raw_signature::{ @@ -19,7 +19,8 @@ use crate::raw_signature::{ }; #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] +#[cfg(not(target_arch = "wasm32"))] fn es256() { let cert_chain = include_bytes!("../fixtures/raw_signature/es256.pub"); let private_key = include_bytes!("../fixtures/raw_signature/es256.priv"); @@ -41,7 +42,8 @@ fn es256() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] +#[cfg(not(target_arch = "wasm32"))] fn es384() { let cert_chain = include_bytes!("../fixtures/raw_signature/es384.pub"); let private_key = include_bytes!("../fixtures/raw_signature/es384.priv"); @@ -63,7 +65,8 @@ fn es384() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] +#[cfg(not(target_arch = "wasm32"))] fn es512() { let cert_chain = include_bytes!("../fixtures/raw_signature/es512.pub"); let private_key = include_bytes!("../fixtures/raw_signature/es512.priv"); @@ -85,7 +88,10 @@ fn es512() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ed25519() { let cert_chain = include_bytes!("../fixtures/raw_signature/ed25519.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ed25519.priv"); @@ -107,7 +113,7 @@ fn ed25519() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] fn ps256() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps256.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps256.priv"); @@ -129,7 +135,7 @@ fn ps256() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] fn ps384() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps384.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps384.priv"); @@ -151,7 +157,7 @@ fn ps384() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] fn ps512() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps512.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps512.priv"); diff --git a/internal/crypto/src/tests/raw_signature/validators.rs b/internal/crypto/src/tests/raw_signature/validators.rs index 50df94130..071642a85 100644 --- a/internal/crypto/src/tests/raw_signature/validators.rs +++ b/internal/crypto/src/tests/raw_signature/validators.rs @@ -15,7 +15,7 @@ use std::str::FromStr; use bcder::Oid; use rasn::types::OctetString; -#[cfg(target_arch = "wasm32")] +#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; use crate::raw_signature::{ @@ -26,7 +26,10 @@ use crate::raw_signature::{ const SAMPLE_DATA: &[u8] = b"some sample content to sign"; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es256() { let signature = include_bytes!("../fixtures/raw_signature/es256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es256.pub_key"); @@ -37,7 +40,10 @@ fn es256() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es256_bad_signature() { let mut signature = include_bytes!("../fixtures/raw_signature/es256.raw_sig").to_vec(); assert_ne!(signature[10], 10); @@ -56,7 +62,10 @@ fn es256_bad_signature() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es256_bad_data() { let signature = include_bytes!("../fixtures/raw_signature/es256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es256.pub_key"); @@ -73,7 +82,10 @@ fn es256_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn es384() { let signature = include_bytes!("../fixtures/raw_signature/es384.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es384.pub_key"); @@ -84,7 +96,7 @@ fn es384() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // ES512 not +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] // ES512 not // implemented fn es512() { let signature = include_bytes!("../fixtures/raw_signature/es512.raw_sig"); @@ -96,7 +108,10 @@ fn es512() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ed25519() { let signature = include_bytes!("../fixtures/raw_signature/ed25519.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/ed25519.pub_key"); @@ -107,7 +122,10 @@ fn ed25519() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ed25519_bad_data() { let signature = include_bytes!("../fixtures/raw_signature/ed25519.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/ed25519.pub_key"); @@ -125,7 +143,10 @@ fn ed25519_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps256() { let signature = include_bytes!("../fixtures/raw_signature/ps256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/ps256.pub_key"); @@ -136,7 +157,10 @@ fn ps256() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps256_bad_signature() { let mut signature = include_bytes!("../fixtures/raw_signature/ps256.raw_sig").to_vec(); assert_ne!(signature[10], 10); @@ -155,7 +179,10 @@ fn ps256_bad_signature() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps256_bad_data() { let signature = include_bytes!("../fixtures/raw_signature/ps256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/ps256.pub_key"); @@ -172,7 +199,10 @@ fn ps256_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps384() { let signature = include_bytes!("../fixtures/raw_signature/ps384.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/ps384.pub_key"); @@ -183,7 +213,10 @@ fn ps384() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn ps512() { let signature = include_bytes!("../fixtures/raw_signature/ps512.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/ps512.pub_key"); @@ -205,10 +238,14 @@ const SHA384_OID: Oid = bcder::Oid(OctetString::from_static(&[96, 134, 72, 1, 10 const SHA512_OID: Oid = bcder::Oid(OctetString::from_static(&[96, 134, 72, 1, 101, 3, 4, 2, 3])); +#[cfg_attr(target_arch = "wasm32", allow(unused))] const SHA1_OID: Oid = bcder::Oid(OctetString::from_static(&[43, 14, 3, 2, 26])); #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn legacy_rs256() { let signature = include_bytes!("../fixtures/raw_signature/legacy/rs256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/legacy/rs256.pub_key"); @@ -219,7 +256,10 @@ fn legacy_rs256() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn legacy_rs256_bad_signature() { let mut signature = include_bytes!("../fixtures/raw_signature/legacy/rs256.raw_sig").to_vec(); assert_ne!(signature[10], 10); @@ -238,7 +278,10 @@ fn legacy_rs256_bad_signature() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn legacy_rs256_bad_data() { let signature = include_bytes!("../fixtures/raw_signature/legacy/rs256.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/legacy/rs256.pub_key"); @@ -255,7 +298,10 @@ fn legacy_rs256_bad_data() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn rs384() { let signature = include_bytes!("../fixtures/raw_signature/legacy/rs384.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/legacy/rs384.pub_key"); @@ -266,7 +312,10 @@ fn rs384() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn rs512() { let signature = include_bytes!("../fixtures/raw_signature/legacy/rs512.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/legacy/rs512.pub_key"); @@ -277,8 +326,9 @@ fn rs512() { } #[test] -// #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] // SHA1 not +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] // SHA1 not // implemented +#[cfg(not(target_arch = "wasm32"))] fn sha1() { let signature = include_bytes!("../fixtures/raw_signature/legacy/sha1.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/legacy/sha1.pub_key"); @@ -297,7 +347,10 @@ fn ans1_oid_bcder_oid(asn1_oid: &asn1_rs::Oid) -> bcder::Oid { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn test_get_by_sig_and_alg() { use crate::raw_signature::oids::*; diff --git a/internal/crypto/src/tests/signing_alg.rs b/internal/crypto/src/tests/signing_alg.rs index 92adafc54..443ea13ce 100644 --- a/internal/crypto/src/tests/signing_alg.rs +++ b/internal/crypto/src/tests/signing_alg.rs @@ -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::raw_signature::{SigningAlg, UnknownAlgorithmError}; #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn alg_from_str() { assert_eq!("es256".parse(), Ok(SigningAlg::Es256)); assert_eq!("es384".parse(), Ok(SigningAlg::Es384)); @@ -32,7 +35,10 @@ fn alg_from_str() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn signing_alg_impl_display() { assert_eq!(format!("{}", SigningAlg::Es256), "es256"); assert_eq!(format!("{}", SigningAlg::Es384), "es384"); @@ -44,7 +50,10 @@ fn signing_alg_impl_display() { } #[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test +)] fn err_impl_display() { assert_eq!( format!("{}", UnknownAlgorithmError("bogus".to_owned())), diff --git a/internal/crypto/src/time_stamp/verify.rs b/internal/crypto/src/time_stamp/verify.rs index f7b9b39b0..95512bd30 100644 --- a/internal/crypto/src/time_stamp/verify.rs +++ b/internal/crypto/src/time_stamp/verify.rs @@ -352,15 +352,13 @@ async fn validate_timestamp_sig_async( .validate_async(&sig_val.to_bytes(), tbs, signing_key_der) .await .map_err(|_| TimeStampError::InvalidData) + } else if let Some(validator) = + crate::raw_signature::validator_for_sig_and_hash_algs(sig_alg, hash_alg) + { + validator + .validate(&sig_val.to_bytes(), tbs, signing_key_der) + .map_err(|_| TimeStampError::InvalidData) } else { - if let Some(validator) = - crate::raw_signature::validator_for_sig_and_hash_algs(sig_alg, hash_alg) - { - validator - .validate(&sig_val.to_bytes(), tbs, signing_key_der) - .map_err(|_| TimeStampError::InvalidData) - } else { - Err(TimeStampError::UnsupportedAlgorithm) - } + Err(TimeStampError::UnsupportedAlgorithm) } } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 983ce27ae..957420cc8 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -179,3 +179,6 @@ wasm-bindgen-test = "0.3.45" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] actix = "0.13.1" tokio = { version = "1.36.0", features = ["full"] } + +[target.'cfg(target_os = "wasi")'.dev-dependencies] +wstd = "0.5" diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index c4bf0c6d0..f1ca99870 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1461,7 +1461,7 @@ mod tests { wasm_bindgen_test )] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] - #[cfg_attr(target_os = "wasi", allow(unused))] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_builder_remote_sign() { let format = "image/jpeg"; let mut source = Cursor::new(TEST_IMAGE); diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 2f4994b0f..3610a6bd4 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -2033,7 +2033,7 @@ pub(crate) mod tests { )] #[allow(deprecated)] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] - #[cfg_attr(target_os = "wasi", allow(unused))] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_embed_jpeg_stream_wasm() { use crate::assertions::User; let image = include_bytes!("../tests/fixtures/earth_apollo17.jpg"); @@ -2078,7 +2078,7 @@ pub(crate) mod tests { )] #[allow(deprecated)] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] - #[cfg_attr(target_os = "wasi", allow(unused))] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_embed_png_stream_wasm() { use crate::assertions::User; let image = include_bytes!("../tests/fixtures/libpng-test.png"); @@ -2116,7 +2116,7 @@ pub(crate) mod tests { )] #[allow(deprecated)] #[cfg_attr(not(any(target_arch = "wasm32", feature = "openssl_sign")), ignore)] - #[cfg_attr(target_os = "wasi", allow(unused))] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_embed_webp_stream_wasm() { use crate::assertions::User; let image = include_bytes!("../tests/fixtures/mars.webp"); diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index 874cfba7a..32ee1819b 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -658,7 +658,7 @@ mod tests { all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test )] - #[cfg_attr(target_os = "wasi", allow(unused))] + #[cfg_attr(target_os = "wasi", wstd::test)] #[cfg(feature = "v1_api")] #[allow(deprecated)] async fn manifest_report_image_async() { @@ -702,7 +702,7 @@ mod tests { all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test )] - #[cfg_attr(target_os = "wasi", allow(unused))] + #[cfg_attr(target_os = "wasi", wstd::test)] #[allow(deprecated)] #[cfg(feature = "v1_api")] async fn manifest_report_from_manifest_and_asset_bytes_async() { diff --git a/sdk/src/utils/io_utils.rs b/sdk/src/utils/io_utils.rs index d881e4aee..ed2a4e3cc 100644 --- a/sdk/src/utils/io_utils.rs +++ b/sdk/src/utils/io_utils.rs @@ -169,7 +169,7 @@ pub(crate) fn tempfile_builder + Sized>(prefix: T) -> Result Result { #[cfg(target_os = "wasi")] - return TempDir::new_in("/").map_err(|e| Error::IoError(e.into())); + return TempDir::new_in("/").map_err(Error::IoError); #[cfg(not(target_os = "wasi"))] return tempdir().map_err(Error::IoError); From 35df2186d6949a61a65b80c81fca5d1f1c26d1cf Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Fri, 7 Feb 2025 11:37:51 -0500 Subject: [PATCH 5/8] feat: wasi file_io --- sdk/examples/data_hash.rs | 6 +- sdk/src/asset_handlers/c2pa_io.rs | 4 +- sdk/src/asset_handlers/jpeg_io.rs | 11 ++- sdk/src/builder.rs | 14 ++-- sdk/src/cose_sign.rs | 5 +- sdk/src/reader.rs | 8 ++- sdk/src/settings.rs | 6 +- sdk/src/store.rs | 110 ++++++++++++++++-------------- sdk/src/utils/io_utils.rs | 2 +- sdk/tests/common/mod.rs | 12 ++++ sdk/tests/integration.rs | 64 ++++++++++++++--- sdk/tests/test_builder.rs | 4 +- 12 files changed, 161 insertions(+), 85 deletions(-) diff --git a/sdk/examples/data_hash.rs b/sdk/examples/data_hash.rs index c17fbc87a..75b1f5d8e 100644 --- a/sdk/examples/data_hash.rs +++ b/sdk/examples/data_hash.rs @@ -14,13 +14,13 @@ // Example code (in unit test) for how you might use client DataHash values. This allows clients // to perform the manifest embedding and optionally the hashing -#[cfg(all(feature = "openssl_sign", not(target_arch = "wasm32")))] +#[cfg(any(feature = "openssl_sign", feature = "file_io"))] use std::{ io::{Cursor, Read, Seek, Write}, path::{Path, PathBuf}, }; -#[cfg(all(feature = "openssl_sign", not(target_arch = "wasm32")))] +#[cfg(any(feature = "openssl_sign", feature = "file_io"))] use c2pa::{ assertions::{ c2pa_action, labels::*, Action, Actions, CreativeWork, DataHash, Exif, SchemaDotOrgPerson, @@ -28,7 +28,7 @@ use c2pa::{ create_signer, hash_stream_by_alg, Builder, ClaimGeneratorInfo, HashRange, Ingredient, Reader, Relationship, Result, }; -#[cfg(all(feature = "openssl_sign", not(target_arch = "wasm32")))] +#[cfg(any(feature = "openssl_sign", feature = "file_io"))] use c2pa_crypto::raw_signature::SigningAlg; fn main() -> std::result::Result<(), Box> { diff --git a/sdk/src/asset_handlers/c2pa_io.rs b/sdk/src/asset_handlers/c2pa_io.rs index 0db895ac7..2505e7536 100644 --- a/sdk/src/asset_handlers/c2pa_io.rs +++ b/sdk/src/asset_handlers/c2pa_io.rs @@ -148,12 +148,12 @@ pub mod tests { use c2pa_crypto::raw_signature::SigningAlg; use c2pa_status_tracker::OneShotStatusTracker; - use tempfile::tempdir; use super::{AssetIO, C2paIO, CAIReader, CAIWriter}; use crate::{ store::Store, utils::{ + io_utils::tempdirectory, test::{fixture_path, temp_dir_path}, test_signer::test_signer, }, @@ -163,7 +163,7 @@ pub mod tests { fn c2pa_io_parse() { let path = fixture_path("C.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let temp_path = temp_dir_path(&temp_dir, "test.c2pa"); let c2pa_io = C2paIO {}; diff --git a/sdk/src/asset_handlers/jpeg_io.rs b/sdk/src/asset_handlers/jpeg_io.rs index e24e8a9e8..b3b0757f7 100644 --- a/sdk/src/asset_handlers/jpeg_io.rs +++ b/sdk/src/asset_handlers/jpeg_io.rs @@ -27,7 +27,6 @@ use img_parts::{ Bytes, DynImage, }; use serde_bytes::ByteBuf; -use tempfile::Builder; use crate::{ assertions::{BoxMap, C2PA_BOXHASH}, @@ -37,7 +36,10 @@ use crate::{ RemoteRefEmbedType, }, error::{Error, Result}, - utils::xmp_inmemory_utils::{add_provenance, MIN_XMP}, + utils::{ + io_utils::tempfile_builder, + xmp_inmemory_utils::{add_provenance, MIN_XMP}, + }, }; static SUPPORTED_TYPES: [&str; 3] = ["jpg", "jpeg", "image/jpeg"]; @@ -481,10 +483,7 @@ impl AssetIO for JpegIO { .open(asset_path) .map_err(Error::IoError)?; - let mut temp_file = Builder::new() - .prefix("c2pa_temp") - .rand_bytes(5) - .tempfile()?; + let mut temp_file = tempfile_builder("c2pa_temp")?; self.write_cai(&mut input_stream, &mut temp_file, store_bytes)?; diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index f1ca99870..d388dec93 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1116,10 +1116,7 @@ mod tests { use wasm_bindgen_test::*; use super::*; - #[cfg(any( - feature = "openssl_sign", - all(target_arch = "wasm32", not(target_os = "wasi")) - ))] + #[cfg(any(feature = "openssl_sign", target_arch = "wasm32"))] use crate::{assertions::BoxHash, asset_handlers::jpeg_io::JpegIO}; use crate::{ hash_stream_by_alg, @@ -1193,7 +1190,7 @@ mod tests { .to_string() } - #[cfg(all(feature = "openssl_sign", not(target_arch = "wasm32")))] + #[cfg(any(feature = "file_io", feature = "openssl_sign"))] const TEST_IMAGE_CLEAN: &[u8] = include_bytes!("../tests/fixtures/IMG_0003.jpg"); const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); const TEST_THUMBNAIL: &[u8] = include_bytes!("../tests/fixtures/thumbnail.jpg"); @@ -1361,8 +1358,10 @@ mod tests { #[test] #[cfg(feature = "file_io")] fn test_builder_sign_file() { + use crate::utils::io_utils::tempdirectory; + let source = "tests/fixtures/CA.jpg"; - let dir = tempfile::tempdir().unwrap(); + let dir = tempdirectory().unwrap(); let dest = dir.path().join("test_file.jpg"); let mut builder = Builder::from_json(&manifest_json()).unwrap(); @@ -1596,8 +1595,9 @@ mod tests { all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test )] + #[cfg_attr(target_os = "wasi", wstd::test)] #[cfg(any( - all(target_arch = "wasm32", not(target_os = "wasi")), + target_arch = "wasm32", all(feature = "openssl_sign", feature = "file_io") ))] async fn test_builder_box_hashed_embeddable() { diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index d991f8ecc..98f5dc068 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -268,7 +268,7 @@ mod tests { use c2pa_crypto::raw_signature::SigningAlg; use super::sign_claim; - #[cfg(all(feature = "openssl_sign", not(target_arch = "wasm32")))] + #[cfg(all(any(feature = "openssl_sign", target_os = "wasi"), feature = "file_io"))] use crate::utils::test_signer::async_test_signer; use crate::{claim::Claim, utils::test_signer::test_signer, Result, Signer}; @@ -288,8 +288,9 @@ mod tests { assert_eq!(cose_sign1.len(), box_size); } - #[cfg(all(feature = "openssl_sign", feature = "file_io"))] + #[cfg(all(any(feature = "openssl_sign", target_os = "wasi"), feature = "file_io"))] #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_sign_claim_async() { use c2pa_crypto::raw_signature::SigningAlg; diff --git a/sdk/src/reader.rs b/sdk/src/reader.rs index fa650a884..109fca326 100644 --- a/sdk/src/reader.rs +++ b/sdk/src/reader.rs @@ -520,10 +520,14 @@ pub mod tests { #[cfg(feature = "file_io")] /// Test that the reader can validate a file with nested assertion errors fn test_reader_to_folder() -> Result<()> { + use crate::utils::{io_utils::tempdirectory, test::temp_dir_path}; + let reader = Reader::from_file("tests/fixtures/CACAE-uri-CA.jpg")?; assert_eq!(reader.validation_status(), None); - reader.to_folder("../target/reader_folder")?; - assert!(std::path::Path::new("../target/reader_folder/manifest.json").exists()); + let temp_dir = tempdirectory().unwrap(); + reader.to_folder(temp_dir.path())?; + let path = temp_dir_path(&temp_dir, "manifest.json"); + assert!(path.exists()); Ok(()) } } diff --git a/sdk/src/settings.rs b/sdk/src/settings.rs index 1f932e3a9..df30a8b7a 100644 --- a/sdk/src/settings.rs +++ b/sdk/src/settings.rs @@ -412,6 +412,8 @@ pub mod tests { use std::sync::Mutex; use super::*; + #[cfg(feature = "file_io")] + use crate::utils::io_utils::tempdirectory; // prevent tests from polluting the results of each other because of Rust unit test concurrency static PROTECT: Mutex = Mutex::new(1); // prevent tests from polluting the results of each other @@ -542,7 +544,7 @@ pub mod tests { fn test_save_load() { let _protect = PROTECT.lock().unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let op = crate::utils::test::temp_dir_path(&temp_dir, "sdk_config.json"); save_settings_as_json(&op).unwrap(); @@ -560,7 +562,7 @@ pub mod tests { fn test_save_load_from_string() { let _protect = PROTECT.lock().unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let op = crate::utils::test::temp_dir_path(&temp_dir, "sdk_config.json"); save_settings_as_json(&op).unwrap(); diff --git a/sdk/src/store.rs b/sdk/src/store.rs index a6f8ae239..4101dc1a9 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -32,11 +32,6 @@ use log::error; #[cfg(feature = "v1_api")] use crate::jumbf_io::save_jumbf_to_memory; -#[cfg(feature = "file_io")] -use crate::jumbf_io::{ - get_file_extension, get_supported_file_extension, load_jumbf_from_file, object_locations, - remove_jumbf_from_file, save_jumbf_to_file, -}; use crate::{ assertion::{ Assertion, AssertionBase, AssertionData, AssertionDecodeError, AssertionDecodeErrorCause, @@ -77,6 +72,14 @@ use crate::{ utils::{hash_utils::HashRange, io_utils::stream_len, patch::patch_bytes}, validation_status, AsyncSigner, RemoteSigner, Signer, }; +#[cfg(feature = "file_io")] +use crate::{ + jumbf_io::{ + get_file_extension, get_supported_file_extension, load_jumbf_from_file, object_locations, + remove_jumbf_from_file, save_jumbf_to_file, + }, + utils::io_utils::tempdirectory, +}; const MANIFEST_STORE_EXT: &str = "c2pa"; // file extension for external manifests @@ -2588,7 +2591,8 @@ impl Store { dest_path: &Path, ) -> Result> { // set up temp dir, contents auto deleted - let td = tempfile::TempDir::new()?; + + let td = tempdirectory()?; let temp_path = td.path(); let temp_file = temp_path.join( dest_path @@ -2642,7 +2646,7 @@ impl Store { dest_path: &Path, ) -> Result> { // set up temp dir, contents auto deleted - let td = tempfile::TempDir::new()?; + let td = tempdirectory()?; let temp_path = td.path(); let temp_file = temp_path.join( dest_path @@ -2698,7 +2702,7 @@ impl Store { dest_path: &Path, ) -> Result> { // set up temp dir, contents auto deleted - let td = tempfile::TempDir::new()?; + let td = tempdirectory()?; let temp_path = td.path(); let temp_file = temp_path.join( dest_path @@ -3722,7 +3726,6 @@ pub mod tests { use memchr::memmem; use serde::Serialize; use sha2::{Digest, Sha256}; - use tempfile::tempdir; use super::*; use crate::{ @@ -3771,7 +3774,7 @@ pub mod tests { fn test_jumbf_generation() { // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "test-image.jpg"); // Create claims store. @@ -3886,7 +3889,7 @@ pub mod tests { use crate::ClaimGeneratorInfo; let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "test-image.jpg"); // Create claims store. @@ -4002,7 +4005,7 @@ pub mod tests { fn test_unknown_asset_type_generation() { // test adding to actual image let ap = fixture_path("unsupported_type.txt"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "unsupported_type.txt"); // Create claims store. @@ -4083,7 +4086,7 @@ pub mod tests { fn test_detects_unverifiable_signature() { // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "test-image-unverified.jpg"); let mut store = Store::new(); @@ -4111,7 +4114,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "test-image-expired-cert.jpg"); let mut store = Store::new(); @@ -4149,7 +4152,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path("prerelease.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "replacement_test.jpg"); // grab jumbf from original @@ -4168,13 +4171,14 @@ pub mod tests { assert_eq!(memmem::find(&buf, &original_jumbf[0..1024]), None); } - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_jumbf_generation_async() { let signer = async_test_signer(SigningAlg::Ps256); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "test-async.jpg"); // Create claims store. @@ -4234,11 +4238,12 @@ pub mod tests { assert!(errors.is_empty()); } - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_jumbf_generation_remote() { // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "test-async.jpg"); // Create claims store. @@ -4277,7 +4282,7 @@ pub mod tests { fn test_png_jumbf_generation() { // test adding to actual image let ap = fixture_path("libpng-test.png"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "libpng-test-c2pa.png"); // Create claims store. @@ -4373,7 +4378,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_arw_jumbf_generation() { let ap = fixture_path("sample1.arw"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "ssample1.arw"); // Create claims store. @@ -4446,7 +4451,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_nef_jumbf_generation() { let ap = fixture_path("sample1.nef"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "ssample1.nef"); // Create claims store. @@ -4520,7 +4525,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_wav_jumbf_generation() { let ap = fixture_path("sample1.wav"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "ssample1.wav"); // Create claims store. @@ -4594,7 +4599,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_avi_jumbf_generation() { let ap = fixture_path("test.avi"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "test.avi"); // Create claims store. @@ -4668,7 +4673,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_webp_jumbf_generation() { let ap = fixture_path("sample1.webp"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "sample1.webp"); // Create claims store. @@ -4742,7 +4747,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_heic() { let ap = fixture_path("sample1.heic"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "sample1.heic"); // Create claims store. @@ -4786,7 +4791,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_avif() { let ap = fixture_path("sample1.avif"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "sample1.avif"); // Create claims store. @@ -4830,7 +4835,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_heif() { let ap = fixture_path("sample1.heif"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "sample1.heif"); // Create claims store. @@ -4970,7 +4975,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "earth_apollo17.jpg"); // get default store with default claim @@ -5008,7 +5013,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "earth_apollo17.jpg"); // get default store with default claim @@ -5045,7 +5050,7 @@ pub mod tests { search_bytes: &[u8], replace_bytes: &[u8], ) -> impl StatusTracker { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let path = temp_fixture_path(&temp_dir, fixture_name); patch_file(&path, search_bytes, replace_bytes).expect("patch_file"); let mut report = DetailedStatusTracker::default(); @@ -5063,7 +5068,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "update_manifest.jpg"); // get default store with default claim @@ -5225,7 +5230,7 @@ pub mod tests { fn test_bmff_jumbf_generation() { // test adding to actual image let ap = fixture_path("video1.mp4"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "video1.mp4"); // Create claims store. @@ -5311,7 +5316,7 @@ pub mod tests { fn test_external_manifest_sidecar() { // test adding to actual image let ap = fixture_path("libpng-test.png"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "libpng-test-c2pa.png"); let sidecar = op.with_extension(MANIFEST_STORE_EXT); @@ -5350,7 +5355,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path(file_name); let extension = ap.extension().unwrap().to_str().unwrap(); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let mut op = temp_dir_path(&temp_dir, file_name); op.set_extension(extension); @@ -5419,7 +5424,7 @@ pub mod tests { fn test_user_guid_external_manifest_embedded() { // test adding to actual image let ap = fixture_path("libpng-test.png"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "libpng-test-c2pa.png"); let sidecar = op.with_extension(MANIFEST_STORE_EXT); @@ -5471,7 +5476,7 @@ pub mod tests { fn test_external_manifest_from_memory() { // test adding to actual image let ap = fixture_path("libpng-test.png"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "libpng-test-c2pa.png"); let sidecar = op.with_extension(MANIFEST_STORE_EXT); @@ -5527,7 +5532,8 @@ pub mod tests { } } - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] async fn test_jumbf_generation_stream() { let file_buffer = include_bytes!("../tests/fixtures/earth_apollo17.jpg").to_vec(); // convert buffer to cursor with Read/Write/Seek capability @@ -5575,7 +5581,7 @@ pub mod tests { fn test_tiff_jumbf_generation() { // test adding to actual image let ap = fixture_path("TUSCANY.TIF"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "TUSCANY-OUTPUT.TIF"); // Create claims store. @@ -5641,7 +5647,8 @@ pub mod tests { } } - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] #[cfg(feature = "file_io")] async fn test_boxhash_embeddable_manifest_async() { // test adding to actual image @@ -5705,7 +5712,7 @@ pub mod tests { out_stream.write_all(&after_buf).unwrap(); // save to output file - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); let mut output_file = std::fs::OpenOptions::new() .read(true) @@ -5790,7 +5797,7 @@ pub mod tests { out_stream.write_all(&after_buf).unwrap(); // save to output file - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); let mut output_file = std::fs::OpenOptions::new() .read(true) @@ -5808,7 +5815,8 @@ pub mod tests { assert!(errors.is_empty()); } - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] #[cfg(feature = "file_io")] async fn test_datahash_embeddable_manifest_async() { // test adding to actual image @@ -5832,7 +5840,7 @@ pub mod tests { .get_data_hashed_manifest_placeholder(signer.reserve_size(), "jpeg") .unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); let mut output_file = std::fs::OpenOptions::new() .read(true) @@ -5901,7 +5909,7 @@ pub mod tests { .get_data_hashed_manifest_placeholder(Signer::reserve_size(&signer), "jpeg") .unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); let mut output_file = std::fs::OpenOptions::new() .read(true) @@ -5972,7 +5980,7 @@ pub mod tests { .get_data_hashed_manifest_placeholder(Signer::reserve_size(&signer), "jpeg") .unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); let mut output_file = std::fs::OpenOptions::new() .read(true) @@ -6067,7 +6075,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path("C.jpg"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let op = temp_dir_path(&temp_dir, "C-placed.jpg"); // Create claims store. @@ -6260,7 +6268,8 @@ pub mod tests { // std::fs::write("target/test.jpg", result).unwrap(); } - #[actix::test] + #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] #[cfg(feature = "openssl_sign")] async fn test_async_dynamic_assertions() { use async_trait::async_trait; @@ -6320,7 +6329,8 @@ pub mod tests { } } - #[async_trait::async_trait] + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl crate::AsyncSigner for DynamicSigner { async fn sign(&self, data: Vec) -> crate::error::Result> { self.0.sign(data).await @@ -6401,7 +6411,7 @@ pub mod tests { fn test_fragmented_jumbf_generation() { // test adding to actual image - let tempdir = tempdir().expect("temp dir"); + let tempdir = tempdirectory().expect("temp dir"); let output_path = tempdir.into_path(); // search folders for init segments diff --git a/sdk/src/utils/io_utils.rs b/sdk/src/utils/io_utils.rs index ed2a4e3cc..6320c77b7 100644 --- a/sdk/src/utils/io_utils.rs +++ b/sdk/src/utils/io_utils.rs @@ -167,7 +167,7 @@ pub(crate) fn tempfile_builder + Sized>(prefix: T) -> Result Result { +pub fn tempdirectory() -> Result { #[cfg(target_os = "wasi")] return TempDir::new_in("/").map_err(Error::IoError); diff --git a/sdk/tests/common/mod.rs b/sdk/tests/common/mod.rs index ddc2bb371..c6d8c3e56 100644 --- a/sdk/tests/common/mod.rs +++ b/sdk/tests/common/mod.rs @@ -20,6 +20,9 @@ use std::{ path::{Path, PathBuf}, }; +#[allow(unused)] // different code path for WASI +use tempfile::{tempdir, TempDir}; + use c2pa::{format_from_path, Reader, Result}; pub use compare_readers::compare_readers; #[allow(unused)] @@ -110,3 +113,12 @@ pub fn check_validation_status(reader: &Reader, code: &str) { panic!("Expected to find validation status"); } } + +#[allow(unused)] +pub fn tempdirectory() -> Result { + #[cfg(target_os = "wasi")] + return TempDir::new_in("/").map_err(c2pa::Error::IoError); + + #[cfg(not(target_os = "wasi"))] + return tempdir().map_err(c2pa::Error::IoError); +} diff --git a/sdk/tests/integration.rs b/sdk/tests/integration.rs index 0603c840e..20ac6efb0 100644 --- a/sdk/tests/integration.rs +++ b/sdk/tests/integration.rs @@ -13,8 +13,10 @@ /// Complete functional integration test with parent and ingredients. // Isolate from wasm by wrapping in module. + #[cfg(feature = "file_io")] mod integration_1 { + use std::io; use std::path::PathBuf; use c2pa::{ @@ -24,7 +26,8 @@ mod integration_1 { Builder, ClaimGeneratorInfo, Ingredient, Reader, Result, Signer, }; use c2pa_crypto::raw_signature::SigningAlg; - use tempfile::tempdir; + #[allow(unused)] // different code path for WASI + use tempfile::{tempdir, TempDir}; //const GENERATOR: &str = "app"; @@ -35,14 +38,28 @@ mod integration_1 { let _protect = PROTECT.lock().unwrap(); // sign and embed into the target file + #[cfg(target_os = "wasi")] + let mut signcert_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut signcert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); signcert_path.push("tests/fixtures/certs/ps256.pub"); + #[cfg(target_os = "wasi")] + let mut pkey_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut pkey_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); pkey_path.push("tests/fixtures/certs/ps256.pem"); create_signer::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None) .expect("get_signer_from_files") } + fn tempdirectory() -> io::Result { + #[cfg(target_os = "wasi")] + return TempDir::new_in("/"); + + #[cfg(not(target_os = "wasi"))] + return tempdir(); + } + fn configure_trust( trust_anchors: Option, allowed_list: Option, @@ -91,11 +108,18 @@ mod integration_1 { #[test] #[cfg(feature = "file_io")] fn test_embed_manifest() -> Result<()> { + println!("HERE"); // set up parent and destination paths - let dir = tempdir()?; + let dir = tempdirectory()?; let output_path = dir.path().join("test_file.jpg"); + #[cfg(target_os = "wasi")] + let mut parent_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut parent_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); parent_path.push("tests/fixtures/earth_apollo17.jpg"); + #[cfg(target_os = "wasi")] + let mut ingredient_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut ingredient_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); ingredient_path.push("tests/fixtures/libpng-test.png"); @@ -174,9 +198,12 @@ mod integration_1 { #[cfg(feature = "file_io")] fn test_embed_json_manifest() -> Result<()> { // set up parent and destination paths - let dir = tempdir()?; + let dir = tempdirectory()?; let output_path = dir.path().join("test_file.jpg"); + #[cfg(target_os = "wasi")] + let mut fixture_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixture_path.push("tests/fixtures"); @@ -186,9 +213,15 @@ mod integration_1 { manifest_path.push("manifest.json"); let json = std::fs::read_to_string(manifest_path)?; + // + // WASI does not support canonicalize(), the path is canonical to begin with + #[cfg(target_os = "wasi")] + let base_path = fixture_path; + #[cfg(not(target_os = "wasi"))] + let base_path = fixture_path.canonicalize()?; let mut builder = Builder::from_json(&json)?; - builder.base_path = Some(fixture_path.canonicalize()?); + builder.base_path = Some(base_path); // sign and embed into the target file let signer = get_temp_signer(); @@ -214,9 +247,12 @@ mod integration_1 { #[cfg(feature = "file_io")] fn test_embed_bmff_manifest() -> Result<()> { // set up parent and destination paths - let dir = tempdir()?; + let dir = tempdirectory()?; let output_path = dir.path().join("test_bmff.heic"); + #[cfg(target_os = "wasi")] + let mut fixture_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixture_path.push("tests/fixtures"); @@ -227,8 +263,14 @@ mod integration_1 { let json = std::fs::read_to_string(manifest_path)?; + // WASI does not support canonicalize(), the path is canonical to begin with + #[cfg(target_os = "wasi")] + let base_path = fixture_path; + #[cfg(not(target_os = "wasi"))] + let base_path = fixture_path.canonicalize()?; + let mut builder = Builder::from_json(&json)?; - builder.base_path = Some(fixture_path.canonicalize()?); + builder.base_path = Some(base_path); // sign and embed into the target file let signer = get_temp_signer(); @@ -302,9 +344,12 @@ mod integration_1 { // set up parent and destination paths use std::io::Seek; - let dir = tempdir()?; + let dir = tempdirectory()?; let output_path = dir.path().join("test_file.jpg"); + #[cfg(target_os = "wasi")] + let mut fixture_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixture_path.push("tests/fixtures"); let mut manifest_path = fixture_path.clone(); @@ -370,9 +415,12 @@ mod integration_1 { // set up parent and destination paths use std::io::Seek; - let dir = tempdir()?; + let dir = tempdirectory()?; let output_path = dir.path().join("video1.mp4"); + #[cfg(target_os = "wasi")] + let mut fixture_path = PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixture_path.push("tests/fixtures"); let mut manifest_path = fixture_path.clone(); diff --git a/sdk/tests/test_builder.rs b/sdk/tests/test_builder.rs index 19ea91cdf..a4dad8106 100644 --- a/sdk/tests/test_builder.rs +++ b/sdk/tests/test_builder.rs @@ -61,11 +61,11 @@ fn test_builder_riff() -> Result<()> { #[test] #[cfg(feature = "file_io")] fn test_builder_fragmented() -> Result<()> { - use tempfile::tempdir; + use common::tempdirectory; let manifest_def = include_str!("fixtures/simple_manifest.json"); let mut builder = Builder::from_json(manifest_def)?; - let tempdir = tempdir().expect("temp dir"); + let tempdir = tempdirectory().expect("temp dir"); let output_path = tempdir.into_path(); let mut init_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); init_path.push("tests/fixtures/bunny/**/BigBuckBunny_2s_init.mp4"); From 1faa79a2ced8b3fb1530d8398c0c37237578ca8a Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Mon, 10 Feb 2025 10:12:10 -0500 Subject: [PATCH 6/8] feat: tests passing for --all-features --- .github/workflows/ci.yml | 5 +- Cargo.lock | 1 + sdk/Cargo.toml | 4 +- sdk/src/asset_handlers/pdf.rs | 151 ++++++++++++++++++++++---------- sdk/src/cose_sign.rs | 2 +- sdk/src/error.rs | 2 +- sdk/src/manifest.rs | 70 ++++++++++----- sdk/src/manifest_store.rs | 4 +- sdk/src/resource_store.rs | 5 +- sdk/src/store.rs | 78 ++++++++++++++++- sdk/src/utils/io_utils.rs | 2 +- sdk/tests/integration.rs | 15 +++- sdk/tests/v2_api_integration.rs | 1 - 13 files changed, 257 insertions(+), 83 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e967fd31a..05436cd24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -321,7 +321,6 @@ jobs: fi wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-${ARCH}-${RUNNER_OS}.tar.gz tar xvf wasi-sdk-25.0-${ARCH}-${RUNNER_OS}.tar.gz - ls wasi* mv $(echo wasi-sdk-25.0-${ARCH}-${RUNNER_OS} | tr '[:upper:]' '[:lower:]') /opt/wasi-sdk - name: Add wasm32-wasip2 target @@ -332,11 +331,11 @@ jobs: - name: Run WASI tests (c2pa-rs) env: - CARGO_TARGET_WASM32_WASIP2_RUNNER: "wasmtime -S common --dir ." + CARGO_TARGET_WASM32_WASIP2_RUNNER: "wasmtime -S cli -S http --dir ." 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 + run: cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto --all-features test-direct-minimal-versions: name: Unit tests with minimum versions of direct dependencies diff --git a/Cargo.lock b/Cargo.lock index 76f2dd6b2..030c02555 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -781,6 +781,7 @@ dependencies = [ "ureq", "url", "uuid", + "wasi 0.14.1+wasi-0.2.3", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 957420cc8..fd41fbf1c 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -30,7 +30,7 @@ add_thumbnails = ["image"] file_io = ["openssl_sign"] serialize_thumbnails = [] no_interleaved_io = ["file_io"] -fetch_remote_manifests = [] +fetch_remote_manifests = ["dep:wasi"] openssl = [] openssl_sign = ["openssl"] json_schema = ["dep:schemars", "c2pa-crypto/json_schema"] @@ -143,7 +143,7 @@ image = { version = "0.24.7", default-features = false, features = [ [target.'cfg(target_os = "wasi")'.dependencies] getrandom = "0.2.7" - +wasi = {version = "0.14", optional = true} [target.'cfg(all(target_arch = "wasm32",not(target_os = "wasi")))'.dependencies] chrono = { version = "0.4.39", default-features = false, features = [ diff --git a/sdk/src/asset_handlers/pdf.rs b/sdk/src/asset_handlers/pdf.rs index 370b1ee96..b9ea0e97f 100644 --- a/sdk/src/asset_handlers/pdf.rs +++ b/sdk/src/asset_handlers/pdf.rs @@ -544,30 +544,39 @@ mod tests { use super::*; - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - #[cfg(target_arch = "wasm32")] + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_loads_pdf_from_bytes() { let bytes = include_bytes!("../../tests/fixtures/basic.pdf"); let pdf_result = Pdf::from_bytes(bytes); assert!(pdf_result.is_ok()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_loads_pdf_from_bytes_with_invalid_file() { let bytes = include_bytes!("../../tests/fixtures/XCA.jpg"); let pdf_result = Pdf::from_bytes(bytes); assert!(matches!(pdf_result, Err(Error::UnableToReadPdf(_)))); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_is_password_protected() { let bytes = include_bytes!("../../tests/fixtures/basic-password.pdf"); let pdf_result = Pdf::from_bytes(bytes).unwrap(); @@ -578,16 +587,22 @@ mod tests { assert!(!pdf.is_password_protected()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_has_c2pa_manifest_on_file_without_manifest() { let bytes = include_bytes!("../../tests/fixtures/basic.pdf"); let pdf = Pdf::from_bytes(bytes).unwrap(); assert!(!pdf.has_c2pa_manifest()) } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_has_c2pa_manifest_on_file_with_manifest() { let bytes = include_bytes!("../../tests/fixtures/basic.pdf"); let mut pdf = Pdf::from_bytes(bytes).unwrap(); @@ -597,8 +612,11 @@ mod tests { assert!(pdf.has_c2pa_manifest()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_adds_embedded_file_spec_to_pdf_stream() { let bytes = include_bytes!("../../tests/fixtures/express.pdf"); let mut pdf = Pdf::from_bytes(bytes).unwrap(); @@ -615,8 +633,11 @@ mod tests { assert_eq!(stream.unwrap().as_stream().unwrap().content, bytes); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_write_manifest_as_annotation() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/express.pdf")).unwrap(); assert!(!pdf.has_c2pa_manifest()); @@ -624,8 +645,11 @@ mod tests { assert!(pdf.has_c2pa_manifest()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_write_manifest_bytes_to_pdf_with_existing_annotations() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic-annotation.pdf")).unwrap(); @@ -633,8 +657,11 @@ mod tests { assert!(pdf.has_c2pa_manifest()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_add_manifest_to_embedded_files() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); pdf.write_manifest_as_embedded_file(vec![10u8, 20u8]) @@ -643,8 +670,11 @@ mod tests { assert!(pdf.has_c2pa_manifest()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_add_manifest_to_embedded_files_attachments_present() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic-attachments.pdf")).unwrap(); @@ -654,8 +684,11 @@ mod tests { assert!(pdf.has_c2pa_manifest()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_save_to() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); assert!(!pdf.has_c2pa_manifest()); @@ -670,8 +703,11 @@ mod tests { assert!(saved_pdf.has_c2pa_manifest()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_reads_manifest_bytes_for_embedded_files_manifest() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/express.pdf")).unwrap(); assert!(!pdf.has_c2pa_manifest()); @@ -687,8 +723,11 @@ mod tests { )); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_reads_manifest_bytes_for_annotation_manifest() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); assert!(!pdf.has_c2pa_manifest()); @@ -704,16 +743,22 @@ mod tests { )); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_read_manifest_bytes_from_pdf_without_bytes_returns_none() { let pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); assert!(!pdf.has_c2pa_manifest()); assert!(matches!(pdf.read_manifest_bytes(), Ok(None))); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_read_manifest_bytes_from_pdf_with_other_af_relationship_returns_none() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); pdf.document @@ -724,8 +769,11 @@ mod tests { assert!(matches!(pdf.read_manifest_bytes(), Ok(None))); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_read_pdf_with_associated_file_that_is_not_manifest() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); pdf.document @@ -736,22 +784,31 @@ mod tests { assert!(matches!(pdf.read_manifest_bytes(), Ok(None))); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_read_xmp_on_pdf_with_none() { let pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic-no-xmp.pdf")).unwrap(); assert_eq!(pdf.read_xmp(), None); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_read_xmp_on_pdf_with_some_metadata() { let pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); assert!(pdf.read_xmp().is_some()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_remove_manifest_bytes_from_file_without_c2pa_returns_error() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); @@ -761,8 +818,11 @@ mod tests { )); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_remove_manifest_from_file_with_annotation_based_manifest() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); let manifest_bytes = vec![0u8, 1u8, 1u8, 2u8, 3u8]; @@ -774,8 +834,11 @@ mod tests { assert!(!pdf.has_c2pa_manifest()); } - #[cfg_attr(not(target_arch = "wasm32"), test)] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[test] + #[cfg_attr( + all(target_arch = "wasm32", not(target_os = "wasi")), + wasm_bindgen_test + )] fn test_remove_manifest_from_file_with_embedded_file_based_manifest() { let mut pdf = Pdf::from_bytes(include_bytes!("../../tests/fixtures/basic.pdf")).unwrap(); let manifest_bytes = vec![0u8, 1u8, 1u8, 2u8, 3u8]; diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 98f5dc068..3cc757250 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -357,7 +357,7 @@ mod tests { let _cose_sign1 = sign_claim(&claim_bytes, &signer, box_size); - #[cfg(feature = "openssl")] // there is no verify on sign when openssl is disabled + #[cfg(any(feature = "openssl", target_os = "wasi"))] // there is no verify on sign when openssl is disabled assert!(_cose_sign1.is_err()); } } diff --git a/sdk/src/error.rs b/sdk/src/error.rs index 0dc95dfa4..6abedfc07 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -282,7 +282,7 @@ pub enum Error { JsonError(#[from] serde_json::Error), #[error(transparent)] - #[cfg(all(not(target_arch = "wasm32"), feature = "add_thumbnails"))] + #[cfg(feature = "add_thumbnails")] ImageError(#[from] image::ImageError), #[error(transparent)] diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 3610a6bd4..6c3a6412d 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1549,11 +1549,11 @@ pub(crate) mod tests { use std::io::Cursor; + #[cfg(feature = "file_io")] + use crate::utils::io_utils::tempdirectory; use c2pa_crypto::raw_signature::SigningAlg; #[cfg(feature = "file_io")] use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; - #[cfg(feature = "file_io")] - use tempfile::tempdir; #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; @@ -1644,7 +1644,7 @@ pub(crate) mod tests { } // copy an image to use as our target - let dir = tempdir().expect("temp dir"); + let dir = tempdirectory().expect("temp dir"); let test_output = dir.path().join("wc_embed_test.jpg"); //embed a claim generated from this manifest @@ -1671,7 +1671,7 @@ pub(crate) mod tests { fn ws_bad_assertion() { // copy an image to use as our target for embedding let ap = fixture_path(TEST_SMALL_JPEG); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let test_output = temp_dir_path(&temp_dir, "ws_bad_assertion.jpg"); std::fs::copy(ap, test_output).expect("copy"); @@ -1701,7 +1701,7 @@ pub(crate) mod tests { fn ws_valid_labeled_assertion() { // copy an image to use as our target for embedding let ap = fixture_path(TEST_SMALL_JPEG); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let test_output = temp_dir_path(&temp_dir, "ws_bad_assertion.jpg"); std::fs::copy(ap, test_output).expect("copy"); @@ -1795,7 +1795,7 @@ pub(crate) mod tests { fn test_redaction() { const ASSERTION_LABEL: &str = "stds.schema-org.CreativeWork"; - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let output2 = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); @@ -1870,7 +1870,7 @@ pub(crate) mod tests { #[cfg(feature = "file_io")] #[allow(deprecated)] fn test_action_assertion_redaction_error() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let parent_output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); // Create parent with a c2pa_action type assertion. @@ -1937,9 +1937,10 @@ pub(crate) mod tests { #[cfg(all(feature = "file_io", feature = "openssl_sign"))] #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] #[allow(deprecated)] async fn test_embed_async_sign() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let async_signer = async_test_signer(SigningAlg::Ps256); @@ -1958,9 +1959,10 @@ pub(crate) mod tests { #[cfg(all(feature = "file_io", feature = "openssl_sign"))] #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] #[allow(deprecated)] async fn test_embed_remote_sign() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let remote_signer = temp_remote_signer(); @@ -1981,7 +1983,7 @@ pub(crate) mod tests { #[test] #[allow(deprecated)] fn test_embed_user_label() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let my_guid = static_test_uuid(); let signer = test_signer(SigningAlg::Ps256); @@ -2003,7 +2005,7 @@ pub(crate) mod tests { #[test] #[allow(deprecated)] fn test_embed_sidecar_user_label() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let sidecar = output.with_extension("c2pa"); let fp = format!("file:/{}", sidecar.to_str().unwrap()); @@ -2185,13 +2187,17 @@ pub(crate) mod tests { //println!("{manifest_store}");main } - #[cfg_attr(feature = "openssl_sign", actix::test)] + #[cfg_attr( + all(not(target_arch = "wasm32"), feature = "openssl_sign"), + actix::test + )] #[cfg_attr( all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test )] + #[cfg_attr(target_os = "wasi", wstd::test)] #[cfg(any( - all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_arch = "wasm32"), all(feature = "openssl_sign", feature = "file_io") ))] async fn test_embed_from_memory_async() { @@ -2235,10 +2241,11 @@ pub(crate) mod tests { #[cfg(feature = "file_io")] #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] #[allow(deprecated)] /// Verify that an ingredient with error is reported on the ingredient and not on the manifest_store async fn test_embed_with_ingredient_error() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let signer = test_signer(SigningAlg::Ps256); @@ -2271,7 +2278,7 @@ pub(crate) mod tests { #[test] #[allow(deprecated)] fn test_embed_sidecar_with_parent_manifest() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let source = fixture_path("XCA.jpg"); let output = temp_dir.path().join("XCAplus.jpg"); let sidecar = output.with_extension("c2pa"); @@ -2302,7 +2309,7 @@ pub(crate) mod tests { #[test] #[allow(deprecated)] fn test_embed_user_thumbnail() { - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let signer = test_signer(SigningAlg::Ps256); @@ -2569,15 +2576,22 @@ pub(crate) mod tests { // println!("{manifest_store}"); } + // WASI cannot read files in the target directory #[test] - #[cfg(feature = "file_io")] + #[cfg(all(feature = "file_io", not(target_arch = "wasm32")))] fn from_json_with_files() { let mut manifest = Manifest::from_json(MANIFEST_JSON).unwrap(); + #[cfg(target_os = "wasi")] + let mut path = std::path::PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); path.push("tests/fixtures"); // the path we want to read files from manifest.with_base_path(path).expect("with_files"); // convert the manifest to a store let store = manifest.to_store().expect("to store"); + #[cfg(target_os = "wasi")] + let mut resource_path = std::path::PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut resource_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); resource_path.push("../target/tmp/manifest"); let m2 = Manifest::from_store( @@ -2595,10 +2609,13 @@ pub(crate) mod tests { #[test] #[allow(deprecated)] fn test_embed_from_json() { + #[cfg(target_os = "wasi")] + let mut fixtures = std::path::PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut fixtures = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixtures.push("tests/fixtures"); // the path we want to read files from - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let signer = test_signer(SigningAlg::Ps256); @@ -2622,10 +2639,13 @@ pub(crate) mod tests { fn test_embed_webp_from_json() { use crate::utils::test::TEST_WEBP; + #[cfg(target_os = "wasi")] + let mut fixtures = std::path::PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut fixtures = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixtures.push("tests/fixtures"); // the path we want to read files from - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_WEBP); let signer = test_signer(SigningAlg::Ps256); @@ -2647,10 +2667,13 @@ pub(crate) mod tests { #[cfg(feature = "file_io")] #[allow(deprecated)] fn test_create_file_based_ingredient() { + #[cfg(target_os = "wasi")] + let mut fixtures = std::path::PathBuf::from("/"); + #[cfg(not(target_os = "wasi"))] let mut fixtures = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixtures.push("tests/fixtures"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let mut manifest = Manifest::new("claim_generator"); @@ -2679,7 +2702,7 @@ pub(crate) mod tests { let mut fixtures = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); fixtures.push("tests/fixtures"); - let temp_dir = tempdir().expect("temp dir"); + let temp_dir = tempdirectory().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); let mut manifest = Manifest::new("claim_generator"); @@ -2749,7 +2772,7 @@ pub(crate) mod tests { .data_hash_placeholder(signer.reserve_size(), "jpeg") .unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); let mut output_file = std::fs::OpenOptions::new() .read(true) @@ -2794,6 +2817,7 @@ pub(crate) mod tests { #[cfg(all(feature = "file_io", feature = "openssl_sign"))] #[cfg_attr(not(target_arch = "wasm32"), actix::test)] + #[cfg_attr(target_os = "wasi", wstd::test)] #[allow(deprecated)] async fn test_data_hash_embeddable_manifest_remote_signed() { let ap = fixture_path("cloud.jpg"); @@ -2807,7 +2831,7 @@ pub(crate) mod tests { .data_hash_placeholder(signer.reserve_size(), "jpeg") .unwrap(); - let temp_dir = tempfile::tempdir().unwrap(); + let temp_dir = tempdirectory().unwrap(); let output = temp_dir_path(&temp_dir, "boxhash-out.jpg"); let mut output_file = std::fs::OpenOptions::new() .read(true) diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index 32ee1819b..cd86be18b 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -721,9 +721,9 @@ mod tests { println!("{manifest_store}"); } + // WASI cannot read files in the target directory #[test] - #[cfg(feature = "file_io")] - #[cfg(feature = "v1_api")] + #[cfg(all(feature = "file_io", feature = "v1_api", not(target_arch = "wasm32")))] #[allow(deprecated)] fn manifest_report_from_file_with_resources() { let manifest_store = ManifestStore::from_file_with_resources( diff --git a/sdk/src/resource_store.rs b/sdk/src/resource_store.rs index 0774b1b82..1aa0304ae 100644 --- a/sdk/src/resource_store.rs +++ b/sdk/src/resource_store.rs @@ -39,7 +39,10 @@ use crate::{ /// resources based on the `serialize_resources` flag. /// (Serialization is disabled by default.) pub(crate) fn skip_serializing_resources(_: &ResourceStore) -> bool { - !cfg!(feature = "serialize_thumbnails") || cfg!(test) || cfg!(not(target_arch = "wasm32")) + //TODO: Why is this disabled for wasm32? + !cfg!(feature = "serialize_thumbnails") + || cfg!(test) + || cfg!(not(all(target_arch = "wasm32", not(target_os = "wasi")))) } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 4101dc1a9..5fa7f1ce5 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -3229,7 +3229,7 @@ impl Store { } // fetch remote manifest if possible - #[cfg(feature = "fetch_remote_manifests")] + #[cfg(all(feature = "fetch_remote_manifests", not(target_os = "wasi")))] fn fetch_remote_manifest(url: &str) -> Result> { use conv::ValueFrom; use ureq::Error as uError; @@ -3276,6 +3276,82 @@ impl Store { } } + // fetch remote manifest if possible + #[cfg(all(feature = "fetch_remote_manifests", target_os = "wasi"))] + fn fetch_remote_manifest(url: &str) -> Result> { + use url::Url; + use wasi::http::{ + outgoing_handler, + types::{Fields, OutgoingRequest, Scheme}, + }; + + //const MANIFEST_CONTENT_TYPE: &str = "application/x-c2pa-manifest-store"; // todo verify once these are served + //const DEFAULT_MANIFEST_RESPONSE_SIZE: usize = 10 * 1024 * 1024; // 10 MB + let parsed_url = Url::parse(url) + .map_err(|e| Error::RemoteManifestFetch(format!("invalid URL: {}", e)))?; + let path_with_query = parsed_url[url::Position::BeforeHost..].to_string(); + + let request = OutgoingRequest::new(Fields::new()); + request.set_path_with_query(Some(&path_with_query)).unwrap(); + request.set_scheme(Some(&Scheme::Https)).unwrap(); + match outgoing_handler::handle(request, None) { + Ok(resp) => { + resp.subscribe().block(); + let response = resp + .get() + .expect("HTTP request response missing") + .expect("HTTP request response requested more than once") + .expect("HTTP request failed"); + if response.status() == 200 { + let raw_header = response.headers().get("Content-Length"); + if raw_header.first().map(|val| val.is_empty()).unwrap_or(true) { + return Err(Error::RemoteManifestFetch( + "url returned no content length".to_string(), + )); + } + let str_parsed_header = match std::str::from_utf8(raw_header.first().unwrap()) { + Ok(s) => s, + Err(e) => { + return Err(Error::RemoteManifestFetch(format!( + "error parsing content length header: {}", + e + ))) + } + }; + let content_length: usize = match str_parsed_header.parse() { + Ok(s) => s, + Err(e) => { + return Err(Error::RemoteManifestFetch(format!( + "error parsing content length header: {}", + e + ))) + } + }; + let body = { + let mut buf = Vec::with_capacity(content_length); + let response_body = response + .consume() + .expect("failed to get incoming request body"); + let mut stream = response_body + .stream() + .expect("failed to get response body stream"); + stream + .read_to_end(&mut buf) + .expect("failed to read response body"); + buf + }; + Ok(body) + } else { + Err(Error::RemoteManifestFetch(format!( + "fetch failed: code: {}", + response.status(), + ))) + } + } + Err(e) => Err(Error::RemoteManifestFetch(e.to_string())), + } + } + /// Handles remote manifests when file_io/fetch_remote_manifests feature is enabled fn handle_remote_manifest(ext_ref: &str) -> Result> { // verify provenance path is remote url diff --git a/sdk/src/utils/io_utils.rs b/sdk/src/utils/io_utils.rs index 6320c77b7..ed2a4e3cc 100644 --- a/sdk/src/utils/io_utils.rs +++ b/sdk/src/utils/io_utils.rs @@ -167,7 +167,7 @@ pub(crate) fn tempfile_builder + Sized>(prefix: T) -> Result Result { +pub(crate) fn tempdirectory() -> Result { #[cfg(target_os = "wasi")] return TempDir::new_in("/").map_err(Error::IoError); diff --git a/sdk/tests/integration.rs b/sdk/tests/integration.rs index 20ac6efb0..43a7b377d 100644 --- a/sdk/tests/integration.rs +++ b/sdk/tests/integration.rs @@ -108,7 +108,6 @@ mod integration_1 { #[test] #[cfg(feature = "file_io")] fn test_embed_manifest() -> Result<()> { - println!("HERE"); // set up parent and destination paths let dir = tempdirectory()?; let output_path = dir.path().join("test_file.jpg"); @@ -360,7 +359,12 @@ mod integration_1 { let json = std::fs::read_to_string(manifest_path)?; let mut manifest = Manifest::from_json(&json)?; - manifest.with_base_path(fixture_path.canonicalize()?)?; + // WASI does not support canonicalize(), but the path is canonical to begin with + #[cfg(target_os = "wasi")] + let base_path = fixture_path; + #[cfg(not(target_os = "wasi"))] + let base_path = fixture_path.canonicalize()?; + manifest.with_base_path(base_path)?; // sign and embed into the target file let signer = get_temp_signer(); @@ -431,7 +435,12 @@ mod integration_1 { let json = std::fs::read_to_string(manifest_path)?; let mut manifest = Manifest::from_json(&json)?; - manifest.with_base_path(fixture_path.canonicalize()?)?; + // WASI does not support canonicalize(), but the path is canonical to begin with + #[cfg(target_os = "wasi")] + let base_path = fixture_path; + #[cfg(not(target_os = "wasi"))] + let base_path = fixture_path.canonicalize()?; + manifest.with_base_path(base_path)?; // sign and embed into the target file let signer = get_temp_signer(); diff --git a/sdk/tests/v2_api_integration.rs b/sdk/tests/v2_api_integration.rs index 39189bd57..d26bcb195 100644 --- a/sdk/tests/v2_api_integration.rs +++ b/sdk/tests/v2_api_integration.rs @@ -13,7 +13,6 @@ /// Complete functional integration test with acquisitions and ingredients. // Isolate from wasm by wrapping in module. -#[cfg(not(target_arch = "wasm32"))] // wasm doesn't support ed25519 yet mod integration_v2 { use std::io::{Cursor, Seek}; From e1a5b40e5c56c473d491d4dc24beb7a8c5d4243e Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Mon, 10 Feb 2025 17:00:31 -0500 Subject: [PATCH 7/8] fix: rebase --- Cargo.lock | 116 +++++++++--------- .../crypto/src/tests/raw_signature/signers.rs | 9 +- .../src/tests/raw_signature/validators.rs | 4 +- sdk/Cargo.toml | 4 +- sdk/src/manifest.rs | 7 +- sdk/tests/common/mod.rs | 5 +- sdk/tests/integration.rs | 3 +- 7 files changed, 73 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 030c02555..ec9b0e2cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,7 +734,7 @@ dependencies = [ "coset", "ed25519-dalek", "extfmt", - "getrandom 0.2.15", + "getrandom", "glob", "hex", "hex-literal", @@ -750,7 +750,7 @@ dependencies = [ "memchr", "mockall", "mp4", - "pem 3.0.4", + "pem", "png_pong", "quick-xml", "rand", @@ -786,6 +786,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-bindgen-test", "web-sys", + "wstd", "x509-certificate", "x509-parser", "zip", @@ -811,7 +812,7 @@ dependencies = [ "der", "ecdsa", "ed25519-dalek", - "getrandom 0.2.15", + "getrandom", "hex", "js-sys", "nom", @@ -826,6 +827,7 @@ dependencies = [ "rasn", "rasn-ocsp", "rasn-pkix", + "ring", "rsa", "schemars", "serde", @@ -841,6 +843,7 @@ dependencies = [ "wasm-bindgen-test", "web-sys", "web-time", + "wstd", "x509-certificate", "x509-parser", ] @@ -865,7 +868,7 @@ dependencies = [ "log", "mockall", "openssl", - "pem 3.0.4", + "pem", "predicates", "reqwest", "serde", @@ -1103,7 +1106,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom", "once_cell", "tiny-keccak", ] @@ -1816,18 +1819,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "getrandom" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", -] - [[package]] name = "gimli" version = "0.31.1" @@ -2559,7 +2550,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -3083,16 +3074,6 @@ dependencies = [ "utf8-decode", ] -[[package]] -name = "pem" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b13fe415cdf3c8e44518e18a7c95a13431d9bdf6d15367d82b23c377fdd441a" -dependencies = [ - "base64 0.21.7", - "serde", -] - [[package]] name = "pem" version = "3.0.4" @@ -3428,7 +3409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", - "getrandom 0.2.15", + "getrandom", "rand", "ring 0.17.9", "rustc-hash", @@ -3497,7 +3478,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom", ] [[package]] @@ -3635,7 +3616,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom", "libredox", "thiserror 1.0.69", ] @@ -3755,10 +3736,12 @@ name = "ring" version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom", "libc", "untrusted 0.9.0", "windows-sys 0.52.0", @@ -3891,7 +3874,7 @@ checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring 0.17.9", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -4252,12 +4235,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -4406,13 +4383,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.16.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", - "getrandom 0.3.1", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -4735,12 +4712,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -4800,12 +4771,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.13.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" dependencies = [ - "getrandom 0.3.1", - "js-sys", + "getrandom", "serde", "wasm-bindgen", ] @@ -4864,9 +4834,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.1+wasi-0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "ad7df608df60a1c33e247881838b0f809512086e3e3bb1c18323b77eeb1f844e" dependencies = [ "wit-bindgen-rt", ] @@ -5164,9 +5134,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.8.0", ] @@ -5183,6 +5153,31 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "wstd" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26293c0587d2203a141282311221f9ba0048425a04b21d9fcff8fc62fc8ecd75" +dependencies = [ + "futures-core", + "http 1.2.0", + "itoa", + "pin-project-lite", + "slab", + "wasi 0.14.1+wasi-0.2.3", + "wstd-macro", +] + +[[package]] +name = "wstd-macro" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a8316f91fc3e508b03fdc979dec4ceeddb29744fc1c2c2ed825e00159f96f9" +dependencies = [ + "quote", + "syn 2.0.98", +] + [[package]] name = "wyz" version = "0.5.1" @@ -5194,20 +5189,21 @@ dependencies = [ [[package]] name = "x509-certificate" -version = "0.21.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5d27c90840e84503cf44364de338794d5d5680bdd1da6272d13f80b0769ee0" +checksum = "66534846dec7a11d7c50a74b7cdb208b9a581cad890b7866430d438455847c85" dependencies = [ "bcder", "bytes", "chrono", "der", "hex", - "pem 2.0.1", - "ring 0.16.20", + "pem", + "ring", "signature", "spki", "thiserror 1.0.69", + "zeroize", ] [[package]] diff --git a/internal/crypto/src/tests/raw_signature/signers.rs b/internal/crypto/src/tests/raw_signature/signers.rs index 846b6f79f..14d98697b 100644 --- a/internal/crypto/src/tests/raw_signature/signers.rs +++ b/internal/crypto/src/tests/raw_signature/signers.rs @@ -113,7 +113,8 @@ fn ed25519() { } #[test] -// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), +// wasm_bindgen_test)] fn ps256() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps256.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps256.priv"); @@ -135,7 +136,8 @@ fn ps256() { } #[test] -// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), +// wasm_bindgen_test)] fn ps384() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps384.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps384.priv"); @@ -157,7 +159,8 @@ fn ps384() { } #[test] -// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), +// wasm_bindgen_test)] fn ps512() { let cert_chain = include_bytes!("../fixtures/raw_signature/ps512.pub"); let private_key = include_bytes!("../fixtures/raw_signature/ps512.priv"); diff --git a/internal/crypto/src/tests/raw_signature/validators.rs b/internal/crypto/src/tests/raw_signature/validators.rs index 071642a85..146c2b5d0 100644 --- a/internal/crypto/src/tests/raw_signature/validators.rs +++ b/internal/crypto/src/tests/raw_signature/validators.rs @@ -96,8 +96,8 @@ fn es384() { } #[test] -// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), wasm_bindgen_test)] // ES512 not -// implemented +// #[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), +// wasm_bindgen_test)] // ES512 not implemented fn es512() { let signature = include_bytes!("../fixtures/raw_signature/es512.raw_sig"); let pub_key = include_bytes!("../fixtures/raw_signature/es512.pub_key"); diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index fd41fbf1c..4bda462a5 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -116,11 +116,11 @@ serde_with = "3.11.0" serde-transcode = "1.1.1" sha1 = "0.10.6" sha2 = "0.10.6" -tempfile = "3.15" +tempfile = "=3.15.0" thiserror = "2.0.8" treeline = "0.1.0" url = "2.5.3" -uuid = { version = "1.10.0", features = ["serde", "v4"] } +uuid = { version = "=1.12.0", features = ["serde", "v4"] } x509-certificate = "0.23.1" x509-parser = "0.16.0" zip = { version = "2.2.1", default-features = false } diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 6c3a6412d..9b1c03934 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1549,14 +1549,15 @@ pub(crate) mod tests { use std::io::Cursor; - #[cfg(feature = "file_io")] - use crate::utils::io_utils::tempdirectory; use c2pa_crypto::raw_signature::SigningAlg; #[cfg(feature = "file_io")] use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; + #[cfg(feature = "file_io")] + use crate::utils::io_utils::tempdirectory; + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); @@ -2197,7 +2198,7 @@ pub(crate) mod tests { )] #[cfg_attr(target_os = "wasi", wstd::test)] #[cfg(any( - all(target_arch = "wasm32"), + target_arch = "wasm32", all(feature = "openssl_sign", feature = "file_io") ))] async fn test_embed_from_memory_async() { diff --git a/sdk/tests/common/mod.rs b/sdk/tests/common/mod.rs index c6d8c3e56..f79cb6076 100644 --- a/sdk/tests/common/mod.rs +++ b/sdk/tests/common/mod.rs @@ -20,11 +20,10 @@ use std::{ path::{Path, PathBuf}, }; -#[allow(unused)] // different code path for WASI -use tempfile::{tempdir, TempDir}; - use c2pa::{format_from_path, Reader, Result}; pub use compare_readers::compare_readers; +#[allow(unused)] // different code path for WASI +use tempfile::{tempdir, TempDir}; #[allow(unused)] pub use test_signer::test_signer; diff --git a/sdk/tests/integration.rs b/sdk/tests/integration.rs index 43a7b377d..22577ae70 100644 --- a/sdk/tests/integration.rs +++ b/sdk/tests/integration.rs @@ -16,8 +16,7 @@ #[cfg(feature = "file_io")] mod integration_1 { - use std::io; - use std::path::PathBuf; + use std::{io, path::PathBuf}; use c2pa::{ assertions::{c2pa_action, Action, Actions}, From 345498746aa7ef6333cad0d357a71aebae0185aa Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Tue, 11 Feb 2025 11:56:45 -0500 Subject: [PATCH 8/8] fix: Working remote manifest fetch. --- Cargo.lock | 25 ++------ Makefile | 3 +- sdk/src/error.rs | 4 +- sdk/src/store.rs | 61 +++++++++---------- sdk/src/utils/test.rs | 101 -------------------------------- sdk/tests/integration.rs | 1 - sdk/tests/test_builder.rs | 2 +- sdk/tests/v2_api_integration.rs | 11 ++-- 8 files changed, 47 insertions(+), 161 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec9b0e2cd..7d7e0e9d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3411,7 +3411,7 @@ dependencies = [ "bytes", "getrandom", "rand", - "ring 0.17.9", + "ring", "rustc-hash", "rustls", "rustls-pki-types", @@ -3716,34 +3716,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c601484456988d75017d86700d3743b949c21cdc7399f940c75e34680d185c5" -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" dependencies = [ "cc", "cfg-if", "getrandom", "libc", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -3841,7 +3824,7 @@ checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "log", "once_cell", - "ring 0.17.9", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -3872,7 +3855,7 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.9", + "ring", "rustls-pki-types", "untrusted", ] diff --git a/Makefile b/Makefile index 85c41b35c..385dd97bc 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,8 @@ test-wasm-web: # WASI testing requires the WASI SDK https://github.com/WebAssembly/wasi-sdk installed in /opt, # wasmtime, and the target wasm32-wasip2 on the nightly toolchain test-wasi: - CC=/opt/wasi-sdk/bin/clang CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime -S common --dir ." cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto + CC=/opt/wasi-sdk/bin/clang CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime -S cli -S http --dir ." cargo +nightly test --target wasm32-wasip2 -p c2pa -p c2pa-crypto --all-features + rm -r sdk/Users # Full local validation, build and test all features including wasm # Run this before pushing a PR to pre-validate diff --git a/sdk/src/error.rs b/sdk/src/error.rs index 6abedfc07..49d4892ff 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -191,10 +191,10 @@ pub enum Error { #[error("required JUMBF box not found")] JumbfBoxNotFound, - #[error("could not fetch the remote manifest")] + #[error("could not fetch the remote manifest {0}")] RemoteManifestFetch(String), - #[error("must fetch remote manifests from url")] + #[error("must fetch remote manifests from url {0}")] RemoteManifestUrl(String), #[error("stopped because of logged error")] diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 5fa7f1ce5..6af9f855f 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -3286,47 +3286,48 @@ impl Store { }; //const MANIFEST_CONTENT_TYPE: &str = "application/x-c2pa-manifest-store"; // todo verify once these are served - //const DEFAULT_MANIFEST_RESPONSE_SIZE: usize = 10 * 1024 * 1024; // 10 MB + const DEFAULT_MANIFEST_RESPONSE_SIZE: usize = 10 * 1024 * 1024; // 10 MB let parsed_url = Url::parse(url) .map_err(|e| Error::RemoteManifestFetch(format!("invalid URL: {}", e)))?; - let path_with_query = parsed_url[url::Position::BeforeHost..].to_string(); + let authority = parsed_url.authority(); + let path_with_query = parsed_url[url::Position::AfterPort..].to_string(); + let scheme = match parsed_url.scheme() { + "http" => Scheme::Http, + "https" => Scheme::Https, + _ => { + return Err(Error::RemoteManifestFetch( + "unsupported URL scheme".to_string(), + )) + } + }; let request = OutgoingRequest::new(Fields::new()); request.set_path_with_query(Some(&path_with_query)).unwrap(); - request.set_scheme(Some(&Scheme::Https)).unwrap(); + request.set_authority(Some(&authority)).unwrap(); + request.set_scheme(Some(&scheme)).unwrap(); match outgoing_handler::handle(request, None) { Ok(resp) => { resp.subscribe().block(); let response = resp .get() - .expect("HTTP request response missing") - .expect("HTTP request response requested more than once") - .expect("HTTP request failed"); + .ok_or(Error::RemoteManifestFetch( + "HTTP request response missing".to_string(), + ))? + .map_err(|_| { + Error::RemoteManifestFetch( + "HTTP request response requested more than once".to_string(), + ) + })? + .map_err(|_| Error::RemoteManifestFetch("HTTP request failed".to_string()))?; if response.status() == 200 { - let raw_header = response.headers().get("Content-Length"); - if raw_header.first().map(|val| val.is_empty()).unwrap_or(true) { - return Err(Error::RemoteManifestFetch( - "url returned no content length".to_string(), - )); - } - let str_parsed_header = match std::str::from_utf8(raw_header.first().unwrap()) { - Ok(s) => s, - Err(e) => { - return Err(Error::RemoteManifestFetch(format!( - "error parsing content length header: {}", - e - ))) - } - }; - let content_length: usize = match str_parsed_header.parse() { - Ok(s) => s, - Err(e) => { - return Err(Error::RemoteManifestFetch(format!( - "error parsing content length header: {}", - e - ))) - } - }; + let content_length: usize = response + .headers() + .get("Content-Length") + .first() + .and_then(|val| if val.is_empty() { None } else { Some(val) }) + .and_then(|val| std::str::from_utf8(val).ok()) + .and_then(|str_parsed_header| str_parsed_header.parse().ok()) + .unwrap_or(DEFAULT_MANIFEST_RESPONSE_SIZE); let body = { let mut buf = Vec::with_capacity(content_length); let response_body = response diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index c1f904fd2..511dd94f0 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -389,107 +389,6 @@ impl crate::signer::RemoteSigner for TempRemoteSigner { } } -/* todo: This test should be replaced by a rust_native signer if desired to sign from wasm -#[cfg(target_arch = "wasm32")] -struct WebCryptoSigner { - signing_alg: SigningAlg, - signing_alg_name: String, - certs: Vec>, - key: Vec, -} - -#[cfg(target_arch = "wasm32")] -impl WebCryptoSigner { - pub fn new(alg: &str, cert: &str, key: &str) -> Self { - static START_CERTIFICATE: &str = "-----BEGIN CERTIFICATE-----"; - static END_CERTIFICATE: &str = "-----END CERTIFICATE-----"; - static START_KEY: &str = "-----BEGIN PRIVATE KEY-----"; - static END_KEY: &str = "-----END PRIVATE KEY-----"; - - let mut name = alg.to_owned().to_uppercase(); - name.insert(2, '-'); - - let key = key - .replace("\n", "") - .replace(START_KEY, "") - .replace(END_KEY, ""); - let key = c2pa_crypto::base64::decode(&key).unwrap(); - - let certs = cert - .replace("\n", "") - .replace(START_CERTIFICATE, "") - .split(END_CERTIFICATE) - .map(|x| c2pa_crypto::base64::decode(x).unwrap()) - .collect(); - - Self { - signing_alg: alg.parse().unwrap(), - signing_alg_name: name, - certs, - key, - } - } -} - -#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] -#[async_trait::async_trait(?Send)] -impl AsyncSigner for WebCryptoSigner { - fn alg(&self) -> SigningAlg { - self.signing_alg - } - - fn certs(&self) -> Result>> { - Ok(self.certs.clone()) - } - - async fn sign(&self, claim_bytes: Vec) -> crate::error::Result> { - use c2pa_crypto::raw_signature::webcrypto::WindowOrWorker; - use js_sys::{Array, Object, Reflect, Uint8Array}; - use wasm_bindgen_futures::JsFuture; - use web_sys::CryptoKey; - let context = WindowOrWorker::new().unwrap(); - let crypto = context.subtle_crypto().unwrap(); - - let mut data = claim_bytes.clone(); - let promise = crypto - .digest_with_str_and_u8_array("SHA-256", &mut data) - .unwrap(); - let result = JsFuture::from(promise).await.unwrap(); - let mut digest = Uint8Array::new(&result).to_vec(); - - let key = Uint8Array::new_with_length(self.key.len() as u32); - key.copy_from(&self.key); - let usages = Array::new(); - usages.push(&"sign".into()); - let alg = Object::new(); - Reflect::set(&alg, &"name".into(), &"ECDSA".into()).unwrap(); - Reflect::set(&alg, &"namedCurve".into(), &"P-256".into()).unwrap(); - - let promise = crypto - .import_key_with_object("pkcs8", &key, &alg, true, &usages) - .unwrap(); - let key: CryptoKey = JsFuture::from(promise).await.unwrap().into(); - - let alg = Object::new(); - Reflect::set(&alg, &"name".into(), &"ECDSA".into()).unwrap(); - Reflect::set(&alg, &"hash".into(), &"SHA-256".into()).unwrap(); - let promise = crypto - .sign_with_object_and_u8_array(&alg, &key, &mut digest) - .unwrap(); - let result = JsFuture::from(promise).await.unwrap(); - Ok(Uint8Array::new(&result).to_vec()) - } - - fn reserve_size(&self) -> usize { - 10000 - } - - async fn send_timestamp_request(&self, _: &[u8]) -> Option>> { - None - } -} -*/ - /// Create a [`RemoteSigner`] instance that can be used for testing purposes. /// /// # Returns diff --git a/sdk/tests/integration.rs b/sdk/tests/integration.rs index 22577ae70..2746964fd 100644 --- a/sdk/tests/integration.rs +++ b/sdk/tests/integration.rs @@ -13,7 +13,6 @@ /// Complete functional integration test with parent and ingredients. // Isolate from wasm by wrapping in module. - #[cfg(feature = "file_io")] mod integration_1 { use std::{io, path::PathBuf}; diff --git a/sdk/tests/test_builder.rs b/sdk/tests/test_builder.rs index a4dad8106..9f801b095 100644 --- a/sdk/tests/test_builder.rs +++ b/sdk/tests/test_builder.rs @@ -21,7 +21,7 @@ mod common; use common::{compare_stream_to_known_good, fixtures_path, test_signer}; #[test] -#[ignore] // TODO: Test does not pass in WASI or native +#[cfg(all(feature = "add_thumbnails", feature = "file_io"))] fn test_builder_ca_jpg() -> Result<()> { let manifest_def = std::fs::read_to_string(fixtures_path("simple_manifest.json"))?; let mut builder = Builder::from_json(&manifest_def)?; diff --git a/sdk/tests/v2_api_integration.rs b/sdk/tests/v2_api_integration.rs index d26bcb195..1b0cbff49 100644 --- a/sdk/tests/v2_api_integration.rs +++ b/sdk/tests/v2_api_integration.rs @@ -147,10 +147,13 @@ mod integration_v2 { dest }; - // write dest to file for debugging - let debug_path = format!("{}/../target/v2_test.jpg", env!("CARGO_MANIFEST_DIR")); - std::fs::write(debug_path, dest.get_ref())?; - dest.rewind()?; + #[cfg(not(target_os = "wasi"))] + { + // write dest to file for debugging + let debug_path = format!("{}/../target/v2_test.jpg", env!("CARGO_MANIFEST_DIR")); + std::fs::write(debug_path, dest.get_ref())?; + dest.rewind()?; + } let reader = Reader::from_stream(format, &mut dest)?;