Skip to content

Commit

Permalink
fix: Working remote manifest fetch.
Browse files Browse the repository at this point in the history
  • Loading branch information
cdmurph32 committed Feb 18, 2025
1 parent e1a5b40 commit 3454987
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 161 deletions.
25 changes: 4 additions & 21 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions sdk/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
61 changes: 31 additions & 30 deletions sdk/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
101 changes: 0 additions & 101 deletions sdk/src/utils/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<u8>>,
key: Vec<u8>,
}
#[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<Vec<Vec<u8>>> {
Ok(self.certs.clone())
}
async fn sign(&self, claim_bytes: Vec<u8>) -> crate::error::Result<Vec<u8>> {
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<Result<Vec<u8>>> {
None
}
}
*/

/// Create a [`RemoteSigner`] instance that can be used for testing purposes.
///
/// # Returns
Expand Down
1 change: 0 additions & 1 deletion sdk/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
2 changes: 1 addition & 1 deletion sdk/tests/test_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
11 changes: 7 additions & 4 deletions sdk/tests/v2_api_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;

Expand Down

0 comments on commit 3454987

Please sign in to comment.