diff --git a/sdk/src/assertions/timestamp.rs b/sdk/src/assertions/timestamp.rs index 6110f2e93..f7906ec46 100644 --- a/sdk/src/assertions/timestamp.rs +++ b/sdk/src/assertions/timestamp.rs @@ -24,7 +24,7 @@ use crate::{ error::Result, http::{AsyncHttpResolver, SyncHttpResolver}, status_tracker::StatusTracker, - Error, + AsyncSigner, Error, Signer, }; /// Helper class to create a `TimeStamp` assertion. @@ -60,22 +60,24 @@ impl TimeStamp { /// The signature is expected to be the `signature` field of the `COSE_Sign1_Tagged` structure /// found in the C2PA claim signature box of the manifest corresponding to the `manifest_id`. // - // The `signature` is normally obtained from [`Store::get_cose_sign1_signature`]. + // The `signature` is normally obtained via [`Claim::cose_sign1`] using the [`CoseSign1::signature`] + // field. // - // [`Store::get_cose_sign1_signature`][crate::store::Store::get_cose_sign1_structure]. + // [`Claim::cose_sign1`][crate::claim::Claim::cose_sign1]. + // [`CoseSign1::signature`][coset::CoseSign1::signature]. #[async_generic(async_signature( &mut self, tsa_url: &str, manifest_id: &str, signature: &[u8], - http_resolver: &(impl AsyncHttpResolver + ?Sized), + http_resolver: &impl AsyncHttpResolver, ))] - pub(crate) fn refresh_timestamp( + pub fn refresh_timestamp( &mut self, tsa_url: &str, manifest_id: &str, signature: &[u8], - http_resolver: &(impl SyncHttpResolver + ?Sized), + http_resolver: &impl SyncHttpResolver, ) -> Result<()> { let timestamp_token = if _sync { TimeStamp::send_timestamp_token_request(tsa_url, signature, http_resolver)? @@ -97,12 +99,12 @@ impl TimeStamp { #[async_generic(async_signature( tsa_url: &str, message: &[u8], - http_resolver: &(impl AsyncHttpResolver + ?Sized), + http_resolver: &impl AsyncHttpResolver, ))] - pub(crate) fn send_timestamp_token_request( + pub fn send_timestamp_token_request( tsa_url: &str, message: &[u8], - http_resolver: &(impl SyncHttpResolver + ?Sized), + http_resolver: &impl SyncHttpResolver, ) -> Result> { let body = crate::crypto::time_stamp::default_rfc3161_message(message)?; let headers = None; @@ -127,13 +129,102 @@ impl TimeStamp { } .map_err(|err| Error::OtherError(format!("timestamp token not found: {err:?}").into()))?; - // make sure it is a good response + if _sync { + Self::verify_timestamp_response(&bytes, message)?; + } else { + Self::verify_timestamp_response_async(&bytes, message).await?; + } + + let token = + crate::crypto::cose::timestamptoken_from_timestamprsp(&bytes).map_err(|err| { + Error::OtherError(format!("timestamp token not found: {err:?}").into()) + })?; + + Ok(token) + } + + /// Equivalent to [`TimeStamp::refresh_timestamp`] except using a [`Signer`]. + /// + /// [`Signer`]: crate::Signer + #[async_generic(async_signature( + &mut self, + manifest_id: &str, + signature: &[u8], + http_resolver: &impl AsyncHttpResolver, + signer: &(impl AsyncSigner + ?Sized), + ))] + pub(crate) fn refresh_timestamp_with_signer( + &mut self, + manifest_id: &str, + signature: &[u8], + http_resolver: &impl SyncHttpResolver, + signer: &(impl Signer + ?Sized), + ) -> Result<()> { + let timestamp_token = if _sync { + TimeStamp::send_timestamp_token_request_with_signer(signature, http_resolver, signer)? + } else { + TimeStamp::send_timestamp_token_request_with_signer_async( + signature, + http_resolver, + signer, + ) + .await? + }; + + self.0 + .insert(manifest_id.to_owned(), ByteBuf::from(timestamp_token)); + + Ok(()) + } + + /// Equivalent to [`TimeStamp::send_timestamp_token_request`] except using a [`Signer`]. + /// + /// [`Signer`]: crate::Signer + #[async_generic(async_signature( + message: &[u8], + http_resolver: &impl AsyncHttpResolver, + signer: &(impl AsyncSigner + ?Sized), + ))] + pub(crate) fn send_timestamp_token_request_with_signer( + message: &[u8], + http_resolver: &impl SyncHttpResolver, + signer: &(impl Signer + ?Sized), + ) -> Result> { + let bytes = if _sync { + signer.send_timestamp_request(http_resolver, message) + } else { + signer.send_timestamp_request(http_resolver, message).await + } + // TODO: more explicit error + .ok_or_else(|| Error::UnsupportedType)? + .map_err(|err| Error::OtherError(format!("timestamp token not found: {err:?}").into()))?; + + if _sync { + Self::verify_timestamp_response(&bytes, message)?; + } else { + Self::verify_timestamp_response_async(&bytes, message).await?; + } + + let token = + crate::crypto::cose::timestamptoken_from_timestamprsp(&bytes).map_err(|err| { + Error::OtherError(format!("timestamp token not found: {err:?}").into()) + })?; + + Ok(token) + } + + /// Verifies a timestamp respponse given the response bytes and original message. + #[async_generic(async_signature( + bytes: &[u8], + message: &[u8], + ))] + fn verify_timestamp_response(bytes: &[u8], message: &[u8]) -> Result<()> { let ctp = CertificateTrustPolicy::passthrough(); let mut tracker = StatusTracker::default(); if _sync { crate::crypto::time_stamp::verify_time_stamp( - &bytes, + bytes, message, &ctp, &mut tracker, @@ -141,7 +232,7 @@ impl TimeStamp { )?; } else { crate::crypto::time_stamp::verify_time_stamp_async( - &bytes, + bytes, message, &ctp, &mut tracker, @@ -150,12 +241,7 @@ impl TimeStamp { .await?; } - let token = - crate::crypto::cose::timestamptoken_from_timestamprsp(&bytes).map_err(|err| { - Error::OtherError(format!("timestamp token not found: {err:?}").into()) - })?; - - Ok(token) + Ok(()) } } diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index ba60d057e..20e210033 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1787,10 +1787,9 @@ impl Builder { /// [`TimeStampSettings::enabled`]: crate::settings::builder::TimeStampSettings::enabled #[async_generic(async_signature( &self, - tsa_url: &str, provenance_claim: &mut Claim, ))] - fn maybe_add_timestamp(&self, tsa_url: &str, provenance_claim: &mut Claim) -> Result<()> { + fn maybe_add_timestamp(&self, provenance_claim: &mut Claim) -> Result<()> { let settings = self.context().settings(); if !settings.builder.auto_timestamp_assertion.enabled @@ -1799,6 +1798,15 @@ impl Builder { return Ok(()); } + let signer = if _sync { + self.context().signer()? + } else { + self.context().async_signer()? + }; + if signer.time_authority_url().is_none() { + return Ok(()); + } + let mut claim_uris = HashSet::new(); match settings.builder.auto_timestamp_assertion.fetch_scope { TimeStampFetchScope::All => { @@ -1868,19 +1876,19 @@ impl Builder { if let Some(claim) = provenance_claim.claim_ingredient(&manifest_label) { let signature = claim.cose_sign1()?.signature; if _sync { - timestamp_assertion.refresh_timestamp( - tsa_url, + timestamp_assertion.refresh_timestamp_with_signer( &manifest_label, &signature, - self.context().resolver(), + &self.context().resolver(), + signer, )?; } else { timestamp_assertion - .refresh_timestamp_async( - tsa_url, + .refresh_timestamp_with_signer_async( &manifest_label, &signature, - self.context().resolver_async(), + &self.context().resolver_async(), + signer, ) .await?; } @@ -2074,12 +2082,10 @@ impl Builder { let mut claim = self.to_claim()?; - if let Some(tsa_url) = signer.time_authority_url() { - if _sync { - self.maybe_add_timestamp(&tsa_url, &mut claim)?; - } else { - self.maybe_add_timestamp_async(&tsa_url, &mut claim).await? - } + if _sync { + self.maybe_add_timestamp(&mut claim)?; + } else { + self.maybe_add_timestamp_async(&mut claim).await? } let mut store = self.to_store_with_claim(claim)?; @@ -2475,7 +2481,10 @@ impl std::fmt::Display for Builder { mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] - use std::{io::Cursor, vec}; + use std::{ + io::{self, Cursor}, + vec, + }; use c2pa_macros::c2pa_test_async; use serde_json::json; @@ -2489,6 +2498,7 @@ mod tests { assertions::{c2pa_action, BoxHash, DigitalSourceType}, crypto::raw_signature::SigningAlg, hash_stream_by_alg, + maybe_send_sync::MaybeSend, settings::Settings, utils::{ test::{test_context, write_jpeg_placeholder_stream}, @@ -4628,4 +4638,19 @@ mod tests { assert!(reader.active_manifest().is_some()); } + + // Ensures that the future returned by `Builder::sign_async` implements `Send`, thus making it + // possible to spawn on a Tokio runtime. + #[test] + fn test_sign_async_future_is_send() { + fn assert_send(_: T) {} + + let signer = async_test_signer(SigningAlg::Ps256); + let mut builder = Builder::new(); + let mut src = io::empty(); + let mut dst = io::empty(); + + let future = builder.sign_async(&signer, "image/jpeg", &mut src, &mut dst); + assert_send(future); + } } diff --git a/sdk/src/context.rs b/sdk/src/context.rs index e5b239926..fc607df71 100644 --- a/sdk/src/context.rs +++ b/sdk/src/context.rs @@ -1,9 +1,22 @@ -use std::sync::OnceLock; +// Copyright 2026 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::sync::{Arc, OnceLock}; use crate::{ http::{ restricted::RestrictedResolver, AsyncGenericResolver, AsyncHttpResolver, - BoxedAsyncResolver, BoxedSyncResolver, SyncGenericResolver, SyncHttpResolver, + SyncGenericResolver, SyncHttpResolver, }, maybe_send_sync::{MaybeSend, MaybeSync}, settings::Settings, @@ -14,17 +27,17 @@ use crate::{ /// Internal state for sync HTTP resolver selection. enum SyncResolverState { /// User-provided custom resolver. - Custom(BoxedSyncResolver), + Custom(Arc), /// Default resolver with lazy initialization. - Default(OnceLock>), + Default(OnceLock>), } /// Internal state for async HTTP resolver selection. enum AsyncResolverState { /// User-provided custom resolver. - Custom(BoxedAsyncResolver), + Custom(Arc), /// Default resolver with lazy initialization. - Default(OnceLock>), + Default(OnceLock>), } /// Internal state for signer selection. @@ -312,7 +325,7 @@ impl Context { mut self, resolver: T, ) -> Self { - self.sync_resolver = SyncResolverState::Custom(Box::new(resolver)); + self.sync_resolver = SyncResolverState::Custom(Arc::new(resolver)); self } @@ -320,7 +333,7 @@ impl Context { &mut self, resolver: T, ) -> Result<()> { - self.sync_resolver = SyncResolverState::Custom(Box::new(resolver)); + self.sync_resolver = SyncResolverState::Custom(Arc::new(resolver)); Ok(()) } @@ -335,7 +348,7 @@ impl Context { mut self, resolver: T, ) -> Self { - self.async_resolver = AsyncResolverState::Custom(Box::new(resolver)); + self.async_resolver = AsyncResolverState::Custom(Arc::new(resolver)); self } @@ -343,7 +356,7 @@ impl Context { &mut self, resolver: T, ) -> Result<()> { - self.async_resolver = AsyncResolverState::Custom(Box::new(resolver)); + self.async_resolver = AsyncResolverState::Custom(Arc::new(resolver)); Ok(()) } @@ -351,15 +364,17 @@ impl Context { /// /// The default resolver is a `SyncGenericResolver` wrapped with `RestrictedResolver` /// to apply host filtering from the settings. - pub fn resolver(&self) -> &dyn SyncHttpResolver { + pub fn resolver(&self) -> Arc { match &self.sync_resolver { - SyncResolverState::Custom(resolver) => resolver.as_ref(), - SyncResolverState::Default(once_lock) => once_lock.get_or_init(|| { - let inner = SyncGenericResolver::new(); - let mut resolver = RestrictedResolver::new(inner); - resolver.set_allowed_hosts(self.settings.core.allowed_network_hosts.clone()); - resolver - }), + SyncResolverState::Custom(resolver) => resolver.clone(), + SyncResolverState::Default(once_lock) => once_lock + .get_or_init(|| { + let inner = SyncGenericResolver::new(); + let mut resolver = RestrictedResolver::new(inner); + resolver.set_allowed_hosts(self.settings.core.allowed_network_hosts.clone()); + Arc::new(resolver) + }) + .clone(), } } @@ -367,15 +382,17 @@ impl Context { /// /// The default resolver is an `AsyncGenericResolver` wrapped with `RestrictedResolver` /// to apply host filtering from the settings. - pub fn resolver_async(&self) -> &dyn AsyncHttpResolver { + pub fn resolver_async(&self) -> Arc { match &self.async_resolver { - AsyncResolverState::Custom(resolver) => resolver.as_ref(), - AsyncResolverState::Default(once_lock) => once_lock.get_or_init(|| { - let inner = AsyncGenericResolver::new(); - let mut resolver = RestrictedResolver::new(inner); - resolver.set_allowed_hosts(self.settings.core.allowed_network_hosts.clone()); - resolver - }), + AsyncResolverState::Custom(resolver) => resolver.clone(), + AsyncResolverState::Default(once_lock) => once_lock + .get_or_init(|| { + let inner = AsyncGenericResolver::new(); + let mut resolver = RestrictedResolver::new(inner); + resolver.set_allowed_hosts(self.settings.core.allowed_network_hosts.clone()); + Arc::new(resolver) + }) + .clone(), } } diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 02672b93f..536ca8231 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -29,6 +29,7 @@ use crate::{ raw_signature::{AsyncRawSigner, RawSigner, RawSignerError, SigningAlg}, time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider}, }, + http::{AsyncHttpResolver, SyncHttpResolver}, settings::Settings, status_tracker::{ErrorBehavior, StatusTracker}, AsyncSigner, Error, Result, Signer, @@ -56,12 +57,14 @@ use crate::{ signer: &dyn AsyncSigner, box_size: usize, settings: &Settings, + http_resolver: &impl AsyncHttpResolver, ))] pub fn sign_claim( claim_bytes: &[u8], signer: &dyn Signer, box_size: usize, settings: &Settings, + http_resolver: &impl SyncHttpResolver, ) -> Result> { // Must be a valid claim. let label = "dummy_label"; @@ -74,9 +77,9 @@ pub fn sign_claim( }; let signed_bytes = if _sync { - cose_sign(signer, claim_bytes, box_size, tss, settings) + cose_sign(signer, claim_bytes, box_size, tss, settings, http_resolver) } else { - cose_sign_async(signer, claim_bytes, box_size, tss, settings).await + cose_sign_async(signer, claim_bytes, box_size, tss, settings, http_resolver).await }; match signed_bytes { @@ -117,6 +120,7 @@ pub fn sign_claim( box_size: usize, time_stamp_storage: TimeStampStorage, settings: &Settings, + http_resolver: &impl AsyncHttpResolver, ))] pub(crate) fn cose_sign( signer: &dyn Signer, @@ -124,6 +128,7 @@ pub(crate) fn cose_sign( box_size: usize, time_stamp_storage: TimeStampStorage, settings: &Settings, + http_resolver: &impl SyncHttpResolver, ) -> Result> { // Make sure the signing cert is valid. let certs = signer.certs()?; @@ -135,20 +140,44 @@ pub(crate) fn cose_sign( if _sync { match signer.raw_signer() { - Some(raw_signer) => Ok(sign(*raw_signer, data, Some(box_size), time_stamp_storage)?), + Some(raw_signer) => Ok(sign( + *raw_signer, + data, + Some(box_size), + time_stamp_storage, + http_resolver, + )?), None => { let wrapper = SignerWrapper(signer); - Ok(sign(&wrapper, data, Some(box_size), time_stamp_storage)?) + Ok(sign( + &wrapper, + data, + Some(box_size), + time_stamp_storage, + http_resolver, + )?) } } } else { match signer.async_raw_signer() { - Some(raw_signer) => { - Ok(sign_async(*raw_signer, data, Some(box_size), time_stamp_storage).await?) - } + Some(raw_signer) => Ok(sign_async( + *raw_signer, + data, + Some(box_size), + time_stamp_storage, + http_resolver, + ) + .await?), None => { let wrapper = AsyncSignerWrapper(signer); - Ok(sign_async(&wrapper, data, Some(box_size), time_stamp_storage).await?) + Ok(sign_async( + &wrapper, + data, + Some(box_size), + time_stamp_storage, + http_resolver, + ) + .await?) } } } @@ -219,10 +248,11 @@ impl TimeStampProvider for SignerWrapper<'_> { fn send_time_stamp_request( &self, + http_resolver: &dyn SyncHttpResolver, message: &[u8], ) -> Option, TimeStampError>> { self.0 - .send_timestamp_request(message) + .send_timestamp_request(http_resolver, message) .map(|r| r.map_err(|e| e.into())) } } @@ -273,10 +303,11 @@ impl AsyncTimeStampProvider for AsyncSignerWrapper<'_> { async fn send_time_stamp_request( &self, + http_resolver: &dyn AsyncHttpResolver, message: &[u8], ) -> Option, TimeStampError>> { self.0 - .send_timestamp_request(message) + .send_timestamp_request(http_resolver, message) .await .map(|r| r.map_err(|e| e.into())) } @@ -294,6 +325,7 @@ mod tests { use crate::{ claim::Claim, crypto::raw_signature::SigningAlg, + http::{AsyncGenericResolver, SyncGenericResolver, SyncHttpResolver}, settings::Settings, utils::test_signer::{async_test_signer, test_signer}, Result, Signer, @@ -317,7 +349,14 @@ mod tests { let signer = test_signer(SigningAlg::Ps256); let box_size = Signer::reserve_size(signer.as_ref()); - let cose_sign1 = sign_claim(&claim_bytes, signer.as_ref(), box_size, &settings).unwrap(); + let cose_sign1 = sign_claim( + &claim_bytes, + signer.as_ref(), + box_size, + &settings, + &SyncGenericResolver::new(), + ) + .unwrap(); assert_eq!(cose_sign1.len(), box_size); } @@ -342,9 +381,15 @@ mod tests { let signer = async_test_signer(SigningAlg::Ps256); let box_size = signer.reserve_size(); - let cose_sign1 = sign_claim_async(&claim_bytes, &signer, box_size, &settings) - .await - .unwrap(); + let cose_sign1 = sign_claim_async( + &claim_bytes, + &signer, + box_size, + &settings, + &AsyncGenericResolver::new(), + ) + .await + .unwrap(); assert_eq!(cose_sign1.len(), box_size); } @@ -377,7 +422,11 @@ mod tests { 1024 } - fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { + fn send_timestamp_request( + &self, + _http_resolver: &dyn SyncHttpResolver, + _message: &[u8], + ) -> Option>> { Some(Ok(Vec::new())) } } @@ -395,7 +444,13 @@ mod tests { let signer = BogusSigner::new(); - let _cose_sign1 = sign_claim(&claim_bytes, &signer, box_size, &settings); + let _cose_sign1 = sign_claim( + &claim_bytes, + &signer, + box_size, + &settings, + &SyncGenericResolver::new(), + ); assert!(_cose_sign1.is_err()); } diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index 5a71cdab7..5125b1fdb 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -263,8 +263,8 @@ pub mod tests { use super::*; use crate::{ - crypto::raw_signature::SigningAlg, settings::Settings, status_tracker::StatusTracker, - utils::test_signer::test_signer, Signer, + crypto::raw_signature::SigningAlg, http::SyncGenericResolver, settings::Settings, + status_tracker::StatusTracker, utils::test_signer::test_signer, Signer, }; #[test] @@ -283,9 +283,14 @@ pub mod tests { let signer = test_signer(SigningAlg::Ps256); - let cose_bytes = - crate::cose_sign::sign_claim(&claim_bytes, signer.as_ref(), box_size, &settings) - .unwrap(); + let cose_bytes = crate::cose_sign::sign_claim( + &claim_bytes, + signer.as_ref(), + box_size, + &settings, + &SyncGenericResolver::new(), + ) + .unwrap(); let cose_sign1 = parse_cose_sign1(&cose_bytes, &claim_bytes, &mut validation_log).unwrap(); @@ -361,6 +366,7 @@ pub mod tests { &ocsp_signer, ocsp_signer.reserve_size(), &settings, + &SyncGenericResolver::new(), ) .unwrap(); diff --git a/sdk/src/crypto/cose/sign.rs b/sdk/src/crypto/cose/sign.rs index 305a4a927..b9f190a42 100644 --- a/sdk/src/crypto/cose/sign.rs +++ b/sdk/src/crypto/cose/sign.rs @@ -23,10 +23,13 @@ use serde_bytes::ByteBuf; use x509_parser::prelude::X509Certificate; use super::cert_chain_from_sign1; -use crate::crypto::{ - cose::{add_sigtst_header, add_sigtst_header_async, CoseError, TimeStampStorage}, - ec_utils::{der_to_p1363, ec_curve_from_public_key_der, parse_ec_der_sig}, - raw_signature::{AsyncRawSigner, RawSigner, SigningAlg}, +use crate::{ + crypto::{ + cose::{add_sigtst_header, add_sigtst_header_async, CoseError, TimeStampStorage}, + ec_utils::{der_to_p1363, ec_curve_from_public_key_der, parse_ec_der_sig}, + raw_signature::{AsyncRawSigner, RawSigner, SigningAlg}, + }, + http::{AsyncHttpResolver, SyncHttpResolver}, }; /// Given an arbitrary block of data and a [`RawSigner`] or [`AsyncRawSigner`] @@ -80,23 +83,29 @@ use crate::crypto::{ signer: &dyn AsyncRawSigner, data: &[u8], box_size: Option, - tss: TimeStampStorage + tss: TimeStampStorage, + http_resolver: &impl AsyncHttpResolver, ))] pub fn sign( signer: &dyn RawSigner, data: &[u8], box_size: Option, tss: TimeStampStorage, + http_resolver: &impl SyncHttpResolver, ) -> Result, CoseError> { if _sync { match tss { - TimeStampStorage::V1_sigTst => sign_v1(signer, data, box_size, tss), - TimeStampStorage::V2_sigTst2_CTT => sign_v2(signer, data, box_size, tss), + TimeStampStorage::V1_sigTst => sign_v1(signer, data, box_size, tss, http_resolver), + TimeStampStorage::V2_sigTst2_CTT => sign_v2(signer, data, box_size, tss, http_resolver), } } else { match tss { - TimeStampStorage::V1_sigTst => sign_v1_async(signer, data, box_size, tss).await, - TimeStampStorage::V2_sigTst2_CTT => sign_v2_async(signer, data, box_size, tss).await, + TimeStampStorage::V1_sigTst => { + sign_v1_async(signer, data, box_size, tss, http_resolver).await + } + TimeStampStorage::V2_sigTst2_CTT => { + sign_v2_async(signer, data, box_size, tss, http_resolver).await + } } } } @@ -105,13 +114,15 @@ pub fn sign( signer: &dyn AsyncRawSigner, data: &[u8], box_size: Option, - tss: TimeStampStorage + tss: TimeStampStorage, + http_resolver: &impl AsyncHttpResolver, ))] pub fn sign_v1( signer: &dyn RawSigner, data: &[u8], box_size: Option, tss: TimeStampStorage, + http_resolver: &impl SyncHttpResolver, ) -> Result, CoseError> { let alg = signer.alg(); @@ -126,9 +137,9 @@ pub fn sign_v1( // V1: Generate time stamp then sign. let unprotected_header = if _sync { - build_unprotected_header(signer, data, &protected_header, tss)? + build_unprotected_header(signer, data, &protected_header, tss, http_resolver)? } else { - build_unprotected_header_async(signer, data, &protected_header, tss).await? + build_unprotected_header_async(signer, data, &protected_header, tss, http_resolver).await? }; let sign1_builder = CoseSign1Builder::new() @@ -192,18 +203,37 @@ pub fn sign_v1( signer: &dyn AsyncRawSigner, data: &[u8], box_size: Option, - tss: TimeStampStorage + tss: TimeStampStorage, + http_resolver: &impl AsyncHttpResolver, ))] pub fn sign_v2( signer: &dyn RawSigner, data: &[u8], box_size: Option, tss: TimeStampStorage, + http_resolver: &impl SyncHttpResolver, ) -> Result, CoseError> { if _sync { - sign_v2_embedded(signer, data, box_size, CosePayload::Detached, None, tss) + sign_v2_embedded( + signer, + data, + box_size, + CosePayload::Detached, + None, + tss, + http_resolver, + ) } else { - sign_v2_embedded_async(signer, data, box_size, CosePayload::Detached, None, tss).await + sign_v2_embedded_async( + signer, + data, + box_size, + CosePayload::Detached, + None, + tss, + http_resolver, + ) + .await } } @@ -279,7 +309,8 @@ pub enum CosePayload { box_size: Option, payload: CosePayload, content_type: Option, - tss: TimeStampStorage + tss: TimeStampStorage, + http_resolver: &impl AsyncHttpResolver, ))] pub fn sign_v2_embedded( signer: &dyn RawSigner, @@ -288,6 +319,7 @@ pub fn sign_v2_embedded( payload: CosePayload, content_type: Option, tss: TimeStampStorage, + http_resolver: &impl SyncHttpResolver, ) -> Result, CoseError> { let alg = signer.alg(); @@ -363,9 +395,22 @@ pub fn sign_v2_embedded( // Fill in the unprotected header with time stamp data. let unprotected_header = if _sync { - build_unprotected_header(signer, &sig_data_cbor, &protected_header, tss)? + build_unprotected_header( + signer, + &sig_data_cbor, + &protected_header, + tss, + http_resolver, + )? } else { - build_unprotected_header_async(signer, &sig_data_cbor, &protected_header, tss).await? + build_unprotected_header_async( + signer, + &sig_data_cbor, + &protected_header, + tss, + http_resolver, + ) + .await? }; sign1.unprotected = unprotected_header; @@ -424,12 +469,21 @@ fn build_protected_header( Ok(ph2) } -#[async_generic(async_signature(signer: &dyn AsyncRawSigner, data: &[u8], p_header: &ProtectedHeader, tss: TimeStampStorage,))] +#[async_generic( + async_signature( + signer: &dyn AsyncRawSigner, + data: &[u8], + p_header: &ProtectedHeader, + tss: TimeStampStorage, + http_resolver: &impl AsyncHttpResolver, + )) +] fn build_unprotected_header( signer: &dyn RawSigner, data: &[u8], p_header: &ProtectedHeader, tss: TimeStampStorage, + http_resolver: &impl SyncHttpResolver, ) -> Result { // signed_data_from_time_stamp_response @@ -438,9 +492,9 @@ fn build_unprotected_header( let unprotected_h = HeaderBuilder::new(); let mut unprotected_h = if _sync { - add_sigtst_header(signer, data, p_header, unprotected_h, tss)? + add_sigtst_header(signer, data, p_header, unprotected_h, tss, http_resolver)? } else { - add_sigtst_header_async(signer, data, p_header, unprotected_h, tss).await? + add_sigtst_header_async(signer, data, p_header, unprotected_h, tss, http_resolver).await? }; // Set the OCSP responder response if available. diff --git a/sdk/src/crypto/cose/sigtst.rs b/sdk/src/crypto/cose/sigtst.rs index 8bcf3d5ea..f2092208a 100644 --- a/sdk/src/crypto/cose/sigtst.rs +++ b/sdk/src/crypto/cose/sigtst.rs @@ -30,6 +30,7 @@ use crate::{ TimeStampResponse, }, }, + http::{AsyncHttpResolver, SyncHttpResolver}, log_item, status_tracker::StatusTracker, validation_status, Result, @@ -219,20 +220,25 @@ impl TstContainer { p_header: &ProtectedHeader, mut header_builder: HeaderBuilder, tss: TimeStampStorage, - ))] + http_resolver: &impl AsyncHttpResolver, + )) +] pub(crate) fn add_sigtst_header( ts_provider: &dyn RawSigner, data: &[u8], p_header: &ProtectedHeader, mut header_builder: HeaderBuilder, tss: TimeStampStorage, + http_resolver: &impl SyncHttpResolver, ) -> Result { let sd = cose_countersign_data(data, p_header); let maybe_cts = if _sync { - ts_provider.send_time_stamp_request(&sd) + ts_provider.send_time_stamp_request(http_resolver, &sd) } else { - ts_provider.send_time_stamp_request(&sd).await + ts_provider + .send_time_stamp_request(http_resolver, &sd) + .await }; if let Some(cts) = maybe_cts { diff --git a/sdk/src/crypto/raw_signature/signer.rs b/sdk/src/crypto/raw_signature/signer.rs index c4c665a12..81753e201 100644 --- a/sdk/src/crypto/raw_signature/signer.rs +++ b/sdk/src/crypto/raw_signature/signer.rs @@ -268,12 +268,16 @@ impl AsyncTimeStampProvider for AsyncRawSignerWrapper { self.0.time_stamp_request_body(message) } - async fn send_time_stamp_request( - &self, - message: &[u8], - ) -> Option, TimeStampError>> { - self.0.send_time_stamp_request(message) - } + // TODO: this feels hacky but the problem is that we can't pass an AsyncHttpResolver as a SyncHttpResolver + // We create an AsyncRawSignerWrapper from the actual crypto signers which is never supposed to + // implement its own timestamping code anyways(?) because of that I thikn we can remove most of this trait impl + // async fn send_time_stamp_request( + // &self, + // http_resolver: &dyn AsyncHttpResolver, + // message: &[u8], + // ) -> Option, TimeStampError>> { + // self.0.send_time_stamp_request(message) + // } } #[cfg(test)] diff --git a/sdk/src/crypto/time_stamp/provider.rs b/sdk/src/crypto/time_stamp/provider.rs index c66196e4e..82c57b35c 100644 --- a/sdk/src/crypto/time_stamp/provider.rs +++ b/sdk/src/crypto/time_stamp/provider.rs @@ -22,7 +22,7 @@ use crate::{ raw_signature::oids::{ans1_oid_bcder_oid, SHA256_OID}, time_stamp::TimeStampError, }, - http::SyncGenericResolver, + http::{AsyncHttpResolver, SyncHttpResolver}, maybe_send_sync::MaybeSync, }; @@ -59,7 +59,11 @@ pub trait TimeStampProvider { /// [RFC 3161]: https://datatracker.ietf.org/doc/html/rfc3161 /// /// todo: THIS CODE IS NOT COMPATIBLE WITH C2PA 2.x sigTst2 - fn send_time_stamp_request(&self, message: &[u8]) -> Option, TimeStampError>> { + fn send_time_stamp_request( + &self, + http_resolver: &dyn SyncHttpResolver, + message: &[u8], + ) -> Option, TimeStampError>> { if let Some(url) = self.time_stamp_service_url() { if let Ok(body) = self.time_stamp_request_body(message) { let headers: Option> = self.time_stamp_request_headers(); @@ -68,7 +72,7 @@ pub trait TimeStampProvider { headers, &body, message, - &SyncGenericResolver::new(), + http_resolver, )); } } @@ -115,12 +119,11 @@ pub trait AsyncTimeStampProvider: MaybeSync { /// [RFC 3161]: https://datatracker.ietf.org/doc/html/rfc3161 async fn send_time_stamp_request( &self, + http_resolver: &dyn AsyncHttpResolver, message: &[u8], ) -> Option, TimeStampError>> { if let Some(url) = self.time_stamp_service_url() { if let Ok(body) = self.time_stamp_request_body(message) { - use crate::http::AsyncGenericResolver; - let headers: Option> = self.time_stamp_request_headers(); return Some( super::http_request::default_rfc3161_request_async( @@ -128,7 +131,7 @@ pub trait AsyncTimeStampProvider: MaybeSync { headers, &body, message, - &AsyncGenericResolver::new(), + http_resolver, ) .await, ); diff --git a/sdk/src/http/mod.rs b/sdk/src/http/mod.rs index bc44f5303..310f57dac 100644 --- a/sdk/src/http/mod.rs +++ b/sdk/src/http/mod.rs @@ -41,33 +41,18 @@ //! [`CertificateStatus`]: crate::assertions::CertificateStatus //! [`SignerSettings::Remote`]: crate::settings::signer::SignerSettings::Remote -use std::io::{self, Read}; +use std::{ + io::{self, Read}, + sync::Arc, +}; use async_trait::async_trait; use http::{Request, Response}; -use crate::Result; - -// Type aliases for boxed HTTP resolvers with conditional Send + Sync bounds -// These are the canonical definitions used throughout the codebase - -/// Type alias for a boxed [`SyncHttpResolver`] with conditional Send + Sync bounds. -/// On non-WASM targets, the resolver is Send + Sync for thread-safe usage. -#[cfg(not(target_arch = "wasm32"))] -pub type BoxedSyncResolver = Box; - -/// Type alias for a boxed [`SyncHttpResolver`] without Send + Sync bounds (WASM only). -#[cfg(target_arch = "wasm32")] -pub type BoxedSyncResolver = Box; - -/// Type alias for a boxed [`AsyncHttpResolver`] with conditional Send + Sync bounds. -/// On non-WASM targets, the resolver is Send + Sync for thread-safe usage. -#[cfg(not(target_arch = "wasm32"))] -pub type BoxedAsyncResolver = Box; - -/// Type alias for a boxed [`AsyncHttpResolver`] without Send + Sync bounds (WASM only). -#[cfg(target_arch = "wasm32")] -pub type BoxedAsyncResolver = Box; +use crate::{ + maybe_send_sync::{MaybeSend, MaybeSync}, + Result, +}; mod reqwest; mod ureq; @@ -79,7 +64,13 @@ pub mod restricted; pub use http; /// A resolver for sync (blocking) HTTP requests. -pub trait SyncHttpResolver { +/// +/// This trait is a supertrait of [`MaybeSend`] and [`MaybeSync`] for consistency with the +/// [`AsyncHttpResolver`]. For more information on the rationale, see [`AsyncHttpResolver`]. +/// +/// [`MaybeSend`]: crate::maybe_send_sync::MaybeSend +/// [`MaybeSync`]: crate::maybe_send_sync::MaybeSync +pub trait SyncHttpResolver: MaybeSend + MaybeSync { /// Resolve a [`Request`] into a [`Response`] with a streaming body. /// /// [`Request`]: http::Request @@ -90,10 +81,32 @@ pub trait SyncHttpResolver { ) -> Result>, HttpResolverError>; } +/// This implementation is particularly useful for compatibility with the return +/// type of [`Context::resolver`]. +/// +/// [`Context::resolver`]: crate::Context::resolver +impl SyncHttpResolver for Arc { + fn http_resolve( + &self, + request: Request>, + ) -> Result>, HttpResolverError> { + (**self).http_resolve(request) + } +} + /// A resolver for non-blocking (async) HTTP requests. +/// +/// This trait is a supertrait of [`MaybeSend`] and [`MaybeSync`] because in many cases +/// we use the pattern `&dyn AsyncHttpResolver`. For that to cross an await point, it +/// must implement `Send`, and for that to happen, it must also implement `Sync`. Thus, +/// rather than creating a new trait that combines `AsyncHttpResolver + MaybeSend + MaybeSync`, +/// we require it here to reduce complexity. +/// +/// [`MaybeSend`]: crate::maybe_send_sync::MaybeSend +/// [`MaybeSync`]: crate::maybe_send_sync::MaybeSync #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -pub trait AsyncHttpResolver { +pub trait AsyncHttpResolver: MaybeSend + MaybeSync { /// Resolve a [`Request`] into a [`Response`] with a streaming body. /// /// [`Request`]: http::Request @@ -104,6 +117,21 @@ pub trait AsyncHttpResolver { ) -> Result>, HttpResolverError>; } +/// This implementation is particularly useful for compatibility with the return +/// type of [`Context::resolver_async`]. +/// +/// [`Context::resolver_async`]: crate::Context::resolver_async +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +impl AsyncHttpResolver for Arc { + async fn http_resolve_async( + &self, + request: Request>, + ) -> Result>, HttpResolverError> { + (**self).http_resolve_async(request).await + } +} + /// A generic resolver for [`SyncHttpResolver`]. /// /// This implementation will automatically choose a [`SyncHttpResolver`] based on the @@ -125,7 +153,7 @@ pub struct SyncGenericResolver { impl SyncGenericResolver { /// Create a new [`SyncGenericResolver`] with an auto-specified [`SyncHttpResolver`]. /// - /// This function will create a [`SyncHttpResolver`] that returns [`Error::SyncHttpResolverNotImplemented`] + /// This function will create a [`SyncHttpResolver`] that returns [`HttpResolverError::SyncHttpResolverNotImplemented`] /// under any of the following conditions: /// * If both `http_reqwest_blocking` and `http_ureq` aren't enabled. /// * If the platform is WASM. @@ -170,7 +198,7 @@ pub struct AsyncGenericResolver { impl AsyncGenericResolver { /// Create a new [`AsyncGenericResolver`] with an auto-specified [`AsyncHttpResolver`]. /// - /// This function will create a [`AsyncHttpResolver`] that returns [`Error::AsyncHttpResolverNotImplemented`] + /// This function will create a [`AsyncHttpResolver`] that returns [`HttpResolverError::AsyncHttpResolverNotImplemented`] /// under any of the following conditions: /// * If `http_reqwest` isn't enabled. /// * If the platform is WASI and `http_wstd` isn't enabled. @@ -224,7 +252,6 @@ pub enum HttpResolverError { /// The remote URI is blocked by the allowed list. /// /// The allowed list can be set via: - /// - [`SyncGenericResolver::set_allowed_hosts`] / [`AsyncGenericResolver::set_allowed_hosts`] /// - [`RestrictedResolver`] (for wrapping custom resolvers) /// - SDK settings via [`Core::allowed_network_hosts`] /// diff --git a/sdk/src/identity/builder/async_identity_assertion_signer.rs b/sdk/src/identity/builder/async_identity_assertion_signer.rs index 7a7a086dd..317756cbe 100644 --- a/sdk/src/identity/builder/async_identity_assertion_signer.rs +++ b/sdk/src/identity/builder/async_identity_assertion_signer.rs @@ -16,6 +16,7 @@ use async_trait::async_trait; use crate::{ crypto::raw_signature::{AsyncRawSigner, SigningAlg}, dynamic_assertion::AsyncDynamicAssertion, + http::AsyncHttpResolver, identity::builder::AsyncIdentityAssertionBuilder, AsyncSigner, Result, }; @@ -134,9 +135,13 @@ impl AsyncSigner for AsyncIdentityAssertionSigner { .map_err(|e| e.into()) } - async fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + async fn send_timestamp_request( + &self, + http_resolver: &dyn AsyncHttpResolver, + message: &[u8], + ) -> Option>> { self.signer - .send_time_stamp_request(message) + .send_time_stamp_request(http_resolver, message) .await .map(|r| r.map_err(|e| e.into())) } diff --git a/sdk/src/identity/builder/identity_assertion_signer.rs b/sdk/src/identity/builder/identity_assertion_signer.rs index 71028dcd9..8023924f4 100644 --- a/sdk/src/identity/builder/identity_assertion_signer.rs +++ b/sdk/src/identity/builder/identity_assertion_signer.rs @@ -16,6 +16,7 @@ use std::sync::RwLock; use crate::{ crypto::raw_signature::{RawSigner, SigningAlg}, dynamic_assertion::DynamicAssertion, + http::SyncHttpResolver, identity::builder::IdentityAssertionBuilder, Result, Signer, }; @@ -111,9 +112,13 @@ impl Signer for IdentityAssertionSigner { .map_err(|e| e.into()) } - fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + fn send_timestamp_request( + &self, + http_resolver: &dyn SyncHttpResolver, + message: &[u8], + ) -> Option>> { self.signer - .send_time_stamp_request(message) + .send_time_stamp_request(http_resolver, message) .map(|r| r.map_err(|e| e.into())) } diff --git a/sdk/src/identity/x509/async_x509_credential_holder.rs b/sdk/src/identity/x509/async_x509_credential_holder.rs index d9db6d678..f5a990d79 100644 --- a/sdk/src/identity/x509/async_x509_credential_holder.rs +++ b/sdk/src/identity/x509/async_x509_credential_holder.rs @@ -18,6 +18,7 @@ use crate::{ cose::{sign_async, TimeStampStorage}, raw_signature::AsyncRawSigner, }, + http::AsyncGenericResolver, identity::{ builder::{AsyncCredentialHolder, IdentityBuilderError}, SignerPayload, @@ -91,6 +92,8 @@ impl AsyncCredentialHolder for AsyncX509CredentialHolder { &sp_cbor, None, TimeStampStorage::V2_sigTst2_CTT, + // TODO: https://github.com/contentauth/c2pa-rs/issues/1645 + &AsyncGenericResolver::new(), ) .await .map_err(|e| IdentityBuilderError::SignerError(e.to_string()))?) diff --git a/sdk/src/identity/x509/x509_credential_holder.rs b/sdk/src/identity/x509/x509_credential_holder.rs index ccc609de0..24be5464b 100644 --- a/sdk/src/identity/x509/x509_credential_holder.rs +++ b/sdk/src/identity/x509/x509_credential_holder.rs @@ -16,6 +16,7 @@ use crate::{ cose::{sign, TimeStampStorage}, raw_signature::RawSigner, }, + http::SyncGenericResolver, identity::{ builder::{CredentialHolder, IdentityBuilderError}, SignerPayload, @@ -64,6 +65,8 @@ impl CredentialHolder for X509CredentialHolder { &sp_cbor, None, TimeStampStorage::V2_sigTst2_CTT, + // TODO: https://github.com/contentauth/c2pa-rs/issues/1645 + &SyncGenericResolver::new(), ) .map_err(|e| IdentityBuilderError::SignerError(e.to_string())) } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 64bd61db7..1d3370c9e 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -211,7 +211,6 @@ pub mod crypto; #[doc(hidden)] pub mod dynamic_assertion; -// TODO: pub it when we expose in high-level API /// The `http` module contains generic traits for configuring sync and async HTTP resolvers. pub(crate) mod http; diff --git a/sdk/src/maybe_send_sync.rs b/sdk/src/maybe_send_sync.rs index c81e0c402..898ab70e4 100644 --- a/sdk/src/maybe_send_sync.rs +++ b/sdk/src/maybe_send_sync.rs @@ -50,10 +50,10 @@ pub trait MaybeSend: Send {} pub trait MaybeSend {} #[cfg(not(target_arch = "wasm32"))] -impl MaybeSend for T {} +impl MaybeSend for T {} #[cfg(target_arch = "wasm32")] -impl MaybeSend for T {} +impl MaybeSend for T {} /// A trait that is `Sync` on non-WASM targets and not `Sync` on WASM targets. /// @@ -83,7 +83,7 @@ pub trait MaybeSync: Sync {} pub trait MaybeSync {} #[cfg(not(target_arch = "wasm32"))] -impl MaybeSync for T {} +impl MaybeSync for T {} #[cfg(target_arch = "wasm32")] -impl MaybeSync for T {} +impl MaybeSync for T {} diff --git a/sdk/src/settings/signer.rs b/sdk/src/settings/signer.rs index daf16a981..1480c354c 100644 --- a/sdk/src/settings/signer.rs +++ b/sdk/src/settings/signer.rs @@ -213,8 +213,13 @@ impl Signer for CawgX509IdentitySigner { self.c2pa_signer.timestamp_request_body(message) } - fn send_timestamp_request(&self, message: &[u8]) -> Option>> { - self.c2pa_signer.send_timestamp_request(message) + fn send_timestamp_request( + &self, + http_resolver: &dyn SyncHttpResolver, + message: &[u8], + ) -> Option>> { + self.c2pa_signer + .send_timestamp_request(http_resolver, message) } fn ocsp_val(&self) -> Option> { diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 1013ccf15..17665b1c8 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -19,7 +19,7 @@ use crate::{ time_stamp::{TimeStampError, TimeStampProvider}, }, dynamic_assertion::{AsyncDynamicAssertion, DynamicAssertion}, - http::SyncGenericResolver, + http::{AsyncHttpResolver, SyncHttpResolver}, maybe_send_sync::{MaybeSend, MaybeSync}, Result, }; @@ -87,7 +87,11 @@ pub trait Signer { /// /// The default implementation will send the request to the URL /// provided by [`Self::time_authority_url()`], if any. - fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + fn send_timestamp_request( + &self, + http_resolver: &dyn SyncHttpResolver, + message: &[u8], + ) -> Option>> { if let Some(url) = self.time_authority_url() { if let Ok(body) = self.timestamp_request_body(message) { let headers: Option> = self.timestamp_request_headers(); @@ -97,7 +101,7 @@ pub trait Signer { headers, &body, message, - &SyncGenericResolver::new(), + http_resolver, ) .map_err(|e| e.into()), ); @@ -216,11 +220,13 @@ pub trait AsyncSigner: MaybeSend + MaybeSync { /// /// The default implementation will send the request to the URL /// provided by [`Self::time_authority_url()`], if any. - async fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + async fn send_timestamp_request( + &self, + http_resolver: &dyn AsyncHttpResolver, + message: &[u8], + ) -> Option>> { if let Some(url) = self.time_authority_url() { if let Ok(body) = self.timestamp_request_body(message) { - use crate::http::AsyncGenericResolver; - let headers: Option> = self.timestamp_request_headers(); return Some( crate::crypto::time_stamp::default_rfc3161_request_async( @@ -228,7 +234,7 @@ pub trait AsyncSigner: MaybeSend + MaybeSync { headers, &body, message, - &AsyncGenericResolver::new(), + http_resolver, ) .await .map_err(|e| e.into()), @@ -319,8 +325,12 @@ impl Signer for Box { (**self).timestamp_request_body(message) } - fn send_timestamp_request(&self, message: &[u8]) -> Option>> { - (**self).send_timestamp_request(message) + fn send_timestamp_request( + &self, + http_resolver: &dyn SyncHttpResolver, + message: &[u8], + ) -> Option>> { + (**self).send_timestamp_request(http_resolver, message) } fn raw_signer(&self) -> Option> { @@ -369,10 +379,11 @@ impl TimeStampProvider for Box { fn send_time_stamp_request( &self, + http_resolver: &dyn SyncHttpResolver, message: &[u8], ) -> Option, TimeStampError>> { self.as_ref() - .send_timestamp_request(message) + .send_timestamp_request(http_resolver, message) .map(|r| Ok(r?)) } } @@ -410,8 +421,14 @@ impl AsyncSigner for Box { (**self).timestamp_request_body(message) } - async fn send_timestamp_request(&self, message: &[u8]) -> Option>> { - (**self).send_timestamp_request(message).await + async fn send_timestamp_request( + &self, + http_resolver: &dyn AsyncHttpResolver, + message: &[u8], + ) -> Option>> { + (**self) + .send_timestamp_request(http_resolver, message) + .await } async fn ocsp_val(&self) -> Option> { @@ -469,9 +486,13 @@ impl Signer for RawSignerWrapper { .map_err(|e| e.into()) } - fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + fn send_timestamp_request( + &self, + http_resolver: &dyn SyncHttpResolver, + message: &[u8], + ) -> Option>> { self.0 - .send_time_stamp_request(message) + .send_time_stamp_request(http_resolver, message) .map(|r| r.map_err(|e| e.into())) } diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 2a47afcf4..3e2210a1e 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -59,6 +59,7 @@ use crate::{ error::{Error, Result}, hash_utils::{hash_by_alg, vec_compare, verify_by_alg}, hashed_uri::HashedUri, + http::{AsyncHttpResolver, SyncHttpResolver}, jumbf::{ self, boxes::*, @@ -548,6 +549,7 @@ impl Store { signer: &dyn AsyncSigner, box_size: usize, settings: &Settings, + http_resolver: &impl AsyncHttpResolver, ))] pub fn sign_claim( &self, @@ -555,6 +557,7 @@ impl Store { signer: &dyn Signer, box_size: usize, settings: &Settings, + http_resolver: &impl SyncHttpResolver, ) -> Result> { let claim_bytes = claim.data()?; @@ -573,7 +576,14 @@ impl Store { // Let the signer do all the COSE processing and return the structured COSE data. return signer.sign(&claim_bytes); // do not verify remote signers (we never did) } else { - cose_sign(signer, &claim_bytes, box_size, tss, &adjusted_settings) + cose_sign( + signer, + &claim_bytes, + box_size, + tss, + &adjusted_settings, + http_resolver, + ) } } else { if signer.direct_cose_handling() { @@ -581,7 +591,7 @@ impl Store { return signer.sign(claim_bytes.clone()).await; // do not verify remote signers (we never did) } else { - cose_sign_async(signer, &claim_bytes, box_size, tss, settings).await + cose_sign_async(signer, &claim_bytes, box_size, tss, settings, http_resolver).await } }; match result { @@ -2547,7 +2557,13 @@ impl Store { // sign contents let pc = self.provenance_claim().ok_or(Error::ClaimEncoding)?; - let sig = self.sign_claim(pc, signer, signer.reserve_size(), context.settings())?; + let sig = self.sign_claim( + pc, + signer, + signer.reserve_size(), + context.settings(), + &context.resolver(), + )?; let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); @@ -2579,7 +2595,13 @@ impl Store { // sign contents let pc = self.provenance_claim().ok_or(Error::ClaimEncoding)?; let sig = self - .sign_claim_async(pc, signer, signer.reserve_size(), context.settings()) + .sign_claim_async( + pc, + signer, + signer.reserve_size(), + context.settings(), + &context.resolver_async(), + ) .await?; let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); @@ -2611,7 +2633,13 @@ impl Store { let mut jumbf_bytes = self.to_jumbf_internal(signer.reserve_size())?; // sign contents - let sig = self.sign_claim(pc, signer, signer.reserve_size(), context.settings())?; + let sig = self.sign_claim( + pc, + signer, + signer.reserve_size(), + context.settings(), + &context.resolver(), + )?; let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); if sig_placeholder.len() != sig.len() { @@ -2649,7 +2677,13 @@ impl Store { // sign contents let sig = self - .sign_claim_async(pc, signer, signer.reserve_size(), context.settings()) + .sign_claim_async( + pc, + signer, + signer.reserve_size(), + context.settings(), + &context.resolver_async(), + ) .await?; let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); @@ -2917,7 +2951,13 @@ impl Store { // sign the claim let pc = temp_store.provenance_claim().ok_or(Error::ClaimEncoding)?; - let sig = temp_store.sign_claim(pc, signer, signer.reserve_size(), context.settings())?; + let sig = temp_store.sign_claim( + pc, + signer, + signer.reserve_size(), + context.settings(), + &context.resolver(), + )?; let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); match temp_store.finish_save(jumbf_bytes, &dest_path, sig, &sig_placeholder) { @@ -3027,10 +3067,22 @@ impl Store { let pc = self.provenance_claim().ok_or(Error::ClaimEncoding)?; let sig = if _sync { - self.sign_claim(pc, signer, signer.reserve_size(), settings) + self.sign_claim( + pc, + signer, + signer.reserve_size(), + settings, + &context.resolver(), + ) } else { - self.sign_claim_async(pc, signer, signer.reserve_size(), settings) - .await + self.sign_claim_async( + pc, + signer, + signer.reserve_size(), + settings, + &context.resolver_async(), + ) + .await }?; let sig_placeholder = Store::sign_claim_placeholder(pc, signer.reserve_size()); diff --git a/sdk/src/utils/ephemeral_signer.rs b/sdk/src/utils/ephemeral_signer.rs index a863b183c..847b41563 100644 --- a/sdk/src/utils/ephemeral_signer.rs +++ b/sdk/src/utils/ephemeral_signer.rs @@ -28,6 +28,7 @@ use x509_parser::prelude::X509Certificate; use crate::{ crypto::raw_signature::{signer_from_cert_chain_and_private_key, RawSigner, SigningAlg}, + http::SyncHttpResolver, Error, Result, Signer, }; @@ -230,9 +231,13 @@ impl Signer for EphemeralSigner { .map_err(|e| e.into()) } - fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + fn send_timestamp_request( + &self, + http_resolver: &dyn SyncHttpResolver, + message: &[u8], + ) -> Option>> { self.raw_signer - .send_time_stamp_request(message) + .send_time_stamp_request(http_resolver, message) .map(|r| r.map_err(|e| e.into())) } } @@ -252,6 +257,7 @@ mod tests { crypto::cose::{ cert_chain_from_sign1, parse_cose_sign1, CertificateTrustPolicy, TimeStampStorage, }, + http::SyncGenericResolver, settings::Settings, status_tracker::StatusTracker, Signer, @@ -273,8 +279,15 @@ mod tests { let tss = TimeStampStorage::V1_sigTst; - let cose_bytes = cose_sign(&signer, &claim_bytes, signer.reserve_size(), tss, &settings) - .expect("cose_sign with EphemeralSigner"); + let cose_bytes = cose_sign( + &signer, + &claim_bytes, + signer.reserve_size(), + tss, + &settings, + &SyncGenericResolver::new(), + ) + .expect("cose_sign with EphemeralSigner"); let mut log = StatusTracker::default(); let sign1 = parse_cose_sign1(&cose_bytes, &claim_bytes, &mut log) @@ -310,8 +323,15 @@ mod tests { let tss = TimeStampStorage::V1_sigTst; - let cose_bytes = cose_sign(&signer, &claim_bytes, signer.reserve_size(), tss, &settings) - .expect("cose_sign with EphemeralSigner"); + let cose_bytes = cose_sign( + &signer, + &claim_bytes, + signer.reserve_size(), + tss, + &settings, + &SyncGenericResolver::new(), + ) + .expect("cose_sign with EphemeralSigner"); let mut log = StatusTracker::default(); let sign1 = parse_cose_sign1(&cose_bytes, &claim_bytes, &mut log).expect("parse COSE"); @@ -351,8 +371,14 @@ mod tests { let mut settings = Settings::default(); settings.verify.verify_trust = false; - let cose_bytes = sign_claim(&claim_bytes, &signer, signer.reserve_size(), &settings) - .expect("sign_claim with EphemeralSigner"); + let cose_bytes = sign_claim( + &claim_bytes, + &signer, + signer.reserve_size(), + &settings, + &SyncGenericResolver::new(), + ) + .expect("sign_claim with EphemeralSigner"); let mut validation_log = StatusTracker::default(); let ctp = CertificateTrustPolicy::default(); @@ -395,9 +421,14 @@ mod tests { let mut settings = Settings::default(); settings.verify.verify_trust = false; - let cose_bytes = - crate::cose_sign::sign_claim(&claim_bytes, &signer, signer.reserve_size(), &settings) - .expect("sign_claim with EphemeralSigner"); + let cose_bytes = crate::cose_sign::sign_claim( + &claim_bytes, + &signer, + signer.reserve_size(), + &settings, + &SyncGenericResolver::new(), + ) + .expect("sign_claim with EphemeralSigner"); let mut validation_log = StatusTracker::default(); diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 02f9cf4a3..7907f47cc 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -35,6 +35,7 @@ use crate::{ context::Context, crypto::{cose::CertificateTrustPolicy, raw_signature::SigningAlg}, hash_utils::Hasher, + http::{AsyncHttpResolver, SyncHttpResolver}, jumbf_io::get_assetio_handler, resource_store::UriOrResource, store::Store, @@ -706,7 +707,11 @@ impl crate::Signer for TestGoodSigner { 1024 } - fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { + fn send_timestamp_request( + &self, + _http_resolver: &dyn SyncHttpResolver, + _message: &[u8], + ) -> Option>> { Some(Ok(Vec::new())) } } @@ -734,6 +739,7 @@ impl AsyncSigner for AsyncTestGoodSigner { async fn send_timestamp_request( &self, + _http_resolver: &dyn AsyncHttpResolver, _message: &[u8], ) -> Option>> { Some(Ok(Vec::new())) diff --git a/sdk/src/utils/test_signer.rs b/sdk/src/utils/test_signer.rs index 92a08e521..5bbb6be06 100644 --- a/sdk/src/utils/test_signer.rs +++ b/sdk/src/utils/test_signer.rs @@ -20,6 +20,7 @@ use crate::{ async_signer_from_cert_chain_and_private_key, signer_from_cert_chain_and_private_key, AsyncRawSigner, SigningAlg, }, + http::AsyncHttpResolver, signer::{BoxedAsyncSigner, BoxedSigner, RawSignerWrapper}, AsyncSigner, Result, }; @@ -155,9 +156,13 @@ impl AsyncSigner for AsyncRawSignerWrapper { .map_err(|e| e.into()) } - async fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + async fn send_timestamp_request( + &self, + http_resolver: &dyn AsyncHttpResolver, + message: &[u8], + ) -> Option>> { self.0 - .send_time_stamp_request(message) + .send_time_stamp_request(http_resolver, message) .await .map(|r| r.map_err(|e| e.into())) }