diff --git a/internal/crypto/src/time_stamp/mod.rs b/internal/crypto/src/time_stamp/mod.rs index 1f2c418f3..6e9495610 100644 --- a/internal/crypto/src/time_stamp/mod.rs +++ b/internal/crypto/src/time_stamp/mod.rs @@ -20,9 +20,11 @@ pub use error::TimeStampError; #[cfg(not(target_arch = "wasm32"))] mod http_request; +#[cfg(not(target_arch = "wasm32"))] +pub use http_request::{default_rfc3161_request, default_rfc3161_request_async}; mod provider; -pub use provider::{AsyncTimeStampProvider, TimeStampProvider}; +pub use provider::{default_rfc3161_message, AsyncTimeStampProvider, TimeStampProvider}; mod response; diff --git a/internal/crypto/src/time_stamp/provider.rs b/internal/crypto/src/time_stamp/provider.rs index 0502f6d90..414d457e5 100644 --- a/internal/crypto/src/time_stamp/provider.rs +++ b/internal/crypto/src/time_stamp/provider.rs @@ -125,7 +125,10 @@ pub trait AsyncTimeStampProvider { } } -fn default_rfc3161_message(data: &[u8]) -> Result, TimeStampError> { +/// Create an [RFC 3161] time stamp request message for a given piece of data. +/// +/// [RFC 3161]: https://datatracker.ietf.org/doc/html/rfc3161 +pub fn default_rfc3161_message(data: &[u8]) -> Result, TimeStampError> { let request = time_stamp_message_http(data, DigestAlgorithm::Sha256)?; let mut body = Vec::::new(); diff --git a/sdk/src/callback_signer.rs b/sdk/src/callback_signer.rs index 0bac60ec8..494e0851a 100644 --- a/sdk/src/callback_signer.rs +++ b/sdk/src/callback_signer.rs @@ -156,16 +156,13 @@ impl Signer for CallbackSigner { fn reserve_size(&self) -> usize { self.reserve_size } -} -impl TimeStampProvider for CallbackSigner { - fn time_stamp_service_url(&self) -> Option { + fn time_authority_url(&self) -> Option { self.tsa_url.clone() } } use async_trait::async_trait; -use c2pa_crypto::time_stamp::{AsyncTimeStampProvider, TimeStampProvider}; #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] @@ -187,19 +184,13 @@ impl AsyncSigner for CallbackSigner { fn reserve_size(&self) -> usize { self.reserve_size } -} -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -impl AsyncTimeStampProvider for CallbackSigner { - fn time_stamp_service_url(&self) -> Option { + fn time_authority_url(&self) -> Option { self.tsa_url.clone() } #[cfg(target_arch = "wasm32")] - async fn send_time_stamp_request( - &self, - _message: &[u8], - ) -> Option, c2pa_crypto::time_stamp::TimeStampError>> { + async fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { None } } diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 7ceb02f5c..81dccd2f3 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -358,8 +358,6 @@ fn pad_cose_sig(sign1: &mut CoseSign1, end_size: usize) -> Result> { mod tests { #![allow(clippy::unwrap_used)] - use c2pa_crypto::time_stamp::{TimeStampError, TimeStampProvider}; - use super::sign_claim; use crate::{claim::Claim, utils::test::temp_signer}; @@ -428,13 +426,8 @@ mod tests { fn reserve_size(&self) -> usize { 1024 } - } - impl TimeStampProvider for BogusSigner { - fn send_time_stamp_request( - &self, - _message: &[u8], - ) -> Option, TimeStampError>> { + fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { Some(Ok(Vec::new())) } } diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index b746d7827..8ef312a95 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -1504,8 +1504,6 @@ pub mod tests { } } - impl c2pa_crypto::time_stamp::TimeStampProvider for OcspSigner {} - let ocsp_signer = OcspSigner { signer: Box::new(signer), ocsp_rsp: ocsp_rsp_data.to_vec(), diff --git a/sdk/src/openssl/ec_signer.rs b/sdk/src/openssl/ec_signer.rs index 99908c520..136cf328e 100644 --- a/sdk/src/openssl/ec_signer.rs +++ b/sdk/src/openssl/ec_signer.rs @@ -11,7 +11,7 @@ // specific language governing permissions and limitations under // each license. -use c2pa_crypto::{openssl::OpenSslMutex, time_stamp::TimeStampProvider, SigningAlg}; +use c2pa_crypto::{openssl::OpenSslMutex, SigningAlg}; use openssl::{ ec::EcKey, hash::MessageDigest, @@ -107,14 +107,12 @@ impl Signer for EcSigner { Ok(certs) } - fn reserve_size(&self) -> usize { - 1024 + self.certs_size + self.timestamp_size // the Cose_Sign1 contains complete certs and timestamps so account for size + fn time_authority_url(&self) -> Option { + self.tsa_url.clone() } -} -impl TimeStampProvider for EcSigner { - fn time_stamp_service_url(&self) -> Option { - self.tsa_url.clone() + fn reserve_size(&self) -> usize { + 1024 + self.certs_size + self.timestamp_size // the Cose_Sign1 contains complete certs and timestamps so account for size } } diff --git a/sdk/src/openssl/ed_signer.rs b/sdk/src/openssl/ed_signer.rs index cceb38d34..21202eebf 100644 --- a/sdk/src/openssl/ed_signer.rs +++ b/sdk/src/openssl/ed_signer.rs @@ -11,7 +11,7 @@ // specific language governing permissions and limitations under // each license. -use c2pa_crypto::{openssl::OpenSslMutex, time_stamp::TimeStampProvider, SigningAlg}; +use c2pa_crypto::{openssl::OpenSslMutex, SigningAlg}; use openssl::{ pkey::{PKey, Private}, x509::X509, @@ -97,14 +97,12 @@ impl Signer for EdSigner { Ok(certs) } - fn reserve_size(&self) -> usize { - 1024 + self.certs_size + self.timestamp_size // the Cose_Sign1 contains complete certs and timestamps so account for size + fn time_authority_url(&self) -> Option { + self.tsa_url.clone() } -} -impl TimeStampProvider for EdSigner { - fn time_stamp_service_url(&self) -> Option { - self.tsa_url.clone() + fn reserve_size(&self) -> usize { + 1024 + self.certs_size + self.timestamp_size // the Cose_Sign1 contains complete certs and timestamps so account for size } } diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs index e11bdde23..4dc81fa2a 100644 --- a/sdk/src/openssl/temp_signer_async.rs +++ b/sdk/src/openssl/temp_signer_async.rs @@ -60,7 +60,7 @@ impl AsyncSignerAdapter { alg, certs: signer.certs().unwrap_or_default(), reserve_size: signer.reserve_size(), - tsa_url: signer.time_stamp_service_url(), + tsa_url: signer.time_authority_url(), ocsp_val: signer.ocsp_val(), } } @@ -91,16 +91,11 @@ impl crate::AsyncSigner for AsyncSignerAdapter { self.reserve_size } - async fn ocsp_val(&self) -> Option> { - self.ocsp_val.clone() + fn time_authority_url(&self) -> Option { + self.tsa_url.clone() } -} -#[cfg(test)] -#[cfg(feature = "openssl_sign")] -#[async_trait::async_trait] -impl c2pa_crypto::time_stamp::AsyncTimeStampProvider for AsyncSignerAdapter { - fn time_stamp_service_url(&self) -> Option { - self.tsa_url.clone() + async fn ocsp_val(&self) -> Option> { + self.ocsp_val.clone() } } diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 71062d22e..cc42fa37a 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -11,16 +11,15 @@ // specific language governing permissions and limitations under // each license. -use c2pa_crypto::{ - time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider}, - SigningAlg, -}; +use async_trait::async_trait; +use c2pa_crypto::SigningAlg; use crate::{DynamicAssertion, Result}; + /// The `Signer` trait generates a cryptographic signature over a byte array. /// /// This trait exists to allow the signature mechanism to be extended. -pub trait Signer: TimeStampProvider { +pub trait Signer { /// Returns a new byte array which is a signature over the original. fn sign(&self, data: &[u8]) -> Result>; @@ -35,6 +34,46 @@ pub trait Signer: TimeStampProvider { /// than this value. fn reserve_size(&self) -> usize; + /// URL for time authority to time stamp the signature + fn time_authority_url(&self) -> Option { + None + } + + /// Additional request headers to pass to the time stamp authority. + /// + /// IMPORTANT: You should not include the "Content-type" header here. + /// That is provided by default. + fn timestamp_request_headers(&self) -> Option> { + None + } + + fn timestamp_request_body(&self, message: &[u8]) -> Result> { + c2pa_crypto::time_stamp::default_rfc3161_message(message).map_err(|e| e.into()) + } + + /// Request RFC 3161 timestamp to be included in the manifest data + /// structure. + /// + /// `message` is a preliminary hash of the claim + /// + /// The default implementation will send the request to the URL + /// provided by [`Self::time_authority_url()`], if any. + #[allow(unused)] // message not used on WASM + fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + #[cfg(not(target_arch = "wasm32"))] + if let Some(url) = self.time_authority_url() { + if let Ok(body) = self.timestamp_request_body(message) { + let headers: Option> = self.timestamp_request_headers(); + return Some( + c2pa_crypto::time_stamp::default_rfc3161_request(&url, headers, &body, message) + .map_err(|e| e.into()), + ); + } + } + + None + } + /// OCSP response for the signing cert if available /// This is the only C2PA supported cert revocation method. /// By pre-querying the value for a your signing cert the value can @@ -85,8 +124,6 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { ) -> Result; } -use async_trait::async_trait; - /// The `AsyncSigner` trait generates a cryptographic signature over a byte array. /// /// This trait exists to allow the signature mechanism to be extended. @@ -94,7 +131,7 @@ use async_trait::async_trait; /// Use this when the implementation is asynchronous. #[cfg(not(target_arch = "wasm32"))] #[async_trait] -pub trait AsyncSigner: Sync + AsyncTimeStampProvider { +pub trait AsyncSigner: Sync { /// Returns a new byte array which is a signature over the original. async fn sign(&self, data: Vec) -> Result>; @@ -109,6 +146,49 @@ pub trait AsyncSigner: Sync + AsyncTimeStampProvider { /// than this value. fn reserve_size(&self) -> usize; + /// URL for time authority to time stamp the signature + fn time_authority_url(&self) -> Option { + None + } + + /// Additional request headers to pass to the time stamp authority. + /// + /// IMPORTANT: You should not include the "Content-type" header here. + /// That is provided by default. + fn timestamp_request_headers(&self) -> Option> { + None + } + + fn timestamp_request_body(&self, message: &[u8]) -> Result> { + c2pa_crypto::time_stamp::default_rfc3161_message(message).map_err(|e| e.into()) + } + + /// Request RFC 3161 timestamp to be included in the manifest data + /// structure. + /// + /// `message` is a preliminary hash of the claim + /// + /// 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>> { + // NOTE: This is currently synchronous, but may become + // async in the future. + if let Some(url) = self.time_authority_url() { + if let Ok(body) = self.timestamp_request_body(message) { + let headers: Option> = self.timestamp_request_headers(); + return Some( + c2pa_crypto::time_stamp::default_rfc3161_request_async( + &url, headers, &body, message, + ) + .await + .map_err(|e| e.into()), + ); + } + } + + None + } + /// OCSP response for the signing cert if available /// This is the only C2PA supported cert revocation method. /// By pre-querying the value for a your signing cert the value can @@ -131,9 +211,14 @@ pub trait AsyncSigner: Sync + AsyncTimeStampProvider { } } +/// The `AsyncSigner` trait generates a cryptographic signature over a byte array. +/// +/// This trait exists to allow the signature mechanism to be extended. +/// +/// Use this when the implementation is asynchronous. #[cfg(target_arch = "wasm32")] #[async_trait(?Send)] -pub trait AsyncSigner: AsyncTimeStampProvider { +pub trait AsyncSigner { /// Returns a new byte array which is a signature over the original. async fn sign(&self, data: Vec) -> Result>; @@ -148,6 +233,34 @@ pub trait AsyncSigner: AsyncTimeStampProvider { /// than this value. fn reserve_size(&self) -> usize; + /// URL for time authority to time stamp the signature + fn time_authority_url(&self) -> Option { + None + } + + /// Additional request headers to pass to the time stamp authority. + /// + /// IMPORTANT: You should not include the "Content-type" header here. + /// That is provided by default. + fn timestamp_request_headers(&self) -> Option> { + None + } + + fn timestamp_request_body(&self, message: &[u8]) -> Result> { + c2pa_crypto::time_stamp::default_rfc3161_message(message).map_err(|e| e.into()) + } + + /// Request RFC 3161 timestamp to be included in the manifest data + /// structure. + /// + /// `message` is a preliminary hash of the claim + /// + /// 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>> { + None + } + /// OCSP response for the signing cert if available /// This is the only C2PA supported cert revocation method. /// By pre-querying the value for a your signing cert the value can @@ -216,27 +329,3 @@ impl Signer for Box { (**self).dynamic_assertions() } } - -impl TimeStampProvider for Box { - fn time_stamp_service_url(&self) -> Option { - (**self).time_stamp_service_url() - } - - fn time_stamp_request_headers(&self) -> Option> { - (**self).time_stamp_request_headers() - } - - fn time_stamp_request_body( - &self, - message: &[u8], - ) -> std::result::Result, TimeStampError> { - (**self).time_stamp_request_body(message) - } - - fn send_time_stamp_request( - &self, - message: &[u8], - ) -> Option, TimeStampError>> { - (**self).send_time_stamp_request(message) - } -} diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 1d6260d8b..a560a0970 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -3868,8 +3868,6 @@ pub mod tests { } } - impl c2pa_crypto::time_stamp::TimeStampProvider for BadSigner {} - #[test] #[cfg(feature = "file_io")] fn test_detects_unverifiable_signature() { @@ -5975,7 +5973,7 @@ pub mod tests { alg: signer.alg(), certs: signer.certs().unwrap_or_default(), reserve_size: signer.reserve_size(), - tsa_url: signer.time_stamp_service_url(), + tsa_url: signer.time_authority_url(), ocsp_val: signer.ocsp_val(), } } @@ -6004,6 +6002,10 @@ pub mod tests { self.reserve_size } + fn time_authority_url(&self) -> Option { + self.tsa_url.clone() + } + async fn ocsp_val(&self) -> Option> { self.ocsp_val.clone() } @@ -6014,13 +6016,6 @@ pub mod tests { } } - #[async_trait::async_trait] - impl c2pa_crypto::time_stamp::AsyncTimeStampProvider for DynamicSigner { - fn time_stamp_service_url(&self) -> Option { - self.tsa_url.clone() - } - } - let file_buffer = include_bytes!("../tests/fixtures/earth_apollo17.jpg").to_vec(); // convert buffer to cursor with Read/Write/Seek capability let mut buf_io = Cursor::new(file_buffer); diff --git a/sdk/src/time_stamp.rs b/sdk/src/time_stamp.rs index 8bb47db34..12a39b776 100644 --- a/sdk/src/time_stamp.rs +++ b/sdk/src/time_stamp.rs @@ -49,14 +49,9 @@ pub(crate) fn cose_timestamp_countersign( #[async_generic(async_signature(signer: &dyn AsyncSigner, data: &[u8]))] fn timestamp_data(signer: &dyn Signer, data: &[u8]) -> Option>> { if _sync { - signer - .send_time_stamp_request(data) - .map(|r| r.map_err(|e| e.into())) + signer.send_timestamp_request(data) } else { - signer - .send_time_stamp_request(data) - .await - .map(|r| r.map_err(|e| e.into())) + signer.send_timestamp_request(data).await // TO DO: Fix bug in async_generic. This .await // should be automatically removed. } diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 57a7ff741..485dfec82 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -301,13 +301,8 @@ impl crate::Signer for TestGoodSigner { fn reserve_size(&self) -> usize { 1024 } -} -impl c2pa_crypto::time_stamp::TimeStampProvider for TestGoodSigner { - fn send_time_stamp_request( - &self, - _message: &[u8], - ) -> Option, c2pa_crypto::time_stamp::TimeStampError>> { + fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { Some(Ok(Vec::new())) } } @@ -332,15 +327,11 @@ impl crate::AsyncSigner for AsyncTestGoodSigner { fn reserve_size(&self) -> usize { 1024 } -} -#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -impl c2pa_crypto::time_stamp::AsyncTimeStampProvider for AsyncTestGoodSigner { - async fn send_time_stamp_request( + async fn send_timestamp_request( &self, _message: &[u8], - ) -> Option, c2pa_crypto::time_stamp::TimeStampError>> { + ) -> Option>> { Some(Ok(Vec::new())) } } @@ -546,15 +537,8 @@ impl crate::signer::AsyncSigner for WebCryptoSigner { fn reserve_size(&self) -> usize { 10000 } -} -#[cfg(target_arch = "wasm32")] -#[async_trait::async_trait(?Send)] -impl c2pa_crypto::time_stamp::AsyncTimeStampProvider for WebCryptoSigner { - async fn send_time_stamp_request( - &self, - _: &[u8], - ) -> Option, c2pa_crypto::time_stamp::TimeStampError>> { + async fn send_timestamp_request(&self, _: &[u8]) -> Option>> { None } } @@ -622,15 +606,11 @@ impl crate::signer::AsyncSigner for TempAsyncRemoteSigner { fn reserve_size(&self) -> usize { 10000 } -} -#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -impl c2pa_crypto::time_stamp::AsyncTimeStampProvider for TempAsyncRemoteSigner { - async fn send_time_stamp_request( + async fn send_timestamp_request( &self, _message: &[u8], - ) -> Option, c2pa_crypto::time_stamp::TimeStampError>> { + ) -> Option>> { Some(Ok(Vec::new())) } } diff --git a/sdk/src/wasm/rsa_wasm_signer.rs b/sdk/src/wasm/rsa_wasm_signer.rs index 7c8f9b9d6..cb1cda0fa 100644 --- a/sdk/src/wasm/rsa_wasm_signer.rs +++ b/sdk/src/wasm/rsa_wasm_signer.rs @@ -14,10 +14,7 @@ use core::str; use async_trait::async_trait; -use c2pa_crypto::{ - time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider}, - SigningAlg, -}; +use c2pa_crypto::SigningAlg; use rsa::{ pkcs8::DecodePrivateKey, pss::SigningKey, @@ -115,14 +112,12 @@ impl Signer for RsaWasmSigner { self.alg } - fn ocsp_val(&self) -> Option> { - None + fn time_authority_url(&self) -> Option { + self.tsa_url.clone() } -} -impl TimeStampProvider for RsaWasmSigner { - fn time_stamp_service_url(&self) -> Option { - self.tsa_url.clone() + fn ocsp_val(&self) -> Option> { + None } } @@ -279,7 +274,7 @@ impl RsaWasmSignerAsync { } } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[async_trait(?Send)] impl AsyncSigner for RsaWasmSignerAsync { async fn sign(&self, data: Vec) -> Result> { self.signer.sign(&data) @@ -296,14 +291,8 @@ impl AsyncSigner for RsaWasmSignerAsync { fn reserve_size(&self) -> usize { self.signer.reserve_size() } -} -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -impl AsyncTimeStampProvider for RsaWasmSignerAsync { - async fn send_time_stamp_request( - &self, - _message: &[u8], - ) -> Option, TimeStampError>> { + async fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { None } }