From 4a6e94d01afd61bd25d3da29cfbb62d8718c0749 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Thu, 5 Dec 2024 17:28:40 -0800 Subject: [PATCH 01/32] feat: Add `RawSigner` trait to `c2pa-crypto` (derived from `c2pa::Signer`) --- internal/crypto/src/raw_signature/mod.rs | 3 + internal/crypto/src/raw_signature/signer.rs | 169 ++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 internal/crypto/src/raw_signature/signer.rs diff --git a/internal/crypto/src/raw_signature/mod.rs b/internal/crypto/src/raw_signature/mod.rs index 67e0510b5..b6a8c37e9 100644 --- a/internal/crypto/src/raw_signature/mod.rs +++ b/internal/crypto/src/raw_signature/mod.rs @@ -13,6 +13,9 @@ //! Tools for working with raw signature algorithms. +pub(crate) mod signer; +pub use signer::{AsyncRawSigner, RawSigner, RawSignerError}; + pub(crate) mod oids; mod validator; diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs new file mode 100644 index 000000000..729194d01 --- /dev/null +++ b/internal/crypto/src/raw_signature/signer.rs @@ -0,0 +1,169 @@ +// 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 async_trait::async_trait; +use thiserror::Error; + +use crate::SigningAlg; + +/// Implementations of the `RawSigner` trait generate a cryptographic signature +/// over an arbitrary byte array. +/// +/// If an implementation _can_ be asynchronous, that is preferred. +pub trait RawSigner { + /// Return a raw signature over the original byte slice. + fn sign(&self, data: &[u8]) -> Result, RawSignerError>; + + /// Return the algorithm implemented by this signer. + fn alg(&self) -> SigningAlg; + + /// Return the signing certificate chain. + /// + /// Each certificate should be encoded in DER format and sequenced from + /// end-entity certificate to the outermost certificate authority. + fn cert_chain(&self) -> Result>, RawSignerError>; + + /// Return the size in bytes of the largest possible expected signature. + /// Signing will fail if the result of the [`sign`] function is larger + /// than this value. + /// + /// [`sign`]: Self::sign + fn reserve_size(&self) -> usize; + + /// Return an OCSP response for the signing certificate if available. + /// + /// By pre-querying the value for the signing certificate, the value can be + /// cached which will reduce load on the certificate authority, as + /// recommended by the C2PA spec. + fn ocsp_val(&self) -> Option> { + None + } +} + +/// This trait exists to allow the built-in [`RawSigner`] implementations to be +/// configured from a private/public key pair. +#[allow(dead_code)] // TEMPORARY while refactoring +pub(crate) trait ConfigurableSigner: RawSigner + Sized { + fn from_signcert_and_pkey( + signcert: &[u8], + pkey: &[u8], + alg: SigningAlg, + tsa_url: Option, + ) -> Result; + + fn from_files>( + signcert_path: P, + pkey_path: P, + alg: SigningAlg, + tsa_url: Option, + ) -> Result { + let signcert = std::fs::read(signcert_path)?; + let pkey = std::fs::read(pkey_path)?; + + Self::from_signcert_and_pkey(&signcert, &pkey, alg, tsa_url) + } +} + +/// Implementations of the `AsyncRawSigner` trait generate a cryptographic +/// signature over an arbitrary byte array. +/// +/// Use this trait only when the implementation must be asynchronous. +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait AsyncRawSigner: Sync { + /// Return a raw signature over the original byte slice. + async fn sign(&self, data: Vec) -> Result, RawSignerError>; + + /// Return the algorithm implemented by this signer. + fn alg(&self) -> SigningAlg; + + /// Return the signing certificate chain. + /// + /// Each certificate should be encoded in DER format and sequenced from + /// end-entity certificate to the outermost certificate authority. + fn certs(&self) -> Result>, RawSignerError>; + + /// Return the size in bytes of the largest possible expected signature. + /// Signing will fail if the result of the [`sign`] function is larger + /// than this value. + /// + /// [`sign`]: Self::sign + fn reserve_size(&self) -> usize; + + /// Return an OCSP response for the signing certificate if available. + /// + /// By pre-querying the value for the signing certificate, the value can be + /// cached which will reduce load on the certificate authority, as + /// recommended by the C2PA spec. + async fn ocsp_val(&self) -> Option> { + None + } +} + +/// Describes errors that can be identified when generating a raw signature. +#[derive(Debug, Eq, Error, PartialEq)] +#[non_exhaustive] +pub enum RawSignerError { + /// An I/O error occurred. This typically happens when loading + /// public/private key material from files. + /// + /// NOTE: We do not directly capture the I/O error itself because it + /// lacks an `Eq` implementation. Instead we capture the error description. + #[error("I/O error ({0})")] + IoError(String), + + /// An error was reported by the OpenSSL native code. + /// + /// NOTE: We do not directly capture the OpenSSL error itself because it + /// lacks an `Eq` implementation. Instead we capture the error description. + #[cfg(feature = "openssl")] + #[error("an error was reported by OpenSSL native code: {0}")] + OpenSslError(String), + + /// The OpenSSL native code mutex could not be acquired. + #[cfg(feature = "openssl")] + #[error(transparent)] + OpenSslMutexUnavailable(#[from] crate::openssl::OpenSslMutexUnavailable), + + /// An unexpected internal error occured while requesting the time stamp + /// response. + #[error("internal error ({0})")] + InternalError(&'static str), +} + +impl From for RawSignerError { + fn from(err: std::io::Error) -> Self { + Self::IoError(err.to_string()) + } +} + +#[cfg(feature = "openssl")] +impl From for RawSignerError { + fn from(err: openssl::error::ErrorStack) -> Self { + Self::OpenSslError(err.to_string()) + } +} + +#[cfg(target_arch = "wasm32")] +impl From for RawSignerError { + fn from(err: crate::webcrypto::WasmCryptoError) -> Self { + match err { + crate::webcrypto::WasmCryptoError::UnknownContext => { + Self::InternalError("unknown WASM context") + } + crate::webcrypto::WasmCryptoError::NoCryptoAvailable => { + Self::InternalError("WASM crypto unavailable") + } + } + } +} From e2a8cd662db9aaf437e35be60ca294d4fe2cb14c Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Thu, 5 Dec 2024 17:34:42 -0800 Subject: [PATCH 02/32] Move `ConfigurableSigner` to end of file --- internal/crypto/src/raw_signature/signer.rs | 48 ++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index 729194d01..c5ce9343b 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -50,30 +50,6 @@ pub trait RawSigner { } } -/// This trait exists to allow the built-in [`RawSigner`] implementations to be -/// configured from a private/public key pair. -#[allow(dead_code)] // TEMPORARY while refactoring -pub(crate) trait ConfigurableSigner: RawSigner + Sized { - fn from_signcert_and_pkey( - signcert: &[u8], - pkey: &[u8], - alg: SigningAlg, - tsa_url: Option, - ) -> Result; - - fn from_files>( - signcert_path: P, - pkey_path: P, - alg: SigningAlg, - tsa_url: Option, - ) -> Result { - let signcert = std::fs::read(signcert_path)?; - let pkey = std::fs::read(pkey_path)?; - - Self::from_signcert_and_pkey(&signcert, &pkey, alg, tsa_url) - } -} - /// Implementations of the `AsyncRawSigner` trait generate a cryptographic /// signature over an arbitrary byte array. /// @@ -167,3 +143,27 @@ impl From for RawSignerError { } } } + +/// This trait exists to allow the built-in [`RawSigner`] implementations to be +/// configured from a private/public key pair. +#[allow(dead_code)] // TEMPORARY while refactoring +pub(crate) trait ConfigurableSigner: RawSigner + Sized { + fn from_signcert_and_pkey( + signcert: &[u8], + pkey: &[u8], + alg: SigningAlg, + tsa_url: Option, + ) -> Result; + + fn from_files>( + signcert_path: P, + pkey_path: P, + alg: SigningAlg, + tsa_url: Option, + ) -> Result { + let signcert = std::fs::read(signcert_path)?; + let pkey = std::fs::read(pkey_path)?; + + Self::from_signcert_and_pkey(&signcert, &pkey, alg, tsa_url) + } +} From 9dd1741a1559f764d41254249fcbc45af3d310e6 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Thu, 5 Dec 2024 18:09:40 -0800 Subject: [PATCH 03/32] Adjust function names a bit --- internal/crypto/src/raw_signature/signer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index c5ce9343b..4bd89957b 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -45,7 +45,7 @@ pub trait RawSigner { /// By pre-querying the value for the signing certificate, the value can be /// cached which will reduce load on the certificate authority, as /// recommended by the C2PA spec. - fn ocsp_val(&self) -> Option> { + fn ocsp_response(&self) -> Option> { None } } @@ -67,7 +67,7 @@ pub trait AsyncRawSigner: Sync { /// /// Each certificate should be encoded in DER format and sequenced from /// end-entity certificate to the outermost certificate authority. - fn certs(&self) -> Result>, RawSignerError>; + fn cert_chain(&self) -> Result>, RawSignerError>; /// Return the size in bytes of the largest possible expected signature. /// Signing will fail if the result of the [`sign`] function is larger @@ -81,7 +81,7 @@ pub trait AsyncRawSigner: Sync { /// By pre-querying the value for the signing certificate, the value can be /// cached which will reduce load on the certificate authority, as /// recommended by the C2PA spec. - async fn ocsp_val(&self) -> Option> { + async fn ocsp_response(&self) -> Option> { None } } @@ -114,7 +114,7 @@ pub enum RawSignerError { /// An unexpected internal error occured while requesting the time stamp /// response. #[error("internal error ({0})")] - InternalError(&'static str), + InternalError(String), } impl From for RawSignerError { From 1497f9d62955a5af0ce4499a6c9ab97da1401088 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Thu, 5 Dec 2024 18:09:59 -0800 Subject: [PATCH 04/32] Update `CallbackSigner` for new trait interface --- sdk/src/callback_signer.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/sdk/src/callback_signer.rs b/sdk/src/callback_signer.rs index 0bac60ec8..92b804f19 100644 --- a/sdk/src/callback_signer.rs +++ b/sdk/src/callback_signer.rs @@ -16,13 +16,13 @@ //! The `callback_signer` module provides a way to obtain a [`Signer`] or [`AsyncSigner`] //! using a callback and public signing certificates. -use c2pa_crypto::SigningAlg; - -use crate::{ - error::{Error, Result}, - AsyncSigner, Signer, +use c2pa_crypto::{ + raw_signature::{AsyncRawSigner, RawSigner, RawSignerError}, + SigningAlg, }; +use crate::{error::Error, AsyncSigner, Signer}; + /// Defines a callback function interface for a [`CallbackSigner`]. /// /// The callback should return a signature for the given data. @@ -54,7 +54,6 @@ pub struct CallbackSigner { } unsafe impl Send for CallbackSigner {} - unsafe impl Sync for CallbackSigner {} impl CallbackSigner { @@ -139,8 +138,10 @@ impl Default for CallbackSigner { } } -impl Signer for CallbackSigner { - fn sign(&self, data: &[u8]) -> Result> { +impl Signer for CallbackSigner {} + +impl RawSigner for CallbackSigner { + fn sign(&self, data: &[u8]) -> std::result::Result, RawSignerError> { (self.callback)(self.context, data) } @@ -148,8 +149,10 @@ impl Signer for CallbackSigner { self.alg } - fn certs(&self) -> Result>> { - let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?; + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { + let pems = pem::parse_many(&self.certs) + .map_err(|e| RawSignerError::InternalError(e.to_string()))?; + Ok(pems.into_iter().map(|p| p.into_contents()).collect()) } @@ -170,8 +173,10 @@ use c2pa_crypto::time_stamp::{AsyncTimeStampProvider, TimeStampProvider}; #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] // I'm not sure if this is useful since the callback is still synchronous. -impl AsyncSigner for CallbackSigner { - async fn sign(&self, data: Vec) -> Result> { +impl AsyncSigner for CallbackSigner {} + +impl AsyncRawSigner for CallbackSigner { + async fn sign(&self, data: Vec) -> std::result::Result, RawSignerError> { (self.callback)(self.context, &data) } @@ -179,7 +184,7 @@ impl AsyncSigner for CallbackSigner { self.alg } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?; Ok(pems.into_iter().map(|p| p.into_contents()).collect()) } From 795516d3498f328fc58da060bb7d2d3f1ef118e9 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Thu, 5 Dec 2024 18:12:17 -0800 Subject: [PATCH 05/32] Redefine `Signer` and `AsyncSigner` in terms of the new raw signer traits --- sdk/src/signer.rs | 117 ++++++++-------------------------------------- 1 file changed, 19 insertions(+), 98 deletions(-) diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 71062d22e..d86fb39f0 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -12,37 +12,17 @@ // each license. use c2pa_crypto::{ + raw_signature::{AsyncRawSigner, RawSigner, RawSignerError}, time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider}, SigningAlg, }; +use x509_certificate::Sign; 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 { - /// Returns a new byte array which is a signature over the original. - fn sign(&self, data: &[u8]) -> Result>; - - /// Returns the algorithm of the Signer. - fn alg(&self) -> SigningAlg; - - /// Returns the certificates as a Vec containing a Vec of DER bytes for each certificate. - fn certs(&self) -> Result>>; - - /// Returns the size in bytes of the largest possible expected signature. - /// Signing will fail if the result of the `sign` function is larger - /// than this value. - fn reserve_size(&self) -> usize; - - /// 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 - /// be cached taking pressure off of the CA (recommended by C2PA spec) - fn ocsp_val(&self) -> Option> { - None - } - +pub trait Signer: RawSigner + TimeStampProvider { /// If this returns true the sign function is responsible for for direct handling of the COSE structure. /// /// This is useful for cases where the signer needs to handle the COSE structure directly. @@ -92,70 +72,9 @@ use async_trait::async_trait; /// This trait exists to allow the signature mechanism to be extended. /// /// Use this when the implementation is asynchronous. -#[cfg(not(target_arch = "wasm32"))] -#[async_trait] -pub trait AsyncSigner: Sync + AsyncTimeStampProvider { - /// Returns a new byte array which is a signature over the original. - async fn sign(&self, data: Vec) -> Result>; - - /// Returns the algorithm of the Signer. - fn alg(&self) -> SigningAlg; - - /// Returns the certificates as a Vec containing a Vec of DER bytes for each certificate. - fn certs(&self) -> Result>>; - - /// Returns the size in bytes of the largest possible expected signature. - /// Signing will fail if the result of the `sign` function is larger - /// than this value. - fn reserve_size(&self) -> usize; - - /// 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 - /// be cached taking pressure off of the CA (recommended by C2PA spec) - async fn ocsp_val(&self) -> Option> { - None - } - - /// If this returns true the sign function is responsible for for direct handling of the COSE structure. - /// - /// This is useful for cases where the signer needs to handle the COSE structure directly. - /// Not recommended for general use. - fn direct_cose_handling(&self) -> bool { - false - } - - /// Returns a list of dynamic assertions that should be included in the manifest. - fn dynamic_assertions(&self) -> Vec> { - Vec::new() - } -} - -#[cfg(target_arch = "wasm32")] -#[async_trait(?Send)] -pub trait AsyncSigner: AsyncTimeStampProvider { - /// Returns a new byte array which is a signature over the original. - async fn sign(&self, data: Vec) -> Result>; - - /// Returns the algorithm of the Signer. - fn alg(&self) -> SigningAlg; - - /// Returns the certificates as a Vec containing a Vec of DER bytes for each certificate. - fn certs(&self) -> Result>>; - - /// Returns the size in bytes of the largest possible expected signature. - /// Signing will fail if the result of the `sign` function is larger - /// than this value. - fn reserve_size(&self) -> usize; - - /// 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 - /// be cached taking pressure off of the CA (recommended by C2PA spec) - async fn ocsp_val(&self) -> Option> { - None - } - +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait AsyncSigner: Sync + AsyncRawSigner + AsyncTimeStampProvider { /// If this returns true the sign function is responsible for for direct handling of the COSE structure. /// /// This is useful for cases where the signer needs to handle the COSE structure directly. @@ -188,7 +107,17 @@ pub trait RemoteSigner: Sync { } impl Signer for Box { - fn sign(&self, data: &[u8]) -> Result> { + fn direct_cose_handling(&self) -> bool { + (**self).direct_cose_handling() + } + + fn dynamic_assertions(&self) -> Vec> { + (**self).dynamic_assertions() + } +} + +impl RawSigner for Box { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { (**self).sign(data) } @@ -196,8 +125,8 @@ impl Signer for Box { (**self).alg() } - fn certs(&self) -> Result>> { - (**self).certs() + fn cert_chain(&self) -> Result>, RawSignerError> { + (**self).cert_chain() } fn reserve_size(&self) -> usize { @@ -207,14 +136,6 @@ impl Signer for Box { fn ocsp_val(&self) -> Option> { (**self).ocsp_val() } - - fn direct_cose_handling(&self) -> bool { - (**self).direct_cose_handling() - } - - fn dynamic_assertions(&self) -> Vec> { - (**self).dynamic_assertions() - } } impl TimeStampProvider for Box { From 34ff5f4508c079ce1a371c515127fc1586b2b773 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Thu, 5 Dec 2024 18:39:17 -0800 Subject: [PATCH 06/32] Start to build in the create_signer interface --- internal/crypto/src/openssl/mod.rs | 1 + internal/crypto/src/openssl/signers/mod.rs | 37 +++++++++++ internal/crypto/src/raw_signature/mod.rs | 4 +- internal/crypto/src/raw_signature/signer.rs | 68 +++++++++++++++++---- sdk/src/signer.rs | 3 +- 5 files changed, 97 insertions(+), 16 deletions(-) create mode 100644 internal/crypto/src/openssl/signers/mod.rs diff --git a/internal/crypto/src/openssl/mod.rs b/internal/crypto/src/openssl/mod.rs index 2b0ad9437..13af016a7 100644 --- a/internal/crypto/src/openssl/mod.rs +++ b/internal/crypto/src/openssl/mod.rs @@ -21,4 +21,5 @@ mod ffi_mutex; pub use ffi_mutex::{OpenSslMutex, OpenSslMutexUnavailable}; +pub mod signers; pub mod validators; diff --git a/internal/crypto/src/openssl/signers/mod.rs b/internal/crypto/src/openssl/signers/mod.rs new file mode 100644 index 000000000..21aaca338 --- /dev/null +++ b/internal/crypto/src/openssl/signers/mod.rs @@ -0,0 +1,37 @@ +// Copyright 2024 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. + +//! This module binds OpenSSL logic for generating raw signatures to this +//! crate's [`RawSigner`] trait. + +use crate::{ + raw_signature::{RawSigner, RawSignerError}, + SigningAlg, +}; + +/// Return a built-in [`RawSigner`] instance using the provided signing +/// certificate and private key. +/// +/// Which signers are available may vary depending on the platform and which +/// crate features were enabled. +/// +/// Returns `None` if the signing algorithm is unsupported. May return an `Err` +/// response if the certificate chain or private key are invalid. +pub fn signer_from_cert_chain_and_private_key( + _cert_chain: &[u8], + _private_key: &[u8], + _alg: SigningAlg, +) -> Option, RawSignerError>> { + // TEMPORARY: None implemented yet. + None +} diff --git a/internal/crypto/src/raw_signature/mod.rs b/internal/crypto/src/raw_signature/mod.rs index b6a8c37e9..7381e906f 100644 --- a/internal/crypto/src/raw_signature/mod.rs +++ b/internal/crypto/src/raw_signature/mod.rs @@ -14,7 +14,9 @@ //! Tools for working with raw signature algorithms. pub(crate) mod signer; -pub use signer::{AsyncRawSigner, RawSigner, RawSignerError}; +pub use signer::{ + signer_from_cert_chain_and_private_key, AsyncRawSigner, RawSigner, RawSignerError, +}; pub(crate) mod oids; diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index 4bd89957b..3bc3aa89d 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -14,13 +14,16 @@ use async_trait::async_trait; use thiserror::Error; -use crate::SigningAlg; +use crate::{ + time_stamp::{AsyncTimeStampProvider, TimeStampProvider}, + SigningAlg, +}; /// Implementations of the `RawSigner` trait generate a cryptographic signature /// over an arbitrary byte array. /// /// If an implementation _can_ be asynchronous, that is preferred. -pub trait RawSigner { +pub trait RawSigner: TimeStampProvider { /// Return a raw signature over the original byte slice. fn sign(&self, data: &[u8]) -> Result, RawSignerError>; @@ -56,7 +59,7 @@ pub trait RawSigner { /// Use this trait only when the implementation must be asynchronous. #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -pub trait AsyncRawSigner: Sync { +pub trait AsyncRawSigner: Sync + AsyncTimeStampProvider { /// Return a raw signature over the original byte slice. async fn sign(&self, data: Vec) -> Result, RawSignerError>; @@ -148,22 +151,61 @@ impl From for RawSignerError { /// configured from a private/public key pair. #[allow(dead_code)] // TEMPORARY while refactoring pub(crate) trait ConfigurableSigner: RawSigner + Sized { - fn from_signcert_and_pkey( - signcert: &[u8], - pkey: &[u8], + fn from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], alg: SigningAlg, - tsa_url: Option, + time_stamp_service_url: Option, ) -> Result; fn from_files>( - signcert_path: P, - pkey_path: P, + cert_chain_path: P, + private_key_path: P, alg: SigningAlg, - tsa_url: Option, + time_stamp_service_url: Option, ) -> Result { - let signcert = std::fs::read(signcert_path)?; - let pkey = std::fs::read(pkey_path)?; + let cert_chain = std::fs::read(cert_chain_path)?; + let private_key = std::fs::read(private_key_path)?; + + Self::from_cert_chain_and_private_key( + &cert_chain, + &private_key, + alg, + time_stamp_service_url, + ) + } +} - Self::from_signcert_and_pkey(&signcert, &pkey, alg, tsa_url) +/// Return a built-in [`RawSigner`] instance using the provided signing +/// certificate and private key. +/// +/// Which signers are available may vary depending on the platform and which +/// crate features were enabled. +/// +/// Returns `None` if the signing algorithm is unsupported. May return an `Err` +/// response if the certificate chain or private key are invalid. +#[allow(unused)] // arguments may or may not be used depending on crate features +pub fn signer_from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + alg: SigningAlg, +) -> Option, RawSignerError>> { + #[cfg(feature = "openssl")] + if let Some(signer) = crate::openssl::signers::signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + alg, + ) { + return Some(signer); } + + // TO DO: Do we need this for WASM or is it all async? + // #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] + // if let Some(validator) = + // crate::webcrypto::validators::validator_for_sig_and_hash_algs(sig_alg, + // hash_alg) { + // return Some(validator); + // } + + None } diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index d86fb39f0..35b257360 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -16,7 +16,6 @@ use c2pa_crypto::{ time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider}, SigningAlg, }; -use x509_certificate::Sign; use crate::{DynamicAssertion, Result}; /// The `Signer` trait generates a cryptographic signature over a byte array. @@ -133,7 +132,7 @@ impl RawSigner for Box { (**self).reserve_size() } - fn ocsp_val(&self) -> Option> { + fn ocsp_response(&self) -> Option> { (**self).ocsp_val() } } From 8cdb210d2accb599078ec2a192ac0c9bb3380d10 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Thu, 5 Dec 2024 19:41:50 -0800 Subject: [PATCH 07/32] Update all the call sites for Signer -> RawSigner transition --- internal/crypto/src/raw_signature/signer.rs | 4 + sdk/src/callback_signer.rs | 115 +++++++++++--------- sdk/src/cose_sign.rs | 29 +++-- sdk/src/cose_validator.rs | 14 ++- sdk/src/error.rs | 3 + sdk/src/openssl/ec_signer.rs | 34 +++--- sdk/src/openssl/ed_signer.rs | 25 +++-- sdk/src/openssl/openssl_trust_handler.rs | 63 +++++------ sdk/src/openssl/rsa_signer.rs | 44 ++++---- sdk/src/openssl/temp_signer_async.rs | 19 ++-- sdk/src/signer.rs | 11 +- sdk/src/store.rs | 46 +++++--- sdk/src/utils/sig_utils.rs | 23 ++-- sdk/src/utils/test.rs | 49 ++++++--- sdk/tests/common/test_signer.rs | 13 ++- sdk/tests/v2_api_integration.rs | 14 +-- 16 files changed, 286 insertions(+), 220 deletions(-) diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index 3bc3aa89d..9d61f3be3 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -93,6 +93,10 @@ pub trait AsyncRawSigner: Sync + AsyncTimeStampProvider { #[derive(Debug, Eq, Error, PartialEq)] #[non_exhaustive] pub enum RawSignerError { + /// The signing credentials are invalid. + #[error("invalid signing credentials ({0})")] + InvalidSigningCredentials(String), + /// An I/O error occurred. This typically happens when loading /// public/private key material from files. /// diff --git a/sdk/src/callback_signer.rs b/sdk/src/callback_signer.rs index 92b804f19..eb0fd32c6 100644 --- a/sdk/src/callback_signer.rs +++ b/sdk/src/callback_signer.rs @@ -17,18 +17,19 @@ //! using a callback and public signing certificates. use c2pa_crypto::{ - raw_signature::{AsyncRawSigner, RawSigner, RawSignerError}, + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, SigningAlg, }; -use crate::{error::Error, AsyncSigner, Signer}; +use crate::Signer; /// Defines a callback function interface for a [`CallbackSigner`]. /// /// The callback should return a signature for the given data. /// The callback should return an error if the data cannot be signed. -pub type CallbackFunc = - dyn Fn(*const (), &[u8]) -> std::result::Result, Error> + Send + Sync; +pub type CallbackFunc = dyn Fn(*const (), &[u8]) -> Result, RawSignerError> + Send + Sync; +// TO REVIEW: Changing the error type to RawSignerError /// Defines a signer that uses a callback to sign data. /// @@ -60,11 +61,12 @@ impl CallbackSigner { /// Create a new callback signer. pub fn new(callback: F, alg: SigningAlg, certs: T) -> Self where - F: Fn(*const (), &[u8]) -> std::result::Result, Error> + Send + Sync + 'static, + F: Fn(*const (), &[u8]) -> Result, RawSignerError> + Send + Sync + 'static, T: Into>, { let certs = certs.into(); let reserve_size = 10000 + certs.len(); + Self { context: std::ptr::null(), callback: Box::new(callback), @@ -107,19 +109,21 @@ impl CallbackSigner { /// |_context: *const _, data: &[u8]| CallbackSigner::ed25519_sign(data, PRIVATE_KEY); /// let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS); /// ``` - pub fn ed25519_sign(data: &[u8], private_key: &[u8]) -> Result> { + pub fn ed25519_sign(data: &[u8], private_key: &[u8]) -> Result, RawSignerError> { use ed25519_dalek::{Signature, Signer, SigningKey}; use pem::parse; // Parse the PEM data to get the private key - let pem = parse(private_key).map_err(|e| Error::OtherError(Box::new(e)))?; + let pem = parse(private_key) + .map_err(|e| RawSignerError::InvalidSigningCredentials(e.to_string()))?; + // For Ed25519, the key is 32 bytes long, so we skip the first 16 bytes of the PEM data let key_bytes = &pem.contents()[16..]; - let signing_key = - SigningKey::try_from(key_bytes).map_err(|e| Error::OtherError(Box::new(e)))?; + let signing_key = SigningKey::try_from(key_bytes) + .map_err(|e| RawSignerError::InvalidSigningCredentials(e.to_string()))?; + // Sign the data let signature: Signature = signing_key.sign(data); - Ok(signature.to_bytes().to_vec()) } } @@ -129,7 +133,11 @@ impl Default for CallbackSigner { fn default() -> Self { Self { context: std::ptr::null(), - callback: Box::new(|_, _| Err(Error::UnsupportedType)), + callback: Box::new(|_, _| { + Err(RawSignerError::InternalError( + "no callback provided".to_string(), + )) + }), alg: SigningAlg::Es256, certs: Vec::new(), reserve_size: 10000, @@ -141,7 +149,7 @@ impl Default for CallbackSigner { impl Signer for CallbackSigner {} impl RawSigner for CallbackSigner { - fn sign(&self, data: &[u8]) -> std::result::Result, RawSignerError> { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { (self.callback)(self.context, data) } @@ -149,7 +157,7 @@ impl RawSigner for CallbackSigner { self.alg } - fn cert_chain(&self) -> std::result::Result>, RawSignerError> { + fn cert_chain(&self) -> Result>, RawSignerError> { let pems = pem::parse_many(&self.certs) .map_err(|e| RawSignerError::InternalError(e.to_string()))?; @@ -167,44 +175,43 @@ impl TimeStampProvider for CallbackSigner { } } -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)] -// I'm not sure if this is useful since the callback is still synchronous. -impl AsyncSigner for CallbackSigner {} - -impl AsyncRawSigner for CallbackSigner { - async fn sign(&self, data: Vec) -> std::result::Result, RawSignerError> { - (self.callback)(self.context, &data) - } - - fn alg(&self) -> SigningAlg { - self.alg - } - - fn cert_chain(&self) -> std::result::Result>, RawSignerError> { - let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?; - Ok(pems.into_iter().map(|p| p.into_contents()).collect()) - } - - 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 { - self.tsa_url.clone() - } - - #[cfg(target_arch = "wasm32")] - async fn send_time_stamp_request( - &self, - _message: &[u8], - ) -> Option, c2pa_crypto::time_stamp::TimeStampError>> { - None - } -} +// TO REVIEW WITH GAVIN: Can we remove this impl? I don't see how it's useful and it's causing build errors with the new AsyncRawSigner trait. + +// #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +// #[cfg_attr(not(target_arch = "wasm32"), async_trait)] +// // I'm not sure if this is useful since the callback is still synchronous. +// impl AsyncSigner for CallbackSigner {} + +// impl AsyncRawSigner for CallbackSigner { +// async fn sign(&self, data: Vec) -> Result, RawSignerError> { +// (self.callback)(self.context, &data) +// } + +// fn alg(&self) -> SigningAlg { +// self.alg +// } + +// fn cert_chain(&self) -> Result>, RawSignerError> { +// let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?; +// Ok(pems.into_iter().map(|p| p.into_contents()).collect()) +// } + +// 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 { +// self.tsa_url.clone() +// } + +// #[cfg(target_arch = "wasm32")] +// async fn send_time_stamp_request( +// &self, +// _message: &[u8], +// ) -> Option, c2pa_crypto::time_stamp::TimeStampError>> { +// None +// } +// } diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 7ceb02f5c..ff1f102aa 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -145,7 +145,7 @@ pub(crate) fn cose_sign(signer: &dyn Signer, data: &[u8], box_size: usize) -> Re */ // make sure the signing cert is valid - let certs = signer.certs()?; + let certs = signer.cert_chain()?; if let Some(signing_cert) = certs.first() { signing_cert_valid(signing_cert)?; } else { @@ -218,12 +218,12 @@ fn build_headers(signer: &dyn Signer, data: &[u8], alg: SigningAlg) -> Result<(H SigningAlg::Ed25519 => HeaderBuilder::new().algorithm(iana::Algorithm::EdDSA), }; - let certs = signer.certs()?; + let certs = signer.cert_chain()?; let ocsp_val = if _sync { - signer.ocsp_val() + signer.ocsp_response() } else { - signer.ocsp_val().await + signer.ocsp_response().await }; let sc_der_array_or_bytes = match certs.len() { @@ -358,10 +358,13 @@ 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 c2pa_crypto::{ + raw_signature::{RawSigner, RawSignerError}, + time_stamp::{TimeStampError, TimeStampProvider}, + }; use super::sign_claim; - use crate::{claim::Claim, utils::test::temp_signer}; + use crate::{claim::Claim, utils::test::temp_signer, Signer}; #[test] fn test_sign_claim() { @@ -382,9 +385,9 @@ mod tests { #[cfg(feature = "openssl")] #[actix::test] async fn test_sign_claim_async() { - use crate::{ - cose_sign::sign_claim_async, openssl::AsyncSignerAdapter, AsyncSigner, SigningAlg, - }; + use c2pa_crypto::raw_signature::AsyncRawSigner; + + use crate::{cose_sign::sign_claim_async, openssl::AsyncSignerAdapter, SigningAlg}; let mut claim = Claim::new("extern_sign_test", Some("contentauth")); claim.build().unwrap(); @@ -409,8 +412,10 @@ mod tests { } } - impl crate::Signer for BogusSigner { - fn sign(&self, _data: &[u8]) -> crate::error::Result> { + impl Signer for BogusSigner {} + + impl RawSigner for BogusSigner { + fn sign(&self, _data: &[u8]) -> Result, RawSignerError> { eprintln!("Canary, canary, please cause this deploy to fail!"); Ok(b"totally bogus signature".to_vec()) } @@ -419,7 +424,7 @@ mod tests { c2pa_crypto::SigningAlg::Ps256 } - fn certs(&self) -> crate::error::Result>> { + fn cert_chain(&self) -> Result>, RawSignerError> { let cert_vec: Vec = Vec::new(); let certs = vec![cert_vec]; Ok(certs) diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index b746d7827..3b3a7ac11 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -1457,6 +1457,8 @@ pub mod tests { #[test] #[cfg(feature = "openssl_sign")] fn test_stapled_ocsp() { + use c2pa_crypto::raw_signature::{RawSigner, RawSignerError}; + let mut validation_log = DetailedStatusTracker::default(); let mut claim = crate::claim::Claim::new("ocsp_sign_test", Some("contentauth")); @@ -1482,8 +1484,10 @@ pub mod tests { pub ocsp_rsp: Vec, } - impl crate::Signer for OcspSigner { - fn sign(&self, data: &[u8]) -> Result> { + impl Signer for OcspSigner {} + + impl RawSigner for OcspSigner { + fn sign(&self, data: &[u8]) -> std::result::Result, RawSignerError> { self.signer.sign(data) } @@ -1491,15 +1495,15 @@ pub mod tests { SigningAlg::Ps256 } - fn certs(&self) -> Result>> { - self.signer.certs() + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { + self.signer.cert_chain() } fn reserve_size(&self) -> usize { self.signer.reserve_size() } - fn ocsp_val(&self) -> Option> { + fn ocsp_response(&self) -> Option> { Some(self.ocsp_rsp.clone()) } } diff --git a/sdk/src/error.rs b/sdk/src/error.rs index d4ee87532..2e2f54b89 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -306,6 +306,9 @@ pub enum Error { #[error(transparent)] RawSignatureValidationError(#[from] c2pa_crypto::raw_signature::RawSignatureValidationError), + + #[error(transparent)] + RawSignerError(#[from] c2pa_crypto::raw_signature::RawSignerError), } /// A specialized `Result` type for C2PA toolkit operations. diff --git a/sdk/src/openssl/ec_signer.rs b/sdk/src/openssl/ec_signer.rs index 99908c520..b74597f01 100644 --- a/sdk/src/openssl/ec_signer.rs +++ b/sdk/src/openssl/ec_signer.rs @@ -11,7 +11,12 @@ // specific language governing permissions and limitations under // each license. -use c2pa_crypto::{openssl::OpenSslMutex, time_stamp::TimeStampProvider, SigningAlg}; +use c2pa_crypto::{ + openssl::OpenSslMutex, + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, + SigningAlg, +}; use openssl::{ ec::EcKey, hash::MessageDigest, @@ -20,12 +25,7 @@ use openssl::{ }; use super::check_chain_order; -use crate::{ - error::{Error, Result}, - signer::ConfigurableSigner, - utils::sig_utils::der_to_p1363, - Signer, -}; +use crate::{error::Error, signer::ConfigurableSigner, utils::sig_utils::der_to_p1363, Signer}; /// Implements `Signer` trait using OpenSSL's implementation of /// ECDSA encryption. @@ -46,7 +46,7 @@ impl ConfigurableSigner for EcSigner { pkey: &[u8], alg: SigningAlg, tsa_url: Option, - ) -> Result { + ) -> crate::Result { let _openssl = OpenSslMutex::acquire()?; let certs_size = signcert.len(); @@ -71,22 +71,24 @@ impl ConfigurableSigner for EcSigner { } } -impl Signer for EcSigner { - fn sign(&self, data: &[u8]) -> Result> { +impl Signer for EcSigner {} + +impl RawSigner for EcSigner { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { let _openssl = OpenSslMutex::acquire()?; - let key = PKey::from_ec_key(self.pkey.clone()).map_err(Error::OpenSslError)?; + let key = PKey::from_ec_key(self.pkey.clone())?; let mut signer = match self.alg { SigningAlg::Es256 => openssl::sign::Signer::new(MessageDigest::sha256(), &key)?, SigningAlg::Es384 => openssl::sign::Signer::new(MessageDigest::sha384(), &key)?, SigningAlg::Es512 => openssl::sign::Signer::new(MessageDigest::sha512(), &key)?, - _ => return Err(Error::UnsupportedType), + _ => panic!("Shouldn't happen"), }; - signer.update(data).map_err(Error::OpenSslError)?; - let der_sig = signer.sign_to_vec().map_err(Error::OpenSslError)?; + signer.update(data)?; + let der_sig = signer.sign_to_vec()?; der_to_p1363(&der_sig, self.alg) } @@ -94,13 +96,13 @@ impl Signer for EcSigner { self.alg } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> Result>, RawSignerError> { let _openssl = OpenSslMutex::acquire()?; let mut certs: Vec> = Vec::new(); for c in &self.signcerts { - let cert = c.to_der().map_err(Error::OpenSslError)?; + let cert = c.to_der()?; certs.push(cert); } diff --git a/sdk/src/openssl/ed_signer.rs b/sdk/src/openssl/ed_signer.rs index cceb38d34..90e9e03cc 100644 --- a/sdk/src/openssl/ed_signer.rs +++ b/sdk/src/openssl/ed_signer.rs @@ -11,14 +11,19 @@ // specific language governing permissions and limitations under // each license. -use c2pa_crypto::{openssl::OpenSslMutex, time_stamp::TimeStampProvider, SigningAlg}; +use c2pa_crypto::{ + openssl::OpenSslMutex, + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, + SigningAlg, +}; use openssl::{ pkey::{PKey, Private}, x509::X509, }; use super::check_chain_order; -use crate::{signer::ConfigurableSigner, Error, Result, Signer}; +use crate::{signer::ConfigurableSigner, Error, Signer}; /// Implements `Signer` trait using OpenSSL's implementation of /// Edwards Curve encryption. @@ -39,7 +44,7 @@ impl ConfigurableSigner for EdSigner { pkey: &[u8], alg: SigningAlg, tsa_url: Option, - ) -> Result { + ) -> crate::Result { let _openssl = OpenSslMutex::acquire()?; let certs_size = signcert.len(); @@ -68,15 +73,15 @@ impl ConfigurableSigner for EdSigner { } } -impl Signer for EdSigner { - fn sign(&self, data: &[u8]) -> Result> { +impl Signer for EdSigner {} + +impl RawSigner for EdSigner { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { let _openssl = OpenSslMutex::acquire()?; - let mut signer = - openssl::sign::Signer::new_without_digest(&self.pkey).map_err(Error::OpenSslError)?; + let mut signer = openssl::sign::Signer::new_without_digest(&self.pkey)?; let signed_data = signer.sign_oneshot_to_vec(data)?; - Ok(signed_data) } @@ -84,13 +89,13 @@ impl Signer for EdSigner { self.alg } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> Result>, RawSignerError> { let _openssl = OpenSslMutex::acquire()?; let mut certs: Vec> = Vec::new(); for c in &self.signcerts { - let cert = c.to_der().map_err(Error::OpenSslError)?; + let cert = c.to_der()?; certs.push(cert); } diff --git a/sdk/src/openssl/openssl_trust_handler.rs b/sdk/src/openssl/openssl_trust_handler.rs index 9d5ae56b9..fc3dbef89 100644 --- a/sdk/src/openssl/openssl_trust_handler.rs +++ b/sdk/src/openssl/openssl_trust_handler.rs @@ -299,13 +299,10 @@ pub mod tests { #![allow(clippy::panic)] #![allow(clippy::unwrap_used)] - use c2pa_crypto::SigningAlg; + use c2pa_crypto::{raw_signature::RawSigner, SigningAlg}; use super::*; - use crate::{ - openssl::temp_signer::{self}, - Signer, - }; + use crate::openssl::temp_signer::{self}; #[test] fn test_trust_store() { @@ -325,13 +322,13 @@ pub mod tests { let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); - let ps256_certs = ps256.certs().unwrap(); - let ps384_certs = ps384.certs().unwrap(); - let ps512_certs = ps512.certs().unwrap(); - let es256_certs = es256.certs().unwrap(); - let es384_certs = es384.certs().unwrap(); - let es512_certs = es512.certs().unwrap(); - let ed25519_certs = ed25519.certs().unwrap(); + let ps256_certs = ps256.cert_chain().unwrap(); + let ps384_certs = ps384.cert_chain().unwrap(); + let ps512_certs = ps512.cert_chain().unwrap(); + let es256_certs = es256.cert_chain().unwrap(); + let es384_certs = es384.cert_chain().unwrap(); + let es512_certs = es512.cert_chain().unwrap(); + let ed25519_certs = ed25519.cert_chain().unwrap(); assert!(verify_trust(&th, &ps256_certs[1..], &ps256_certs[0], None).unwrap()); assert!(verify_trust(&th, &ps384_certs[1..], &ps384_certs[0], None).unwrap()); @@ -363,13 +360,13 @@ pub mod tests { let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); - let ps256_certs = ps256.certs().unwrap(); - let ps384_certs = ps384.certs().unwrap(); - let ps512_certs = ps512.certs().unwrap(); - let es256_certs = es256.certs().unwrap(); - let es384_certs = es384.certs().unwrap(); - let es512_certs = es512.certs().unwrap(); - let ed25519_certs = ed25519.certs().unwrap(); + let ps256_certs = ps256.cert_chain().unwrap(); + let ps384_certs = ps384.cert_chain().unwrap(); + let ps512_certs = ps512.cert_chain().unwrap(); + let es256_certs = es256.cert_chain().unwrap(); + let es384_certs = es384.cert_chain().unwrap(); + let es512_certs = es512.cert_chain().unwrap(); + let ed25519_certs = ed25519.cert_chain().unwrap(); assert!(!verify_trust(&th, &ps256_certs[2..], &ps256_certs[0], None).unwrap()); assert!(!verify_trust(&th, &ps384_certs[2..], &ps384_certs[0], None).unwrap()); @@ -405,13 +402,13 @@ pub mod tests { let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); - let ps256_certs = ps256.certs().unwrap(); - let ps384_certs = ps384.certs().unwrap(); - let ps512_certs = ps512.certs().unwrap(); - let es256_certs = es256.certs().unwrap(); - let es384_certs = es384.certs().unwrap(); - let es512_certs = es512.certs().unwrap(); - let ed25519_certs = ed25519.certs().unwrap(); + let ps256_certs = ps256.cert_chain().unwrap(); + let ps384_certs = ps384.cert_chain().unwrap(); + let ps512_certs = ps512.cert_chain().unwrap(); + let es256_certs = es256.cert_chain().unwrap(); + let es384_certs = es384.cert_chain().unwrap(); + let es512_certs = es512.cert_chain().unwrap(); + let ed25519_certs = ed25519.cert_chain().unwrap(); assert!(verify_trust(&th, &ps256_certs[1..], &ps256_certs[0], None).unwrap()); assert!(verify_trust(&th, &ps384_certs[1..], &ps384_certs[0], None).unwrap()); @@ -446,13 +443,13 @@ pub mod tests { let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); - let ps256_certs = ps256.certs().unwrap(); - let ps384_certs = ps384.certs().unwrap(); - let ps512_certs = ps512.certs().unwrap(); - let es256_certs = es256.certs().unwrap(); - let es384_certs = es384.certs().unwrap(); - let es512_certs = es512.certs().unwrap(); - let ed25519_certs = ed25519.certs().unwrap(); + let ps256_certs = ps256.cert_chain().unwrap(); + let ps384_certs = ps384.cert_chain().unwrap(); + let ps512_certs = ps512.cert_chain().unwrap(); + let es256_certs = es256.cert_chain().unwrap(); + let es384_certs = es384.cert_chain().unwrap(); + let es512_certs = es512.cert_chain().unwrap(); + let ed25519_certs = ed25519.cert_chain().unwrap(); assert!(verify_trust(&th, &ps256_certs[1..], &ps256_certs[0], None).unwrap()); assert!(verify_trust(&th, &ps384_certs[1..], &ps384_certs[0], None).unwrap()); diff --git a/sdk/src/openssl/rsa_signer.rs b/sdk/src/openssl/rsa_signer.rs index ce19a5004..3c5bebfa1 100644 --- a/sdk/src/openssl/rsa_signer.rs +++ b/sdk/src/openssl/rsa_signer.rs @@ -14,7 +14,11 @@ use std::cell::Cell; use c2pa_crypto::{ - ocsp::OcspResponse, openssl::OpenSslMutex, time_stamp::TimeStampProvider, SigningAlg, + ocsp::OcspResponse, + openssl::OpenSslMutex, + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, + SigningAlg, }; use openssl::{ hash::MessageDigest, @@ -24,7 +28,7 @@ use openssl::{ }; use super::check_chain_order; -use crate::{signer::ConfigurableSigner, Error, Result, Signer}; +use crate::{signer::ConfigurableSigner, Error, Signer}; /// Implements `Signer` trait using OpenSSL's implementation of /// SHA256 + RSA encryption. @@ -76,7 +80,7 @@ impl RsaSigner { } } - fn certs_internal(&self) -> Result>> { + fn certs_internal(&self) -> Result>, RawSignerError> { // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please // don't make this pub or pub(crate) without finding a way to ensure that // precondition. @@ -84,7 +88,7 @@ impl RsaSigner { let mut certs: Vec> = Vec::new(); for c in &self.signcerts { - let cert = c.to_der().map_err(wrap_openssl_err)?; + let cert = c.to_der()?; certs.push(cert); } @@ -98,7 +102,7 @@ impl ConfigurableSigner for RsaSigner { pkey: &[u8], alg: SigningAlg, tsa_url: Option, - ) -> Result { + ) -> crate::Result { let _openssl = OpenSslMutex::acquire()?; let signcerts = X509::stack_from_pem(signcert).map_err(wrap_openssl_err)?; @@ -162,43 +166,39 @@ impl ConfigurableSigner for RsaSigner { } } -impl Signer for RsaSigner { - fn sign(&self, data: &[u8]) -> Result> { +impl Signer for RsaSigner {} + +impl RawSigner for RsaSigner { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { let mut signer = match self.alg { SigningAlg::Ps256 => { - let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey) - .map_err(wrap_openssl_err)?; + let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey)?; signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding signer.set_rsa_mgf1_md(MessageDigest::sha256())?; signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; signer } + SigningAlg::Ps384 => { - let mut signer = openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey) - .map_err(wrap_openssl_err)?; + let mut signer = openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey)?; signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding signer.set_rsa_mgf1_md(MessageDigest::sha384())?; signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; signer } + SigningAlg::Ps512 => { - let mut signer = openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey) - .map_err(wrap_openssl_err)?; + let mut signer = openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey)?; signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding signer.set_rsa_mgf1_md(MessageDigest::sha512())?; signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; signer } - // "rs256" => openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey) - // .map_err(wrap_openssl_err)?, - // "rs384" => openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey) - // .map_err(wrap_openssl_err)?, - // "rs512" => openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey) - // .map_err(wrap_openssl_err)?, - _ => return Err(Error::UnsupportedType), + + _ => unreachable!(), }; let signed_data = signer.sign_oneshot_to_vec(data)?; @@ -212,7 +212,7 @@ impl Signer for RsaSigner { 1024 + self.certs_size + self.timestamp_size + self.ocsp_size.get() // the Cose_Sign1 contains complete certs, timestamps and ocsp so account for size } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> Result>, RawSignerError> { let _openssl = OpenSslMutex::acquire()?; self.certs_internal() } @@ -221,7 +221,7 @@ impl Signer for RsaSigner { self.alg } - fn ocsp_val(&self) -> Option> { + fn ocsp_response(&self) -> Option> { let _openssl = OpenSslMutex::acquire().ok()?; // update OCSP if needed diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs index e11bdde23..f342ac8b7 100644 --- a/sdk/src/openssl/temp_signer_async.rs +++ b/sdk/src/openssl/temp_signer_async.rs @@ -19,9 +19,12 @@ //! the asynchronous signing of claims. //! This module should be used only for testing purposes. +use c2pa_crypto::raw_signature::{AsyncRawSigner, RawSignerError}; #[cfg(feature = "openssl_sign")] use c2pa_crypto::SigningAlg; +use crate::AsyncSigner; + #[cfg(feature = "openssl_sign")] fn get_local_signer(alg: SigningAlg) -> Box { let cert_dir = crate::utils::test::fixture_path("certs"); @@ -58,19 +61,21 @@ impl AsyncSignerAdapter { AsyncSignerAdapter { alg, - certs: signer.certs().unwrap_or_default(), + certs: signer.cert_chain().unwrap_or_default(), reserve_size: signer.reserve_size(), tsa_url: signer.time_stamp_service_url(), - ocsp_val: signer.ocsp_val(), + ocsp_val: signer.ocsp_response(), } } } #[cfg(test)] -#[cfg(feature = "openssl_sign")] +impl AsyncSigner for AsyncSignerAdapter {} + +#[cfg(test)] #[async_trait::async_trait] -impl crate::AsyncSigner for AsyncSignerAdapter { - async fn sign(&self, data: Vec) -> crate::error::Result> { +impl AsyncRawSigner for AsyncSignerAdapter { + async fn sign(&self, data: Vec) -> Result, RawSignerError> { let signer = get_local_signer(self.alg); signer.sign(&data) } @@ -79,7 +84,7 @@ impl crate::AsyncSigner for AsyncSignerAdapter { self.alg } - fn certs(&self) -> crate::Result>> { + fn cert_chain(&self) -> Result>, RawSignerError> { let mut output: Vec> = Vec::new(); for v in &self.certs { output.push(v.clone()); @@ -91,7 +96,7 @@ impl crate::AsyncSigner for AsyncSignerAdapter { self.reserve_size } - async fn ocsp_val(&self) -> Option> { + async fn ocsp_response(&self) -> Option> { self.ocsp_val.clone() } } diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 35b257360..1b5ad5451 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -17,7 +17,8 @@ use c2pa_crypto::{ SigningAlg, }; -use crate::{DynamicAssertion, Result}; +use crate::DynamicAssertion; + /// The `Signer` trait generates a cryptographic signature over a byte array. /// /// This trait exists to allow the signature mechanism to be extended. @@ -46,7 +47,7 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { pkey_path: P, alg: SigningAlg, tsa_url: Option, - ) -> Result { + ) -> crate::Result { use crate::Error; let signcert = std::fs::read(signcert_path).map_err(Error::IoError)?; @@ -61,7 +62,7 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { pkey: &[u8], alg: SigningAlg, tsa_url: Option, - ) -> Result; + ) -> crate::Result; } use async_trait::async_trait; @@ -96,7 +97,7 @@ pub trait RemoteSigner: Sync { /// The size of returned `Vec` must match the value returned by `reserve_size`. /// This data will be embedded in the JUMBF `c2pa.signature` box of the manifest. /// `data` are the bytes of the claim to be remotely signed. - async fn sign_remote(&self, data: &[u8]) -> Result>; + async fn sign_remote(&self, data: &[u8]) -> crate::Result>; /// Returns the size in bytes of the largest possible expected signature. /// @@ -133,7 +134,7 @@ impl RawSigner for Box { } fn ocsp_response(&self) -> Option> { - (**self).ocsp_val() + (**self).ocsp_response() } } diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 1d6260d8b..b1639006e 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -503,14 +503,15 @@ impl Store { let result = if _sync { if signer.direct_cose_handling() { // 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) + return Ok(signer.sign(&claim_bytes)?); // do not verify remote signers (we never did) } else { cose_sign(signer, &claim_bytes, box_size) } } else { if signer.direct_cose_handling() { // Let the signer do all the COSE processing and return the structured COSE data. - return signer.sign(claim_bytes.clone()).await; // do not verify remote signers (we never did) + return signer.sign(claim_bytes.clone()).await.map_err(|e| e.into()); + // do not verify remote signers (we never did) } else { cose_sign_async(signer, &claim_bytes, box_size).await } @@ -3626,7 +3627,10 @@ pub mod tests { use std::io::Write; - use c2pa_crypto::SigningAlg; + use c2pa_crypto::{ + raw_signature::{RawSigner, RawSignerError}, + SigningAlg, + }; use c2pa_status_tracker::StatusTracker; use memchr::memmem; use serde::Serialize; @@ -3850,8 +3854,10 @@ pub mod tests { struct BadSigner {} - impl crate::Signer for BadSigner { - fn sign(&self, _data: &[u8]) -> Result> { + impl Signer for BadSigner {} + + impl RawSigner for BadSigner { + fn sign(&self, _data: &[u8]) -> std::result::Result, RawSignerError> { Ok(b"not a valid signature".to_vec()) } @@ -3859,7 +3865,7 @@ pub mod tests { SigningAlg::Ps256 } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { Ok(Vec::new()) } @@ -5599,8 +5605,9 @@ pub mod tests { #[cfg(feature = "file_io")] async fn test_datahash_embeddable_manifest_async() { // test adding to actual image - use std::io::SeekFrom; + + use c2pa_crypto::raw_signature::AsyncRawSigner; let ap = fixture_path("cloud.jpg"); // Do we generate JUMBF? @@ -5922,6 +5929,7 @@ pub mod tests { #[cfg(feature = "openssl_sign")] async fn test_dynamic_assertions() { use async_trait::async_trait; + use c2pa_crypto::raw_signature::AsyncRawSigner; #[derive(Serialize)] struct TestAssertion { @@ -5973,17 +5981,24 @@ pub mod tests { let signer = temp_signer(); DynamicSigner { alg: signer.alg(), - certs: signer.certs().unwrap_or_default(), + certs: signer.cert_chain().unwrap_or_default(), reserve_size: signer.reserve_size(), tsa_url: signer.time_stamp_service_url(), - ocsp_val: signer.ocsp_val(), + ocsp_val: signer.ocsp_response(), } } } + impl AsyncSigner for DynamicSigner { + // Returns our dynamic assertion here. + fn dynamic_assertions(&self) -> Vec> { + vec![Box::new(TestDynamicAssertion {})] + } + } + #[async_trait::async_trait] - impl crate::AsyncSigner for DynamicSigner { - async fn sign(&self, data: Vec) -> crate::error::Result> { + impl AsyncRawSigner for DynamicSigner { + async fn sign(&self, data: Vec) -> std::result::Result, RawSignerError> { let signer = temp_signer(); signer.sign(&data) } @@ -5992,7 +6007,7 @@ pub mod tests { self.alg } - fn certs(&self) -> crate::Result>> { + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { let mut output: Vec> = Vec::new(); for v in &self.certs { output.push(v.clone()); @@ -6004,14 +6019,9 @@ pub mod tests { self.reserve_size } - async fn ocsp_val(&self) -> Option> { + async fn ocsp_response(&self) -> Option> { self.ocsp_val.clone() } - - // Returns our dynamic assertion here. - fn dynamic_assertions(&self) -> Vec> { - vec![Box::new(TestDynamicAssertion {})] - } } #[async_trait::async_trait] diff --git a/sdk/src/utils/sig_utils.rs b/sdk/src/utils/sig_utils.rs index c5ef223a4..096a9de0d 100644 --- a/sdk/src/utils/sig_utils.rs +++ b/sdk/src/utils/sig_utils.rs @@ -11,14 +11,13 @@ // specific language governing permissions and limitations under // each license. -use c2pa_crypto::{p1363::parse_ec_der_sig, SigningAlg}; +use c2pa_crypto::{p1363::parse_ec_der_sig, raw_signature::RawSignerError, SigningAlg}; -use crate::{Error, Result}; - -pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result> { +pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result, RawSignerError> { // P1363 format: r | s - let (_, p) = parse_ec_der_sig(data).map_err(|_err| Error::InvalidEcdsaSignature)?; + let (_, p) = parse_ec_der_sig(data) + .map_err(|err| RawSignerError::InvalidSigningCredentials(err.to_string()))?; let mut r = extfmt::Hexlify(p.r).to_string(); let mut s = extfmt::Hexlify(p.s).to_string(); @@ -27,7 +26,11 @@ pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result> { SigningAlg::Es256 => 64, SigningAlg::Es384 => 96, SigningAlg::Es512 => 132, - _ => return Err(Error::UnsupportedType), + _ => { + return Err(RawSignerError::InternalError( + "unexpected signing alg".to_string(), + )) + } }; // pad or truncate as needed @@ -56,7 +59,9 @@ pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result> { }; if rp.len() != sig_len || rp.len() != sp.len() { - return Err(Error::InvalidEcdsaSignature); + return Err(RawSignerError::InternalError( + "unexpected signature length".to_string(), + )); } // merge r and s strings @@ -67,7 +72,9 @@ pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result> { (0..new_sig.len()) .step_by(2) .map(|i| { - u8::from_str_radix(&new_sig[i..i + 2], 16).map_err(|_err| Error::InvalidEcdsaSignature) + u8::from_str_radix(&new_sig[i..i + 2], 16).map_err(|_err| { + RawSignerError::InternalError("failed to convert hex string".to_string()) + }) }) .collect() } diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 57a7ff741..8511b33f4 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -20,7 +20,10 @@ use std::{ path::PathBuf, }; -use c2pa_crypto::SigningAlg; +use c2pa_crypto::{ + raw_signature::{AsyncRawSigner, RawSigner, RawSignerError}, + SigningAlg, +}; use tempfile::TempDir; #[cfg(feature = "file_io")] @@ -33,7 +36,7 @@ use crate::{ jumbf_io::get_assetio_handler, salt::DefaultSalt, store::Store, - RemoteSigner, Result, Signer, + AsyncSigner, RemoteSigner, Result, Signer, }; #[cfg(feature = "openssl_sign")] use crate::{ @@ -285,8 +288,10 @@ where pub(crate) struct TestGoodSigner {} -impl crate::Signer for TestGoodSigner { - fn sign(&self, _data: &[u8]) -> Result> { +impl Signer for TestGoodSigner {} + +impl RawSigner for TestGoodSigner { + fn sign(&self, _data: &[u8]) -> std::result::Result, RawSignerError> { Ok(b"not a valid signature".to_vec()) } @@ -294,7 +299,7 @@ impl crate::Signer for TestGoodSigner { SigningAlg::Ps256 } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { Ok(Vec::new()) } @@ -314,10 +319,12 @@ impl c2pa_crypto::time_stamp::TimeStampProvider for TestGoodSigner { pub(crate) struct AsyncTestGoodSigner {} +impl AsyncSigner for AsyncTestGoodSigner {} + #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -impl crate::AsyncSigner for AsyncTestGoodSigner { - async fn sign(&self, _data: Vec) -> Result> { +impl AsyncRawSigner for AsyncTestGoodSigner { + async fn sign(&self, _data: Vec) -> std::result::Result, RawSignerError> { Ok(b"not a valid signature".to_vec()) } @@ -325,7 +332,7 @@ impl crate::AsyncSigner for AsyncTestGoodSigner { SigningAlg::Ps256 } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { Ok(Vec::new()) } @@ -573,18 +580,31 @@ struct TempAsyncRemoteSigner { signer: TempRemoteSigner, } +impl AsyncSigner for TempAsyncRemoteSigner { + // signer will return a COSE structure + fn direct_cose_handling(&self) -> bool { + true + } +} + #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -impl crate::signer::AsyncSigner for TempAsyncRemoteSigner { +impl AsyncRawSigner for TempAsyncRemoteSigner { // this will not be called but requires an implementation - async fn sign(&self, claim_bytes: Vec) -> Result> { + async fn sign(&self, claim_bytes: Vec) -> std::result::Result, RawSignerError> { #[cfg(feature = "openssl_sign")] { let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); // this would happen on some remote server - crate::cose_sign::cose_sign_async(&signer, &claim_bytes, self.reserve_size()).await + crate::cose_sign::cose_sign_async(&signer, &claim_bytes, self.reserve_size()) + .await + .map_err(|_e| { + RawSignerError::InternalError( + "TO DO: figure out better error mapping".to_string(), + ) + }) } #[cfg(target_arch = "wasm32")] { @@ -606,16 +626,11 @@ impl crate::signer::AsyncSigner for TempAsyncRemoteSigner { } } - // signer will return a COSE structure - fn direct_cose_handling(&self) -> bool { - true - } - fn alg(&self) -> SigningAlg { SigningAlg::Ps256 } - fn certs(&self) -> Result>> { + fn cert_chain(&self) -> std::result::Result>, RawSignerError> { Ok(Vec::new()) } diff --git a/sdk/tests/common/test_signer.rs b/sdk/tests/common/test_signer.rs index c3bd1a2c1..60032a686 100644 --- a/sdk/tests/common/test_signer.rs +++ b/sdk/tests/common/test_signer.rs @@ -12,7 +12,7 @@ // each license. use c2pa::CallbackSigner; -use c2pa_crypto::SigningAlg; +use c2pa_crypto::{raw_signature::RawSignerError, SigningAlg}; const CERTS: &[u8] = include_bytes!("../../tests/fixtures/certs/ed25519.pub"); const PRIVATE_KEY: &[u8] = include_bytes!("../../tests/fixtures/certs/ed25519.pem"); @@ -23,18 +23,19 @@ pub fn test_signer() -> CallbackSigner { .set_context("test" as *const _ as *const ()) } -fn ed_sign(data: &[u8], private_key: &[u8]) -> c2pa::Result> { +fn ed_sign(data: &[u8], private_key: &[u8]) -> Result, RawSignerError> { use ed25519_dalek::{Signature, Signer, SigningKey}; use pem::parse; // Parse the PEM data to get the private key - let pem = parse(private_key).map_err(|e| c2pa::Error::OtherError(Box::new(e)))?; + let pem = parse(private_key).map_err(|e| RawSignerError::InternalError(e.to_string()))?; + // For Ed25519, the key is 32 bytes long, so we skip the first 16 bytes of the PEM data let key_bytes = &pem.contents()[16..]; - let signing_key = - SigningKey::try_from(key_bytes).map_err(|e| c2pa::Error::OtherError(Box::new(e)))?; + let signing_key = SigningKey::try_from(key_bytes) + .map_err(|e| RawSignerError::InternalError(e.to_string()))?; + // Sign the data let signature: Signature = signing_key.sign(data); - Ok(signature.to_bytes().to_vec()) } diff --git a/sdk/tests/v2_api_integration.rs b/sdk/tests/v2_api_integration.rs index cbeebd0cc..5b21ba013 100644 --- a/sdk/tests/v2_api_integration.rs +++ b/sdk/tests/v2_api_integration.rs @@ -15,12 +15,11 @@ // 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}; use anyhow::Result; use c2pa::{Builder, CallbackSigner, Reader}; - use c2pa_crypto::SigningAlg; + use c2pa_crypto::{raw_signature::RawSignerError, SigningAlg}; use serde_json::json; const PARENT_JSON: &str = r#" @@ -175,19 +174,20 @@ mod integration_v2 { Ok(()) } - fn ed_sign(data: &[u8], private_key: &[u8]) -> c2pa::Result> { + fn ed_sign(data: &[u8], private_key: &[u8]) -> Result, RawSignerError> { use ed25519_dalek::{Signature, Signer, SigningKey}; use pem::parse; // Parse the PEM data to get the private key - let pem = parse(private_key).map_err(|e| c2pa::Error::OtherError(Box::new(e)))?; + let pem = parse(private_key).map_err(|e| RawSignerError::InternalError(e.to_string()))?; + // For Ed25519, the key is 32 bytes long, so we skip the first 16 bytes of the PEM data let key_bytes = &pem.contents()[16..]; - let signing_key = - SigningKey::try_from(key_bytes).map_err(|e| c2pa::Error::OtherError(Box::new(e)))?; + let signing_key = SigningKey::try_from(key_bytes) + .map_err(|e| RawSignerError::InternalError(e.to_string()))?; + // Sign the data let signature: Signature = signing_key.sign(data); - Ok(signature.to_bytes().to_vec()) } } From 4540f5622d8bc7a113924410e322b6659c87b4c7 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 08:09:48 -0800 Subject: [PATCH 08/32] Clippy hack --- sdk/src/openssl/ec_signer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/openssl/ec_signer.rs b/sdk/src/openssl/ec_signer.rs index b74597f01..69038218c 100644 --- a/sdk/src/openssl/ec_signer.rs +++ b/sdk/src/openssl/ec_signer.rs @@ -83,7 +83,7 @@ impl RawSigner for EcSigner { SigningAlg::Es256 => openssl::sign::Signer::new(MessageDigest::sha256(), &key)?, SigningAlg::Es384 => openssl::sign::Signer::new(MessageDigest::sha384(), &key)?, SigningAlg::Es512 => openssl::sign::Signer::new(MessageDigest::sha512(), &key)?, - _ => panic!("Shouldn't happen"), + _ => unreachable!(), }; signer.update(data)?; From 1c03e97eb6e3938623af7895fe7bc18cce0d924a Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 08:32:45 -0800 Subject: [PATCH 09/32] Move check_chain_order into c2pa_crypto --- internal/crypto/src/openssl/cert_chain.rs | 48 +++++++++++++++++++++++ internal/crypto/src/openssl/mod.rs | 2 + 2 files changed, 50 insertions(+) create mode 100644 internal/crypto/src/openssl/cert_chain.rs diff --git a/internal/crypto/src/openssl/cert_chain.rs b/internal/crypto/src/openssl/cert_chain.rs new file mode 100644 index 000000000..f8442bfb3 --- /dev/null +++ b/internal/crypto/src/openssl/cert_chain.rs @@ -0,0 +1,48 @@ +// 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. + +#![allow(dead_code)] // TEMPORARY while refactoring + +use openssl::x509::X509; + +// Verify the certificate chain order. +// +// Return `true` if each cert in the chain can be verified as issued by the next +// issuer. +pub(crate) fn check_chain_order(certs: &[X509]) -> bool { + // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please + // don't make this pub or pub(crate) without finding a way to ensure that + // precondition. + + let mut iter = certs.iter().peekable(); + + while let Some(cert) = iter.next() { + let Some(next) = iter.peek() else { + break; + }; + + let Ok(pkey) = next.public_key() else { + return false; + }; + + let Ok(verified) = cert.verify(&pkey) else { + return false; + }; + + if !verified { + return false; + } + } + + true +} diff --git a/internal/crypto/src/openssl/mod.rs b/internal/crypto/src/openssl/mod.rs index 13af016a7..dc9102fa8 100644 --- a/internal/crypto/src/openssl/mod.rs +++ b/internal/crypto/src/openssl/mod.rs @@ -18,6 +18,8 @@ //! //! [`openssl` native code library]: https://crates.io/crates/openssl +mod cert_chain; + mod ffi_mutex; pub use ffi_mutex::{OpenSslMutex, OpenSslMutexUnavailable}; From 53788bb23849d330f345ae021a6c1d3aa4c45d34 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 09:25:21 -0800 Subject: [PATCH 10/32] Move RsaSigner into c2pa_crypto --- internal/crypto/Cargo.toml | 1 + internal/crypto/src/openssl/signers/mod.rs | 29 +- .../crypto/src/openssl/signers/rsa_signer.rs | 255 ++++++++++++++++++ internal/crypto/src/raw_signature/signer.rs | 20 +- internal/crypto/src/tests/openssl/mod.rs | 1 + .../crypto/src/tests/openssl/signers/mod.rs | 14 + .../src/tests/openssl/signers/rsa_signer.rs | 79 ++++++ 7 files changed, 384 insertions(+), 15 deletions(-) create mode 100644 internal/crypto/src/openssl/signers/rsa_signer.rs create mode 100644 internal/crypto/src/tests/openssl/signers/mod.rs create mode 100644 internal/crypto/src/tests/openssl/signers/rsa_signer.rs diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 06b3712f6..01acc663c 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -28,6 +28,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] json_schema = ["dep:schemars"] openssl = ["dep:openssl"] +psxxx_ocsp_stapling_experimental = ["dep:openssl"] [dependencies] async-generic = "1.1" diff --git a/internal/crypto/src/openssl/signers/mod.rs b/internal/crypto/src/openssl/signers/mod.rs index 21aaca338..138b1600e 100644 --- a/internal/crypto/src/openssl/signers/mod.rs +++ b/internal/crypto/src/openssl/signers/mod.rs @@ -19,6 +19,8 @@ use crate::{ SigningAlg, }; +mod rsa_signer; + /// Return a built-in [`RawSigner`] instance using the provided signing /// certificate and private key. /// @@ -27,11 +29,24 @@ use crate::{ /// /// Returns `None` if the signing algorithm is unsupported. May return an `Err` /// response if the certificate chain or private key are invalid. -pub fn signer_from_cert_chain_and_private_key( - _cert_chain: &[u8], - _private_key: &[u8], - _alg: SigningAlg, -) -> Option, RawSignerError>> { - // TEMPORARY: None implemented yet. - None +pub(crate) fn signer_from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + alg: SigningAlg, + time_stamp_service_url: Option, +) -> Result, RawSignerError> { + match alg { + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Ok(Box::new( + rsa_signer::RsaSigner::from_cert_chain_and_private_key( + cert_chain, + private_key, + alg, + time_stamp_service_url, + )?, + )), + + _ => Err(RawSignerError::InvalidSigningCredentials( + "unsupported algorithm".to_string(), + )), + } } diff --git a/internal/crypto/src/openssl/signers/rsa_signer.rs b/internal/crypto/src/openssl/signers/rsa_signer.rs new file mode 100644 index 000000000..e542f554d --- /dev/null +++ b/internal/crypto/src/openssl/signers/rsa_signer.rs @@ -0,0 +1,255 @@ +// 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::cell::Cell; + +use openssl::{ + hash::MessageDigest, + pkey::{PKey, Private}, + rsa::{Rsa, RsaPrivateKeyBuilder}, + sign::Signer, + x509::X509, +}; + +use crate::{ + ocsp::OcspResponse, + openssl::{cert_chain::check_chain_order, OpenSslMutex}, + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, + SigningAlg, +}; + +enum RsaSigningAlg { + Ps256, + Ps384, + Ps512, +} + +/// Implements [`RawSigner`] trait using OpenSSL's implementation of SHA256 + +/// RSA encryption. +pub(crate) struct RsaSigner { + alg: RsaSigningAlg, + + cert_chain: Vec, + cert_chain_len: usize, + + private_key: PKey, + + time_stamp_service_url: Option, + time_stamp_size: usize, + + ocsp_size: Cell, + ocsp_response: Cell, +} + +impl RsaSigner { + pub(crate) fn from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + alg: SigningAlg, + time_stamp_service_url: Option, + ) -> Result { + let _openssl = OpenSslMutex::acquire()?; + + let cert_chain = X509::stack_from_pem(cert_chain)?; + let cert_chain_len = cert_chain.len(); + + if !check_chain_order(&cert_chain) { + return Err(RawSignerError::InvalidSigningCredentials( + "certificate chain in incorrect order".to_string(), + )); + } + + // Rebuild RSA keys to eliminate incompatible values. + + let private_key = Rsa::private_key_from_pem(private_key)?; + + let n = private_key.n().to_owned()?; + let e = private_key.e().to_owned()?; + let d = private_key.d().to_owned()?; + let po = private_key.p(); + let qo = private_key.q(); + let dmp1o = private_key.dmp1(); + let dmq1o = private_key.dmq1(); + let iqmpo = private_key.iqmp(); + + let mut pk_builder = RsaPrivateKeyBuilder::new(n, e, d)?; + + if let Some(p) = po { + if let Some(q) = qo { + pk_builder = pk_builder.set_factors(p.to_owned()?, q.to_owned()?)?; + } + } + + if let Some(dmp1) = dmp1o { + if let Some(dmq1) = dmq1o { + if let Some(iqmp) = iqmpo { + pk_builder = pk_builder.set_crt_params( + dmp1.to_owned()?, + dmq1.to_owned()?, + iqmp.to_owned()?, + )?; + } + } + } + + let private_key = PKey::from_rsa(pk_builder.build())?; + + let alg: RsaSigningAlg = match alg { + SigningAlg::Ps256 => RsaSigningAlg::Ps256, + SigningAlg::Ps384 => RsaSigningAlg::Ps384, + SigningAlg::Ps512 => RsaSigningAlg::Ps512, + _ => { + return Err(RawSignerError::InternalError( + "RsaSigner should be used only for SigningAlg::Ps***".to_string(), + )); + } + }; + + let signer = RsaSigner { + alg, + cert_chain, + private_key, + cert_chain_len, + time_stamp_service_url, + time_stamp_size: 10000, + // TO DO: Call out to time stamp service to get actual time stamp and use that size? + ocsp_size: Cell::new(0), + ocsp_response: Cell::new(OcspResponse::default()), + }; + + // Acquire OCSP response if possible. + signer.update_ocsp(); + + Ok(signer) + } + + // Sample of OCSP stapling while signing. This code is only for demo purposes + // and not for production use since there is no caching in the SDK and fetching + // is expensive. This is behind the feature flag + // 'psxxx_ocsp_stapling_experimental'. + fn update_ocsp(&self) { + // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please + // don't make this pub or pub(crate) without finding a way to ensure that + // precondition. + + // Is it time for an OCSP update? + let now = chrono::offset::Utc::now(); + let ocsp_data = self.ocsp_response.take(); + let next_update = ocsp_data.next_update; + self.ocsp_response.set(ocsp_data); + + #[cfg(feature = "psxxx_ocsp_stapling_experimental")] + if now > next_update { + if let Ok(certs) = self.certs_internal() { + if let Some(ocsp_rsp) = crate::ocsp::fetch_ocsp_response(&certs) { + self.ocsp_size.set(ocsp_rsp.len()); + + let mut validation_log = c2pa_status_tracker::DetailedStatusTracker::default(); + + if let Ok(ocsp_response) = + OcspResponse::from_der_checked(&ocsp_rsp, None, &mut validation_log) + { + self.ocsp_response.set(ocsp_response); + } + } + } + } + } + + fn certs_internal(&self) -> Result>, RawSignerError> { + // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please + // don't make this pub or pub(crate) without finding a way to ensure that + // precondition. + + let mut certs: Vec> = Vec::new(); + + for c in &self.cert_chain { + let cert = c.to_der()?; + certs.push(cert); + } + + Ok(certs) + } +} + +impl RawSigner for RsaSigner { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { + let mut signer = match self.alg { + RsaSigningAlg::Ps256 => { + let mut signer = Signer::new(MessageDigest::sha256(), &self.private_key)?; + signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; + signer.set_rsa_mgf1_md(MessageDigest::sha256())?; + signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; + signer + } + + RsaSigningAlg::Ps384 => { + let mut signer = Signer::new(MessageDigest::sha384(), &self.private_key)?; + signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; + signer.set_rsa_mgf1_md(MessageDigest::sha384())?; + signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; + signer + } + + RsaSigningAlg::Ps512 => { + let mut signer = Signer::new(MessageDigest::sha512(), &self.private_key)?; + signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; + signer.set_rsa_mgf1_md(MessageDigest::sha512())?; + signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; + signer + } + }; + + Ok(signer.sign_oneshot_to_vec(data)?) + } + + fn reserve_size(&self) -> usize { + 1024 + self.cert_chain_len + self.time_stamp_size + self.ocsp_size.get() + // The Cose_Sign1 contains complete certs, timestamps, and OCSP. + } + + fn cert_chain(&self) -> Result>, RawSignerError> { + let _openssl = OpenSslMutex::acquire()?; + self.certs_internal() + } + + fn alg(&self) -> SigningAlg { + match self.alg { + RsaSigningAlg::Ps256 => SigningAlg::Ps256, + RsaSigningAlg::Ps384 => SigningAlg::Ps384, + RsaSigningAlg::Ps512 => SigningAlg::Ps512, + } + } + + fn ocsp_response(&self) -> Option> { + let _openssl = OpenSslMutex::acquire().ok()?; + + self.update_ocsp(); + + let ocsp_data = self.ocsp_response.take(); + let ocsp_response = ocsp_data.ocsp_der.clone(); + self.ocsp_response.set(ocsp_data); + if !ocsp_response.is_empty() { + Some(ocsp_response) + } else { + None + } + } +} + +impl TimeStampProvider for RsaSigner { + fn time_stamp_service_url(&self) -> Option { + self.time_stamp_service_url.clone() + } +} diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index 9d61f3be3..dd9d3b4a9 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -193,14 +193,16 @@ pub fn signer_from_cert_chain_and_private_key( cert_chain: &[u8], private_key: &[u8], alg: SigningAlg, -) -> Option, RawSignerError>> { + time_stamp_service_url: Option, +) -> Result, RawSignerError> { #[cfg(feature = "openssl")] - if let Some(signer) = crate::openssl::signers::signer_from_cert_chain_and_private_key( - cert_chain, - private_key, - alg, - ) { - return Some(signer); + { + return crate::openssl::signers::signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + alg, + time_stamp_service_url, + ); } // TO DO: Do we need this for WASM or is it all async? @@ -211,5 +213,7 @@ pub fn signer_from_cert_chain_and_private_key( // return Some(validator); // } - None + Err(RawSignerError::InternalError(format!( + "unsupported algorithm: {alg}" + ))) } diff --git a/internal/crypto/src/tests/openssl/mod.rs b/internal/crypto/src/tests/openssl/mod.rs index 27cb16350..1ce823fa2 100644 --- a/internal/crypto/src/tests/openssl/mod.rs +++ b/internal/crypto/src/tests/openssl/mod.rs @@ -12,4 +12,5 @@ // each license. mod ffi_mutex; +mod signers; mod validators; diff --git a/internal/crypto/src/tests/openssl/signers/mod.rs b/internal/crypto/src/tests/openssl/signers/mod.rs new file mode 100644 index 000000000..f776d7460 --- /dev/null +++ b/internal/crypto/src/tests/openssl/signers/mod.rs @@ -0,0 +1,14 @@ +// Copyright 2024 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. + +mod rsa_signer; diff --git a/internal/crypto/src/tests/openssl/signers/rsa_signer.rs b/internal/crypto/src/tests/openssl/signers/rsa_signer.rs new file mode 100644 index 000000000..c51db3960 --- /dev/null +++ b/internal/crypto/src/tests/openssl/signers/rsa_signer.rs @@ -0,0 +1,79 @@ +use openssl::x509::X509; + +use crate::{ + openssl::{signers::signer_from_cert_chain_and_private_key, validators::RsaValidator}, + raw_signature::RawSignatureValidator, + SigningAlg, +}; + +#[test] +fn ps256() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ps256.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ps256.priv"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Ps256, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + RsaValidator::Ps256 + .validate(&signature, data, &pub_key) + .unwrap(); +} + +#[test] +fn ps384() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ps384.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ps384.priv"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Ps384, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + RsaValidator::Ps384 + .validate(&signature, data, &pub_key) + .unwrap(); +} + +#[test] +fn ps512() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ps512.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ps512.priv"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Ps512, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + RsaValidator::Ps512 + .validate(&signature, data, &pub_key) + .unwrap(); +} From 5b2605a44ba85064f76138d3d0090a21b8e3237c Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 09:59:07 -0800 Subject: [PATCH 11/32] Remove RsaSigner from c2pa-rs --- sdk/src/cose_validator.rs | 16 +- sdk/src/create_signer.rs | 30 ++- sdk/src/openssl/mod.rs | 8 +- sdk/src/openssl/rsa_signer.rs | 303 --------------------------- sdk/src/openssl/temp_signer.rs | 6 +- sdk/src/openssl/temp_signer_async.rs | 2 +- sdk/src/signer.rs | 56 ++++- sdk/src/store.rs | 6 +- sdk/src/utils/test.rs | 26 +-- 9 files changed, 103 insertions(+), 350 deletions(-) delete mode 100644 sdk/src/openssl/rsa_signer.rs diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index 3b3a7ac11..587a47f8f 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -1457,7 +1457,9 @@ pub mod tests { #[test] #[cfg(feature = "openssl_sign")] fn test_stapled_ocsp() { - use c2pa_crypto::raw_signature::{RawSigner, RawSignerError}; + use c2pa_crypto::raw_signature::{ + signer_from_cert_chain_and_private_key, RawSigner, RawSignerError, + }; let mut validation_log = DetailedStatusTracker::default(); @@ -1470,13 +1472,9 @@ pub mod tests { let pem_key = include_bytes!("../tests/fixtures/certs/ps256.pem").to_vec(); let ocsp_rsp_data = include_bytes!("../tests/fixtures/ocsp_good.data"); - let signer = crate::openssl::RsaSigner::from_signcert_and_pkey( - &sign_cert, - &pem_key, - SigningAlg::Ps256, - None, - ) - .unwrap(); + let signer = + signer_from_cert_chain_and_private_key(&sign_cert, &pem_key, SigningAlg::Ps256, None) + .unwrap(); // create a test signer that supports stapling struct OcspSigner { @@ -1511,7 +1509,7 @@ pub mod tests { impl c2pa_crypto::time_stamp::TimeStampProvider for OcspSigner {} let ocsp_signer = OcspSigner { - signer: Box::new(signer), + signer: Box::new(crate::signer::RawSignerWrapper(signer)), ocsp_rsp: ocsp_rsp_data.to_vec(), }; diff --git a/sdk/src/create_signer.rs b/sdk/src/create_signer.rs index 176cfdf07..9b2767d2c 100644 --- a/sdk/src/create_signer.rs +++ b/sdk/src/create_signer.rs @@ -18,12 +18,12 @@ #[cfg(feature = "file_io")] use std::path::Path; -use c2pa_crypto::SigningAlg; +use c2pa_crypto::{raw_signature::signer_from_cert_chain_and_private_key, SigningAlg}; use crate::{ error::Result, - openssl::{EcSigner, EdSigner, RsaSigner}, - signer::ConfigurableSigner, + openssl::{EcSigner, EdSigner}, + signer::{ConfigurableSigner, RawSignerWrapper}, Signer, }; @@ -46,12 +46,14 @@ pub fn from_keys( tsa_url: Option, ) -> Result> { Ok(match alg { - SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Box::new( - RsaSigner::from_signcert_and_pkey(signcert, pkey, alg, tsa_url)?, - ), + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Box::new(RawSignerWrapper( + signer_from_cert_chain_and_private_key(signcert, pkey, alg, tsa_url)?, + )), + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Box::new( EcSigner::from_signcert_and_pkey(signcert, pkey, alg, tsa_url)?, ), + SigningAlg::Ed25519 => Box::new(EdSigner::from_signcert_and_pkey( signcert, pkey, alg, tsa_url, )?), @@ -75,12 +77,22 @@ pub fn from_files>( tsa_url: Option, ) -> Result> { Ok(match alg { - SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Box::new( - RsaSigner::from_files(&signcert_path, &pkey_path, alg, tsa_url)?, - ), + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => { + let cert_chain = std::fs::read(signcert_path)?; + let private_key = std::fs::read(pkey_path)?; + + Box::new(RawSignerWrapper(signer_from_cert_chain_and_private_key( + &cert_chain, + &private_key, + alg, + tsa_url, + )?)) + } + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Box::new( EcSigner::from_files(&signcert_path, &pkey_path, alg, tsa_url)?, ), + SigningAlg::Ed25519 => Box::new(EdSigner::from_files( &signcert_path, &pkey_path, diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs index 13dbaeb83..684c496f3 100644 --- a/sdk/src/openssl/mod.rs +++ b/sdk/src/openssl/mod.rs @@ -11,10 +11,10 @@ // specific language governing permissions and limitations under // each license. -#[cfg(feature = "openssl_sign")] -mod rsa_signer; -#[cfg(feature = "openssl_sign")] -pub(crate) use rsa_signer::RsaSigner; +// #[cfg(feature = "openssl_sign")] +// mod rsa_signer; +// #[cfg(feature = "openssl_sign")] +// pub(crate) use rsa_signer::RsaSigner; #[cfg(feature = "openssl_sign")] mod ec_signer; diff --git a/sdk/src/openssl/rsa_signer.rs b/sdk/src/openssl/rsa_signer.rs deleted file mode 100644 index 3c5bebfa1..000000000 --- a/sdk/src/openssl/rsa_signer.rs +++ /dev/null @@ -1,303 +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::cell::Cell; - -use c2pa_crypto::{ - ocsp::OcspResponse, - openssl::OpenSslMutex, - raw_signature::{RawSigner, RawSignerError}, - time_stamp::TimeStampProvider, - SigningAlg, -}; -use openssl::{ - hash::MessageDigest, - pkey::{PKey, Private}, - rsa::{Rsa, RsaPrivateKeyBuilder}, - x509::X509, -}; - -use super::check_chain_order; -use crate::{signer::ConfigurableSigner, Error, Signer}; - -/// Implements `Signer` trait using OpenSSL's implementation of -/// SHA256 + RSA encryption. -pub struct RsaSigner { - signcerts: Vec, - pkey: PKey, - - certs_size: usize, - timestamp_size: usize, - ocsp_size: Cell, - - alg: SigningAlg, - tsa_url: Option, - ocsp_rsp: Cell, -} - -impl RsaSigner { - // Sample of OCSP stapling while signing. This code is only for demo purposes and not for - // production use since there is no caching in the SDK and fetching is expensive. This is behind the - // feature flag 'psxxx_ocsp_stapling_experimental' - fn update_ocsp(&self) { - // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please - // don't make this pub or pub(crate) without finding a way to ensure that - // precondition. - - // do we need an update - let now = chrono::offset::Utc::now(); - - // is it time for an OCSP update - let ocsp_data = self.ocsp_rsp.take(); - let next_update = ocsp_data.next_update; - self.ocsp_rsp.set(ocsp_data); - if now > next_update { - #[cfg(feature = "psxxx_ocsp_stapling_experimental")] - { - if let Ok(certs) = self.certs_internal() { - if let Some(ocsp_rsp) = c2pa_crypto::ocsp::fetch_ocsp_response(&certs) { - self.ocsp_size.set(ocsp_rsp.len()); - let mut validation_log = - c2pa_status_tracker::DetailedStatusTracker::default(); - if let Ok(ocsp_response) = - OcspResponse::from_der_checked(&ocsp_rsp, None, &mut validation_log) - { - self.ocsp_rsp.set(ocsp_response); - } - } - } - } - } - } - - fn certs_internal(&self) -> Result>, RawSignerError> { - // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please - // don't make this pub or pub(crate) without finding a way to ensure that - // precondition. - - let mut certs: Vec> = Vec::new(); - - for c in &self.signcerts { - let cert = c.to_der()?; - certs.push(cert); - } - - Ok(certs) - } -} - -impl ConfigurableSigner for RsaSigner { - fn from_signcert_and_pkey( - signcert: &[u8], - pkey: &[u8], - alg: SigningAlg, - tsa_url: Option, - ) -> crate::Result { - let _openssl = OpenSslMutex::acquire()?; - - let signcerts = X509::stack_from_pem(signcert).map_err(wrap_openssl_err)?; - let rsa = Rsa::private_key_from_pem(pkey).map_err(wrap_openssl_err)?; - - // make sure cert chains are in order - if !check_chain_order(&signcerts) { - return Err(Error::BadParam( - "certificate chain is not in correct order".to_string(), - )); - } - - // rebuild RSA keys to eliminate incompatible values - let n = rsa.n().to_owned().map_err(wrap_openssl_err)?; - let e = rsa.e().to_owned().map_err(wrap_openssl_err)?; - let d = rsa.d().to_owned().map_err(wrap_openssl_err)?; - let po = rsa.p(); - let qo = rsa.q(); - let dmp1o = rsa.dmp1(); - let dmq1o = rsa.dmq1(); - let iqmpo = rsa.iqmp(); - let mut builder = RsaPrivateKeyBuilder::new(n, e, d).map_err(wrap_openssl_err)?; - - if let Some(p) = po { - if let Some(q) = qo { - builder = builder - .set_factors(p.to_owned()?, q.to_owned()?) - .map_err(wrap_openssl_err)?; - } - } - - if let Some(dmp1) = dmp1o { - if let Some(dmq1) = dmq1o { - if let Some(iqmp) = iqmpo { - builder = builder - .set_crt_params(dmp1.to_owned()?, dmq1.to_owned()?, iqmp.to_owned()?) - .map_err(wrap_openssl_err)?; - } - } - } - - let new_rsa = builder.build(); - - let pkey = PKey::from_rsa(new_rsa).map_err(wrap_openssl_err)?; - - let signer = RsaSigner { - signcerts, - pkey, - certs_size: signcert.len(), - timestamp_size: 10000, /* todo: call out to TSA to get actual timestamp and use that size */ - ocsp_size: Cell::new(0), - alg, - tsa_url, - ocsp_rsp: Cell::new(OcspResponse::default()), - }; - - // get OCSP if possible - signer.update_ocsp(); - - Ok(signer) - } -} - -impl Signer for RsaSigner {} - -impl RawSigner for RsaSigner { - fn sign(&self, data: &[u8]) -> Result, RawSignerError> { - let mut signer = match self.alg { - SigningAlg::Ps256 => { - let mut signer = openssl::sign::Signer::new(MessageDigest::sha256(), &self.pkey)?; - - signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding - signer.set_rsa_mgf1_md(MessageDigest::sha256())?; - signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; - signer - } - - SigningAlg::Ps384 => { - let mut signer = openssl::sign::Signer::new(MessageDigest::sha384(), &self.pkey)?; - - signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding - signer.set_rsa_mgf1_md(MessageDigest::sha384())?; - signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; - signer - } - - SigningAlg::Ps512 => { - let mut signer = openssl::sign::Signer::new(MessageDigest::sha512(), &self.pkey)?; - - signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?; // use C2PA recommended padding - signer.set_rsa_mgf1_md(MessageDigest::sha512())?; - signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?; - signer - } - - _ => unreachable!(), - }; - - let signed_data = signer.sign_oneshot_to_vec(data)?; - - // println!("sig: {}", Hexlify(&signed_data)); - - Ok(signed_data) - } - - fn reserve_size(&self) -> usize { - 1024 + self.certs_size + self.timestamp_size + self.ocsp_size.get() // the Cose_Sign1 contains complete certs, timestamps and ocsp so account for size - } - - fn cert_chain(&self) -> Result>, RawSignerError> { - let _openssl = OpenSslMutex::acquire()?; - self.certs_internal() - } - - fn alg(&self) -> SigningAlg { - self.alg - } - - fn ocsp_response(&self) -> Option> { - let _openssl = OpenSslMutex::acquire().ok()?; - - // update OCSP if needed - self.update_ocsp(); - - let ocsp_data = self.ocsp_rsp.take(); - let ocsp_rsp = ocsp_data.ocsp_der.clone(); - self.ocsp_rsp.set(ocsp_data); - if !ocsp_rsp.is_empty() { - Some(ocsp_rsp) - } else { - None - } - } -} - -impl TimeStampProvider for RsaSigner { - fn time_stamp_service_url(&self) -> Option { - self.tsa_url.clone() - } -} - -fn wrap_openssl_err(err: openssl::error::ErrorStack) -> Error { - Error::OpenSslError(err) -} - -#[allow(unused_imports)] -#[allow(clippy::unwrap_used)] -#[cfg(test)] -mod tests { - - use super::*; - use crate::{ - utils::test::{fixture_path, temp_signer}, - Signer, SigningAlg, - }; - - #[test] - fn signer_from_files() { - let signer = temp_signer(); - let data = b"some sample content to sign"; - - let signature = signer.sign(data).unwrap(); - println!("signature len = {}", signature.len()); - assert!(signature.len() <= signer.reserve_size()); - } - - #[test] - fn sign_ps256() { - let cert_bytes = include_bytes!("../../tests/fixtures/temp_cert.data"); - let key_bytes = include_bytes!("../../tests/fixtures/temp_priv_key.data"); - - let signer = - RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, SigningAlg::Ps256, None) - .unwrap(); - - let data = b"some sample content to sign"; - - let signature = signer.sign(data).unwrap(); - println!("signature len = {}", signature.len()); - assert!(signature.len() <= signer.reserve_size()); - } - - // #[test] - // fn sign_rs256() { - // let cert_bytes = include_bytes!("../../tests/fixtures/temp_cert.data"); - // let key_bytes = include_bytes!("../../tests/fixtures/temp_priv_key.data"); - - // let signer = - // RsaSigner::from_signcert_and_pkey(cert_bytes, key_bytes, "rs256".to_string(), None) - // .unwrap(); - - // let data = b"some sample content to sign"; - - // let signature = signer.sign(data).unwrap(); - // println!("signature len = {}", signature.len()); - // assert!(signature.len() <= signer.reserve_size()); - // } -} diff --git a/sdk/src/openssl/temp_signer.rs b/sdk/src/openssl/temp_signer.rs index 411615cdf..840f402ac 100644 --- a/sdk/src/openssl/temp_signer.rs +++ b/sdk/src/openssl/temp_signer.rs @@ -39,7 +39,7 @@ use c2pa_crypto::SigningAlg; #[cfg(feature = "file_io")] use crate::{ - openssl::{EcSigner, EdSigner, RsaSigner}, + openssl::{EcSigner, EdSigner}, signer::ConfigurableSigner, }; @@ -153,7 +153,7 @@ pub fn get_rsa_signer>( path: P, alg: SigningAlg, tsa_url: Option, -) -> (RsaSigner, PathBuf) { +) -> (Box, PathBuf) { match alg { SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => (), _ => { @@ -178,7 +178,7 @@ pub fn get_rsa_signer>( } ( - RsaSigner::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), + crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), sign_cert_path, ) } diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs index f342ac8b7..c9c11bd95 100644 --- a/sdk/src/openssl/temp_signer_async.rs +++ b/sdk/src/openssl/temp_signer_async.rs @@ -32,7 +32,7 @@ fn get_local_signer(alg: SigningAlg) -> Box { match alg { SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => { let (s, _k) = super::temp_signer::get_rsa_signer(&cert_dir, alg, None); - Box::new(s) + s } SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => { let (s, _k) = super::temp_signer::get_ec_signer(&cert_dir, alg, None); diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 1b5ad5451..29b2ad57b 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -106,7 +106,7 @@ pub trait RemoteSigner: Sync { fn reserve_size(&self) -> usize; } -impl Signer for Box { +impl Signer for Box { fn direct_cose_handling(&self) -> bool { (**self).direct_cose_handling() } @@ -116,7 +116,7 @@ impl Signer for Box { } } -impl RawSigner for Box { +impl RawSigner for Box { fn sign(&self, data: &[u8]) -> Result, RawSignerError> { (**self).sign(data) } @@ -138,7 +138,7 @@ impl RawSigner for Box { } } -impl TimeStampProvider for Box { +impl TimeStampProvider for Box { fn time_stamp_service_url(&self) -> Option { (**self).time_stamp_service_url() } @@ -161,3 +161,53 @@ impl TimeStampProvider for Box { (**self).send_time_stamp_request(message) } } + +pub(crate) struct RawSignerWrapper(pub(crate) Box); + +impl Signer for RawSignerWrapper {} + +impl RawSigner for RawSignerWrapper { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { + self.0.sign(data) + } + + fn alg(&self) -> SigningAlg { + self.0.alg() + } + + fn cert_chain(&self) -> Result>, RawSignerError> { + self.0.cert_chain() + } + + fn reserve_size(&self) -> usize { + self.0.reserve_size() + } + + fn ocsp_response(&self) -> Option> { + self.0.ocsp_response() + } +} + +impl TimeStampProvider for RawSignerWrapper { + fn time_stamp_service_url(&self) -> Option { + self.0.time_stamp_service_url() + } + + fn time_stamp_request_headers(&self) -> Option> { + self.0.time_stamp_request_headers() + } + + fn time_stamp_request_body( + &self, + message: &[u8], + ) -> std::result::Result, TimeStampError> { + self.0.time_stamp_request_body(message) + } + + fn send_time_stamp_request( + &self, + message: &[u8], + ) -> Option, TimeStampError>> { + self.0.send_time_stamp_request(message) + } +} diff --git a/sdk/src/store.rs b/sdk/src/store.rs index b1639006e..7e51a5618 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -3903,7 +3903,9 @@ pub mod tests { #[test] #[cfg(feature = "file_io")] fn test_sign_with_expired_cert() { - use crate::{openssl::RsaSigner, signer::ConfigurableSigner, SigningAlg}; + use c2pa_crypto::SigningAlg; + + use crate::create_signer; // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); @@ -3917,7 +3919,7 @@ pub mod tests { let signcert_path = fixture_path("rsa-pss256_key-expired.pub"); let pkey_path = fixture_path("rsa-pss256-expired.pem"); let signer = - RsaSigner::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None).unwrap(); + create_signer::from_files(signcert_path, pkey_path, SigningAlg::Ps256, None).unwrap(); store.commit_claim(claim).unwrap(); diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 8511b33f4..52febc3d0 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -28,6 +28,8 @@ use tempfile::TempDir; #[cfg(feature = "file_io")] use crate::create_signer; +#[cfg(feature = "openssl_sign")] +use crate::openssl::AsyncSignerAdapter; use crate::{ assertions::{labels, Action, Actions, Ingredient, ReviewRating, SchemaDotOrg, Thumbnail}, asset_io::CAIReadWrite, @@ -38,11 +40,6 @@ use crate::{ store::Store, AsyncSigner, RemoteSigner, Result, Signer, }; -#[cfg(feature = "openssl_sign")] -use crate::{ - openssl::{AsyncSignerAdapter, RsaSigner}, - signer::ConfigurableSigner, -}; pub const TEST_SMALL_JPEG: &str = "earth_apollo17.jpg"; @@ -215,7 +212,7 @@ pub fn temp_fixture_path(temp_dir: &TempDir, file_name: &str) -> PathBuf { /// Can panic if the certs cannot be read. (This function should only /// be used as part of testing infrastructure.) #[cfg(feature = "file_io")] -pub fn temp_signer_file() -> RsaSigner { +pub fn temp_signer_file() -> Box { #![allow(clippy::expect_used)] let mut sign_cert_path = fixture_path("certs"); sign_cert_path.push("ps256"); @@ -225,7 +222,7 @@ pub fn temp_signer_file() -> RsaSigner { pem_key_path.push("ps256"); pem_key_path.set_extension("pem"); - RsaSigner::from_files(&sign_cert_path, &pem_key_path, SigningAlg::Ps256, None) + crate::create_signer::from_files(&sign_cert_path, &pem_key_path, SigningAlg::Ps256, None) .expect("get_temp_signer") } @@ -365,11 +362,8 @@ pub(crate) fn temp_signer() -> Box { let sign_cert = include_bytes!("../../tests/fixtures/certs/ps256.pub").to_vec(); let pem_key = include_bytes!("../../tests/fixtures/certs/ps256.pem").to_vec(); - let signer = - RsaSigner::from_signcert_and_pkey(&sign_cert, &pem_key, SigningAlg::Ps256, None) - .expect("get_temp_signer"); - - Box::new(signer) + crate::create_signer::from_keys(&sign_cert, &pem_key, SigningAlg::Ps256, None) + .expect("get_temp_signer") } // todo: the will be a RustTLS signer shortly @@ -600,10 +594,10 @@ impl AsyncRawSigner for TempAsyncRemoteSigner { // this would happen on some remote server crate::cose_sign::cose_sign_async(&signer, &claim_bytes, self.reserve_size()) .await - .map_err(|_e| { - RawSignerError::InternalError( - "TO DO: figure out better error mapping".to_string(), - ) + .map_err(|e| { + RawSignerError::InternalError(format!( + "TO DO: figure out better error mapping: {e}" + )) }) } #[cfg(target_arch = "wasm32")] From 36a5bbdd1407b21b33549f20faf862c4723d047c Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 14:50:52 -0800 Subject: [PATCH 12/32] Clippy --- .../crypto/src/openssl/signers/rsa_signer.rs | 34 +++++++++++-------- sdk/src/store.rs | 4 +-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/internal/crypto/src/openssl/signers/rsa_signer.rs b/internal/crypto/src/openssl/signers/rsa_signer.rs index e542f554d..fae7e7f6e 100644 --- a/internal/crypto/src/openssl/signers/rsa_signer.rs +++ b/internal/crypto/src/openssl/signers/rsa_signer.rs @@ -144,23 +144,27 @@ impl RsaSigner { // precondition. // Is it time for an OCSP update? - let now = chrono::offset::Utc::now(); - let ocsp_data = self.ocsp_response.take(); - let next_update = ocsp_data.next_update; - self.ocsp_response.set(ocsp_data); #[cfg(feature = "psxxx_ocsp_stapling_experimental")] - if now > next_update { - if let Ok(certs) = self.certs_internal() { - if let Some(ocsp_rsp) = crate::ocsp::fetch_ocsp_response(&certs) { - self.ocsp_size.set(ocsp_rsp.len()); - - let mut validation_log = c2pa_status_tracker::DetailedStatusTracker::default(); - - if let Ok(ocsp_response) = - OcspResponse::from_der_checked(&ocsp_rsp, None, &mut validation_log) - { - self.ocsp_response.set(ocsp_response); + { + let ocsp_data = self.ocsp_response.take(); + let next_update = ocsp_data.next_update; + self.ocsp_response.set(ocsp_data); + + let now = chrono::offset::Utc::now(); + if now > next_update { + if let Ok(certs) = self.certs_internal() { + if let Some(ocsp_rsp) = crate::ocsp::fetch_ocsp_response(&certs) { + self.ocsp_size.set(ocsp_rsp.len()); + + let mut validation_log = + c2pa_status_tracker::DetailedStatusTracker::default(); + + if let Ok(ocsp_response) = + OcspResponse::from_der_checked(&ocsp_rsp, None, &mut validation_log) + { + self.ocsp_response.set(ocsp_response); + } } } } diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 1832ec699..2bf571ae0 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -503,14 +503,14 @@ impl Store { let result = if _sync { if signer.direct_cose_handling() { // Let the signer do all the COSE processing and return the structured COSE data. - return Ok(signer.sign(&claim_bytes)?); // do not verify remote signers (we never did) + return signer.sign(&claim_bytes); // do not verify remote signers (we never did) } else { cose_sign(signer, &claim_bytes, box_size) } } else { if signer.direct_cose_handling() { // Let the signer do all the COSE processing and return the structured COSE data. - return signer.sign(claim_bytes.clone()).await.map_err(|e| e.into()); + return signer.sign(claim_bytes.clone()).await; // do not verify remote signers (we never did) } else { cose_sign_async(signer, &claim_bytes, box_size).await From c72ce8e40319bd31da3c3420554044ca55362c09 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 14:57:25 -0800 Subject: [PATCH 13/32] Sort a few more build issues --- internal/crypto/src/raw_signature/signer.rs | 4 ++-- sdk/src/callback_signer.rs | 2 +- sdk/src/cose_sign.rs | 6 ++++-- sdk/src/signer.rs | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index dd9d3b4a9..24b5c13ab 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -142,10 +142,10 @@ impl From for RawSignerError { fn from(err: crate::webcrypto::WasmCryptoError) -> Self { match err { crate::webcrypto::WasmCryptoError::UnknownContext => { - Self::InternalError("unknown WASM context") + Self::InternalError("unknown WASM context".to_string()) } crate::webcrypto::WasmCryptoError::NoCryptoAvailable => { - Self::InternalError("WASM crypto unavailable") + Self::InternalError("WASM crypto unavailable".to_string()) } } } diff --git a/sdk/src/callback_signer.rs b/sdk/src/callback_signer.rs index b8beabaa9..9dd1eeac5 100644 --- a/sdk/src/callback_signer.rs +++ b/sdk/src/callback_signer.rs @@ -189,7 +189,7 @@ impl AsyncSigner for CallbackSigner { } #[cfg(target_arch = "wasm32")] - async fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { + 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 4c92ca055..8cc7257f9 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -359,7 +359,7 @@ mod tests { #![allow(clippy::unwrap_used)] use super::sign_claim; - use crate::{claim::Claim, utils::test::temp_signer, AsyncSigner, Signer}; + use crate::{claim::Claim, utils::test::temp_signer, Signer}; #[test] fn test_sign_claim() { @@ -380,7 +380,9 @@ mod tests { #[cfg(feature = "openssl")] #[actix::test] async fn test_sign_claim_async() { - use crate::{cose_sign::sign_claim_async, openssl::AsyncSignerAdapter, SigningAlg}; + use crate::{ + cose_sign::sign_claim_async, openssl::AsyncSignerAdapter, AsyncSigner, SigningAlg, + }; let mut claim = Claim::new("extern_sign_test", Some("contentauth")); claim.build().unwrap(); diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 25684d07c..fe3260336 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -346,6 +346,7 @@ impl Signer for Box { } } +#[allow(dead_code)] // TEMPORARY: Not used on WASM pub(crate) struct RawSignerWrapper(pub(crate) Box); impl Signer for RawSignerWrapper { From 27dee3395b6b016354d12419050a37c52e590f3e Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 15:11:22 -0800 Subject: [PATCH 14/32] Revert error type changes --- .../crypto/src/openssl/signers/rsa_signer.rs | 1 - sdk/src/callback_signer.rs | 22 +++++++++---------- sdk/src/cose_sign.rs | 6 ++--- sdk/src/openssl/mod.rs | 5 ----- sdk/src/openssl/openssl_trust_handler.rs | 6 ++--- sdk/src/openssl/temp_signer_async.rs | 3 ++- sdk/src/signer.rs | 22 +++++++++---------- sdk/src/utils/sig_utils.rs | 12 +++++----- sdk/tests/common/test_signer.rs | 4 ++-- 9 files changed, 36 insertions(+), 45 deletions(-) diff --git a/internal/crypto/src/openssl/signers/rsa_signer.rs b/internal/crypto/src/openssl/signers/rsa_signer.rs index fae7e7f6e..3e5a00044 100644 --- a/internal/crypto/src/openssl/signers/rsa_signer.rs +++ b/internal/crypto/src/openssl/signers/rsa_signer.rs @@ -71,7 +71,6 @@ impl RsaSigner { } // Rebuild RSA keys to eliminate incompatible values. - let private_key = Rsa::private_key_from_pem(private_key)?; let n = private_key.n().to_owned()?; diff --git a/sdk/src/callback_signer.rs b/sdk/src/callback_signer.rs index 9dd1eeac5..5c8df0e47 100644 --- a/sdk/src/callback_signer.rs +++ b/sdk/src/callback_signer.rs @@ -19,7 +19,7 @@ use async_trait::async_trait; use c2pa_crypto::{raw_signature::RawSignerError, SigningAlg}; -use crate::{AsyncSigner, Error, Signer}; +use crate::{AsyncSigner, Error, Result, Signer}; /// Defines a callback function interface for a [`CallbackSigner`]. /// @@ -107,17 +107,17 @@ impl CallbackSigner { /// |_context: *const _, data: &[u8]| CallbackSigner::ed25519_sign(data, PRIVATE_KEY); /// let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS); /// ``` - pub fn ed25519_sign(data: &[u8], private_key: &[u8]) -> crate::Result> { + pub fn ed25519_sign(data: &[u8], private_key: &[u8]) -> Result> { use ed25519_dalek::{Signature, Signer, SigningKey}; use pem::parse; // Parse the PEM data to get the private key - let pem = parse(private_key).map_err(|e| Error::BadParam(e.to_string()))?; + let pem = parse(private_key).map_err(|e| Error::OtherError(Box::new(e)))?; // For Ed25519, the key is 32 bytes long, so we skip the first 16 bytes of the PEM data let key_bytes = &pem.contents()[16..]; let signing_key = - SigningKey::try_from(key_bytes).map_err(|e| Error::BadParam(e.to_string()))?; + SigningKey::try_from(key_bytes).map_err(|e| Error::OtherError(Box::new(e)))?; // Sign the data let signature: Signature = signing_key.sign(data); @@ -140,7 +140,7 @@ impl Default for CallbackSigner { } impl Signer for CallbackSigner { - fn sign(&self, data: &[u8]) -> crate::Result> { + fn sign(&self, data: &[u8]) -> Result> { (self.callback)(self.context, data) } @@ -148,10 +148,8 @@ impl Signer for CallbackSigner { self.alg } - fn certs(&self) -> crate::Result>> { - let pems = pem::parse_many(&self.certs) - .map_err(|e| RawSignerError::InternalError(e.to_string()))?; - + fn certs(&self) -> Result>> { + let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?; Ok(pems.into_iter().map(|p| p.into_contents()).collect()) } @@ -167,7 +165,7 @@ impl Signer for CallbackSigner { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl AsyncSigner for CallbackSigner { - async fn sign(&self, data: Vec) -> crate::Result> { + async fn sign(&self, data: Vec) -> Result> { (self.callback)(self.context, &data) } @@ -175,7 +173,7 @@ impl AsyncSigner for CallbackSigner { self.alg } - fn certs(&self) -> crate::Result>> { + fn certs(&self) -> Result>> { let pems = pem::parse_many(&self.certs).map_err(|e| Error::OtherError(Box::new(e)))?; Ok(pems.into_iter().map(|p| p.into_contents()).collect()) } @@ -189,7 +187,7 @@ impl AsyncSigner for CallbackSigner { } #[cfg(target_arch = "wasm32")] - async fn send_timestamp_request(&self, _message: &[u8]) -> Option>> { + 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 8cc7257f9..0ae88e4d1 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -359,7 +359,7 @@ mod tests { #![allow(clippy::unwrap_used)] use super::sign_claim; - use crate::{claim::Claim, utils::test::temp_signer, Signer}; + use crate::{claim::Claim, utils::test::temp_signer, Signer, Result}; #[test] fn test_sign_claim() { @@ -408,7 +408,7 @@ mod tests { } impl Signer for BogusSigner { - fn sign(&self, _data: &[u8]) -> crate::Result> { + fn sign(&self, _data: &[u8]) -> Result> { eprintln!("Canary, canary, please cause this deploy to fail!"); Ok(b"totally bogus signature".to_vec()) } @@ -417,7 +417,7 @@ mod tests { c2pa_crypto::SigningAlg::Ps256 } - fn certs(&self) -> crate::Result>> { + fn certs(&self) -> Result>> { let cert_vec: Vec = Vec::new(); let certs = vec![cert_vec]; Ok(certs) diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs index 684c496f3..c4b9c21e2 100644 --- a/sdk/src/openssl/mod.rs +++ b/sdk/src/openssl/mod.rs @@ -11,11 +11,6 @@ // specific language governing permissions and limitations under // each license. -// #[cfg(feature = "openssl_sign")] -// mod rsa_signer; -// #[cfg(feature = "openssl_sign")] -// pub(crate) use rsa_signer::RsaSigner; - #[cfg(feature = "openssl_sign")] mod ec_signer; #[cfg(feature = "openssl_sign")] diff --git a/sdk/src/openssl/openssl_trust_handler.rs b/sdk/src/openssl/openssl_trust_handler.rs index 34aff883d..9d5ae56b9 100644 --- a/sdk/src/openssl/openssl_trust_handler.rs +++ b/sdk/src/openssl/openssl_trust_handler.rs @@ -250,12 +250,12 @@ pub(crate) fn verify_trust( let _openssl = OpenSslMutex::acquire()?; - let mut certs = openssl::stack::Stack::new().map_err(Error::OpenSslError)?; + let mut cert_chain = openssl::stack::Stack::new().map_err(Error::OpenSslError)?; let mut store_ctx = openssl::x509::X509StoreContext::new().map_err(Error::OpenSslError)?; let chain = certs_der_to_x509(chain_der)?; for c in chain { - certs.push(c).map_err(Error::OpenSslError)?; + cert_chain.push(c).map_err(Error::OpenSslError)?; } let cert = openssl::x509::X509::from_der(cert_der).map_err(Error::OpenSslError)?; @@ -287,7 +287,7 @@ pub(crate) fn verify_trust( // finalize store let store = builder.build(); - match store_ctx.init(&store, cert.as_ref(), &certs, |f| f.verify_cert()) { + match store_ctx.init(&store, cert.as_ref(), &cert_chain, |f| f.verify_cert()) { Ok(trust) => Ok(trust), Err(_) => Ok(false), } diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs index f27c13903..bea78b330 100644 --- a/sdk/src/openssl/temp_signer_async.rs +++ b/sdk/src/openssl/temp_signer_async.rs @@ -23,6 +23,7 @@ use c2pa_crypto::SigningAlg; use crate::AsyncSigner; +use crate::Result; #[cfg(feature = "openssl_sign")] fn get_local_signer(alg: SigningAlg) -> Box { @@ -71,7 +72,7 @@ impl AsyncSignerAdapter { #[cfg(test)] #[async_trait::async_trait] impl AsyncSigner for AsyncSignerAdapter { - async fn sign(&self, data: Vec) -> crate::Result> { + async fn sign(&self, data: Vec) -> Result> { let signer = get_local_signer(self.alg); signer.sign(&data) } diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index fe3260336..3cf367da6 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -14,7 +14,7 @@ use async_trait::async_trait; use c2pa_crypto::{raw_signature::RawSigner, SigningAlg}; -use crate::{DynamicAssertion, Result}; +use crate::{DynamicAssertion, Error, Result}; /// The `Signer` trait generates a cryptographic signature over a byte array. /// @@ -106,9 +106,7 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { pkey_path: P, alg: SigningAlg, tsa_url: Option, - ) -> crate::Result { - use crate::Error; - + ) -> Result { let signcert = std::fs::read(signcert_path).map_err(Error::IoError)?; let pkey = std::fs::read(pkey_path).map_err(Error::IoError)?; @@ -121,7 +119,7 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { pkey: &[u8], alg: SigningAlg, tsa_url: Option, - ) -> crate::Result; + ) -> Result; } /// The `AsyncSigner` trait generates a cryptographic signature over a byte array. @@ -291,7 +289,7 @@ pub trait RemoteSigner: Sync { /// The size of returned `Vec` must match the value returned by `reserve_size`. /// This data will be embedded in the JUMBF `c2pa.signature` box of the manifest. /// `data` are the bytes of the claim to be remotely signed. - async fn sign_remote(&self, data: &[u8]) -> crate::Result>; + async fn sign_remote(&self, data: &[u8]) -> Result>; /// Returns the size in bytes of the largest possible expected signature. /// @@ -301,7 +299,7 @@ pub trait RemoteSigner: Sync { } impl Signer for Box { - fn sign(&self, data: &[u8]) -> crate::Result> { + fn sign(&self, data: &[u8]) -> Result> { (**self).sign(data) } @@ -309,7 +307,7 @@ impl Signer for Box { (**self).alg() } - fn certs(&self) -> crate::Result>> { + fn certs(&self) -> Result>> { (**self).certs() } @@ -350,7 +348,7 @@ impl Signer for Box { pub(crate) struct RawSignerWrapper(pub(crate) Box); impl Signer for RawSignerWrapper { - fn sign(&self, data: &[u8]) -> crate::Result> { + fn sign(&self, data: &[u8]) -> Result> { self.0.sign(data).map_err(|e| e.into()) } @@ -358,7 +356,7 @@ impl Signer for RawSignerWrapper { self.0.alg() } - fn certs(&self) -> crate::Result>> { + fn certs(&self) -> Result>> { self.0.cert_chain().map_err(|e| e.into()) } @@ -378,13 +376,13 @@ impl Signer for RawSignerWrapper { self.0.time_stamp_request_headers() } - fn timestamp_request_body(&self, message: &[u8]) -> crate::Result> { + fn timestamp_request_body(&self, message: &[u8]) -> Result> { self.0 .time_stamp_request_body(message) .map_err(|e| e.into()) } - fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + fn send_timestamp_request(&self, message: &[u8]) -> Option>> { self.0 .send_time_stamp_request(message) .map(|r| r.map_err(|e| e.into())) diff --git a/sdk/src/utils/sig_utils.rs b/sdk/src/utils/sig_utils.rs index d1a669a2f..4bd60ceb7 100644 --- a/sdk/src/utils/sig_utils.rs +++ b/sdk/src/utils/sig_utils.rs @@ -13,13 +13,13 @@ use c2pa_crypto::{p1363::parse_ec_der_sig, raw_signature::RawSignerError, SigningAlg}; -use crate::Error; +use crate::{Error, Result}; -pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> crate::Result> { +pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result> { // P1363 format: r | s let (_, p) = parse_ec_der_sig(data) - .map_err(|err| RawSignerError::InvalidSigningCredentials(err.to_string()))?; + .map_err(|_err| Error::InvalidEcdsaSignature)?; let mut r = extfmt::Hexlify(p.r).to_string(); let mut s = extfmt::Hexlify(p.s).to_string(); @@ -28,7 +28,7 @@ pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> crate::Result 64, SigningAlg::Es384 => 96, SigningAlg::Es512 => 132, - _ => return Err(Error::BadParam("unexpected signing alg".to_string())), + _ => return Err(Error::UnsupportedType), }; // pad or truncate as needed @@ -57,7 +57,7 @@ pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> crate::Result crate::Result CallbackSigner { .set_context("test" as *const _ as *const ()) } -fn ed_sign(data: &[u8], private_key: &[u8]) -> crate::Result> { +fn ed_sign(data: &[u8], private_key: &[u8]) -> c2pa::Result> { use ed25519_dalek::{Signature, Signer, SigningKey}; use pem::parse; // Parse the PEM data to get the private key - let pem = parse(private_key).map_err(|e| RawSignerError::InternalError(e.to_string()))?; + let pem = parse(private_key).map_err(|e| c2pa::Error::OtherError(Box::new(e)))?; // For Ed25519, the key is 32 bytes long, so we skip the first 16 bytes of the PEM data let key_bytes = &pem.contents()[16..]; From 00ad08e6c6b7d3c219567f2ba830cb9cf545f8ca Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 16:02:17 -0800 Subject: [PATCH 15/32] Remove ConfigurableSigner from c2pa_crypto --- internal/crypto/src/raw_signature/signer.rs | 29 --------------------- 1 file changed, 29 deletions(-) diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index 24b5c13ab..92cffa1eb 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -151,35 +151,6 @@ impl From for RawSignerError { } } -/// This trait exists to allow the built-in [`RawSigner`] implementations to be -/// configured from a private/public key pair. -#[allow(dead_code)] // TEMPORARY while refactoring -pub(crate) trait ConfigurableSigner: RawSigner + Sized { - fn from_cert_chain_and_private_key( - cert_chain: &[u8], - private_key: &[u8], - alg: SigningAlg, - time_stamp_service_url: Option, - ) -> Result; - - fn from_files>( - cert_chain_path: P, - private_key_path: P, - alg: SigningAlg, - time_stamp_service_url: Option, - ) -> Result { - let cert_chain = std::fs::read(cert_chain_path)?; - let private_key = std::fs::read(private_key_path)?; - - Self::from_cert_chain_and_private_key( - &cert_chain, - &private_key, - alg, - time_stamp_service_url, - ) - } -} - /// Return a built-in [`RawSigner`] instance using the provided signing /// certificate and private key. /// From f07ff4d850719653dc047411dfed045ff15df784 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 16:52:29 -0800 Subject: [PATCH 16/32] Move EcDSA signer into c2pa_crypto --- internal/crypto/Cargo.toml | 1 + .../src/openssl/signers/ecdsa_signer.rs | 140 +++++++++++++++ internal/crypto/src/openssl/signers/mod.rs | 10 ++ .../crypto/src/openssl/signers/rsa_signer.rs | 17 +- internal/crypto/src/p1363.rs | 57 ++++++ .../tests/fixtures/raw_signature/es256.priv | 5 + .../tests/fixtures/raw_signature/es384.priv | 6 + .../tests/fixtures/raw_signature/es512.priv | 8 + .../src/tests/openssl/signers/ecdsa_signer.rs | 92 ++++++++++ .../crypto/src/tests/openssl/signers/mod.rs | 1 + .../src/tests/openssl/signers/rsa_signer.rs | 13 ++ sdk/src/callback_signer.rs | 4 +- sdk/src/cose_sign.rs | 60 ++++++- sdk/src/create_signer.rs | 28 +-- sdk/src/openssl/ec_signer.rs | 166 ------------------ sdk/src/openssl/mod.rs | 5 - sdk/src/openssl/temp_signer.rs | 9 +- sdk/src/openssl/temp_signer_async.rs | 3 +- sdk/src/utils/mod.rs | 1 - sdk/src/utils/sig_utils.rs | 75 -------- 20 files changed, 421 insertions(+), 280 deletions(-) create mode 100644 internal/crypto/src/openssl/signers/ecdsa_signer.rs create mode 100644 internal/crypto/src/tests/fixtures/raw_signature/es256.priv create mode 100644 internal/crypto/src/tests/fixtures/raw_signature/es384.priv create mode 100644 internal/crypto/src/tests/fixtures/raw_signature/es512.priv create mode 100644 internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs delete mode 100644 sdk/src/openssl/ec_signer.rs delete mode 100644 sdk/src/utils/sig_utils.rs diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 01acc663c..2cf5d8759 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -38,6 +38,7 @@ bcder = "0.7.3" bytes = "1.7.2" c2pa-status-tracker = { path = "../status-tracker", version = "0.1.0" } ciborium = "0.2.2" +const-hex = "1.14" coset = "0.3.1" getrandom = { version = "0.2.7", features = ["js"] } hex = "0.4.3" diff --git a/internal/crypto/src/openssl/signers/ecdsa_signer.rs b/internal/crypto/src/openssl/signers/ecdsa_signer.rs new file mode 100644 index 000000000..aa35635e4 --- /dev/null +++ b/internal/crypto/src/openssl/signers/ecdsa_signer.rs @@ -0,0 +1,140 @@ +// 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 openssl::{ + ec::EcKey, + hash::MessageDigest, + pkey::{PKey, Private}, + sign::Signer, + x509::X509, +}; + +use crate::{ + openssl::{cert_chain::check_chain_order, OpenSslMutex}, + p1363::der_to_p1363, + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, + SigningAlg, +}; + +enum EcdsaSigningAlg { + Es256, + Es384, + Es512, +} + +/// Implements `Signer` trait using OpenSSL's implementation of +/// ECDSA encryption. +pub struct EcdsaSigner { + alg: EcdsaSigningAlg, + + cert_chain: Vec, + cert_chain_len: usize, + + private_key: EcKey, + + time_stamp_service_url: Option, + time_stamp_size: usize, +} + +impl EcdsaSigner { + pub(crate) fn from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + alg: SigningAlg, + time_stamp_service_url: Option, + ) -> Result { + let alg = match alg { + SigningAlg::Es256 => EcdsaSigningAlg::Es256, + SigningAlg::Es384 => EcdsaSigningAlg::Es384, + SigningAlg::Es512 => EcdsaSigningAlg::Es512, + _ => { + return Err(RawSignerError::InternalError( + "EcdsaSigner should be used only for SigningAlg::Es***".to_string(), + )); + } + }; + + let _openssl = OpenSslMutex::acquire()?; + + let cert_chain = X509::stack_from_pem(cert_chain)?; + let cert_chain_len = cert_chain.len(); + + if !check_chain_order(&cert_chain) { + return Err(RawSignerError::InvalidSigningCredentials( + "certificate chain in incorrect order".to_string(), + )); + } + + let private_key = EcKey::private_key_from_pem(private_key)?; + + Ok(EcdsaSigner { + alg, + cert_chain, + cert_chain_len, + private_key, + time_stamp_service_url, + time_stamp_size: 10000, + // TO DO: Call out to time stamp service to get actual time stamp and use that size? + }) + } +} + +impl RawSigner for EcdsaSigner { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { + let _openssl = OpenSslMutex::acquire()?; + + let private_key = PKey::from_ec_key(self.private_key.clone())?; + + let mut signer = match self.alg { + EcdsaSigningAlg::Es256 => Signer::new(MessageDigest::sha256(), &private_key)?, + EcdsaSigningAlg::Es384 => Signer::new(MessageDigest::sha384(), &private_key)?, + EcdsaSigningAlg::Es512 => Signer::new(MessageDigest::sha512(), &private_key)?, + }; + + signer.update(data)?; + + let der_sig = signer.sign_to_vec()?; + der_to_p1363(&der_sig, self.alg()) + } + + fn alg(&self) -> SigningAlg { + match self.alg { + EcdsaSigningAlg::Es256 => SigningAlg::Es256, + EcdsaSigningAlg::Es384 => SigningAlg::Es384, + EcdsaSigningAlg::Es512 => SigningAlg::Es512, + } + } + + fn reserve_size(&self) -> usize { + 1024 + self.cert_chain_len + self.time_stamp_size + } + + fn cert_chain(&self) -> Result>, RawSignerError> { + let _openssl = OpenSslMutex::acquire()?; + + self.cert_chain + .iter() + .map(|cert| { + cert.to_der() + .map_err(|e| RawSignerError::OpenSslError(e.to_string())) + }) + .collect() + } +} + +impl TimeStampProvider for EcdsaSigner { + fn time_stamp_service_url(&self) -> Option { + self.time_stamp_service_url.clone() + } +} diff --git a/internal/crypto/src/openssl/signers/mod.rs b/internal/crypto/src/openssl/signers/mod.rs index 138b1600e..8efca4072 100644 --- a/internal/crypto/src/openssl/signers/mod.rs +++ b/internal/crypto/src/openssl/signers/mod.rs @@ -19,6 +19,7 @@ use crate::{ SigningAlg, }; +mod ecdsa_signer; mod rsa_signer; /// Return a built-in [`RawSigner`] instance using the provided signing @@ -36,6 +37,15 @@ pub(crate) fn signer_from_cert_chain_and_private_key( time_stamp_service_url: Option, ) -> Result, RawSignerError> { match alg { + SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Ok(Box::new( + ecdsa_signer::EcdsaSigner::from_cert_chain_and_private_key( + cert_chain, + private_key, + alg, + time_stamp_service_url, + )?, + )), + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Ok(Box::new( rsa_signer::RsaSigner::from_cert_chain_and_private_key( cert_chain, diff --git a/internal/crypto/src/openssl/signers/rsa_signer.rs b/internal/crypto/src/openssl/signers/rsa_signer.rs index 3e5a00044..7f3d4775f 100644 --- a/internal/crypto/src/openssl/signers/rsa_signer.rs +++ b/internal/crypto/src/openssl/signers/rsa_signer.rs @@ -175,19 +175,20 @@ impl RsaSigner { // don't make this pub or pub(crate) without finding a way to ensure that // precondition. - let mut certs: Vec> = Vec::new(); - - for c in &self.cert_chain { - let cert = c.to_der()?; - certs.push(cert); - } - - Ok(certs) + self.cert_chain + .iter() + .map(|cert| { + cert.to_der() + .map_err(|e| RawSignerError::OpenSslError(e.to_string())) + }) + .collect() } } impl RawSigner for RsaSigner { fn sign(&self, data: &[u8]) -> Result, RawSignerError> { + let _openssl = OpenSslMutex::acquire()?; + let mut signer = match self.alg { RsaSigningAlg::Ps256 => { let mut signer = Signer::new(MessageDigest::sha256(), &self.private_key)?; diff --git a/internal/crypto/src/p1363.rs b/internal/crypto/src/p1363.rs index 4f806d10a..5c0c676e4 100644 --- a/internal/crypto/src/p1363.rs +++ b/internal/crypto/src/p1363.rs @@ -19,6 +19,8 @@ use x509_parser::der_parser::{ error::BerResult, }; +use crate::{raw_signature::RawSignerError, SigningAlg}; + /// Parse an ASN.1 DER object that contains a P1363 format into its components. /// /// This format is used by C2PA to describe ECDSA signature keys. @@ -43,3 +45,58 @@ pub struct EcSigComps<'a> { pub r: &'a [u8], pub s: &'a [u8], } + +pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result, RawSignerError> { + // P1363 format: r | s + + let (_, p) = parse_ec_der_sig(data) + .map_err(|err| RawSignerError::InternalError(format!("invalid DER signature: {err}")))?; + + let mut r = const_hex::encode(p.r); + let mut s = const_hex::encode(p.s); + + let sig_len: usize = match alg { + SigningAlg::Es256 => 64, + SigningAlg::Es384 => 96, + SigningAlg::Es512 => 132, + _ => { + return Err(RawSignerError::InternalError( + "unsupported algorithm for der_to_p1363".to_string(), + )) + } + }; + + // Pad or truncate as needed. + let rp = if r.len() > sig_len { + let offset = r.len() - sig_len; + &r[offset..r.len()] + } else { + while r.len() != sig_len { + r.insert(0, '0'); + } + r.as_ref() + }; + + let sp = if s.len() > sig_len { + let offset = s.len() - sig_len; + &s[offset..s.len()] + } else { + while s.len() != sig_len { + s.insert(0, '0'); + } + s.as_ref() + }; + + if rp.len() != sig_len || rp.len() != sp.len() { + return Err(RawSignerError::InternalError( + "invalid signature components".to_string(), + )); + } + + // Merge r and s strings. + let new_sig = format!("{rp}{sp}"); + + // Convert back from hex string to byte array. + const_hex::decode(&new_sig) + .map_err(|e| RawSignerError::InternalError(format!("invalid signature components {e}"))) +} diff --git a/internal/crypto/src/tests/fixtures/raw_signature/es256.priv b/internal/crypto/src/tests/fixtures/raw_signature/es256.priv new file mode 100644 index 000000000..5e59fcc5e --- /dev/null +++ b/internal/crypto/src/tests/fixtures/raw_signature/es256.priv @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgfNJBsaRLSeHizv0m +GL+gcn78QmtfLSm+n+qG9veC2W2hRANCAAQPaL6RkAkYkKU4+IryBSYxJM3h77sF +iMrbvbI8fG7w2Bbl9otNG/cch3DAw5rGAPV7NWkyl3QGuV/wt0MrAPDo +-----END PRIVATE KEY----- diff --git a/internal/crypto/src/tests/fixtures/raw_signature/es384.priv b/internal/crypto/src/tests/fixtures/raw_signature/es384.priv new file mode 100644 index 000000000..9dcc3af1c --- /dev/null +++ b/internal/crypto/src/tests/fixtures/raw_signature/es384.priv @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDClh93nMdKl9Ujg71e3 +95QK1Z7OnmHrAYRv9B4ujlgWBoLQVtkoamQdizWQI/GvpAuhZANiAATY7kzZpbiB +azyKpJ3gb/6TZPMaNfJ+P5YeyhCynvzQtQT5l3HnJosPxUWYKTkdwqBWwe9P7QKl +KyX4PpQXVkJXYHffOGYOOKhLkJbc3JpYZzrxlZzY2FiVa1NQ/wOPXiU= +-----END PRIVATE KEY----- diff --git a/internal/crypto/src/tests/fixtures/raw_signature/es512.priv b/internal/crypto/src/tests/fixtures/raw_signature/es512.priv new file mode 100644 index 000000000..33cb2ad84 --- /dev/null +++ b/internal/crypto/src/tests/fixtures/raw_signature/es512.priv @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBDCrjJaQ9kS2QVZHr +AoJMiCyk3YtvecNK913LsZ5alTlq01W13cEWVl3nprbCrCVzANiHWLjqUzfVW2oN +wT0rW5qhgYkDgYYABAHbhWCA1ywh5K74+Al75qoC2FLcqEmIJQoU4lNpy5lsPU59 +3LNjzmVUY8VTK8bK9SLnqhZC0sdijlqitmSOJg/VLwB9uYVTnwBmize3WEQmlEyz +nYJ0XdmCZYJcB4ke1aQKpHw1jNhHGSxEO9xn7+50v1EzEwRa0yM934mF3UxCV2c5 +nw== +-----END PRIVATE KEY----- diff --git a/internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs b/internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs new file mode 100644 index 000000000..a8bba0514 --- /dev/null +++ b/internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs @@ -0,0 +1,92 @@ +// Copyright 2024 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 openssl::x509::X509; + +use crate::{ + openssl::{signers::signer_from_cert_chain_and_private_key, validators::EcdsaValidator}, + raw_signature::RawSignatureValidator, + SigningAlg, +}; + +#[test] +fn es256() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/es256.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/es256.priv"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Es256, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + EcdsaValidator::Es256 + .validate(&signature, data, &pub_key) + .unwrap(); +} + +#[test] +fn es384() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/es384.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/es384.priv"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Es384, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + EcdsaValidator::Es384 + .validate(&signature, data, &pub_key) + .unwrap(); +} + +#[test] +fn es512() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/es512.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/es512.priv"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Es512, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + EcdsaValidator::Es512 + .validate(&signature, data, &pub_key) + .unwrap(); +} diff --git a/internal/crypto/src/tests/openssl/signers/mod.rs b/internal/crypto/src/tests/openssl/signers/mod.rs index f776d7460..6dafca373 100644 --- a/internal/crypto/src/tests/openssl/signers/mod.rs +++ b/internal/crypto/src/tests/openssl/signers/mod.rs @@ -11,4 +11,5 @@ // specific language governing permissions and limitations under // each license. +mod ecdsa_signer; mod rsa_signer; diff --git a/internal/crypto/src/tests/openssl/signers/rsa_signer.rs b/internal/crypto/src/tests/openssl/signers/rsa_signer.rs index c51db3960..c35840996 100644 --- a/internal/crypto/src/tests/openssl/signers/rsa_signer.rs +++ b/internal/crypto/src/tests/openssl/signers/rsa_signer.rs @@ -1,3 +1,16 @@ +// Copyright 2024 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 openssl::x509::X509; use crate::{ diff --git a/sdk/src/callback_signer.rs b/sdk/src/callback_signer.rs index 5c8df0e47..096249034 100644 --- a/sdk/src/callback_signer.rs +++ b/sdk/src/callback_signer.rs @@ -17,7 +17,7 @@ //! using a callback and public signing certificates. use async_trait::async_trait; -use c2pa_crypto::{raw_signature::RawSignerError, SigningAlg}; +use c2pa_crypto::SigningAlg; use crate::{AsyncSigner, Error, Result, Signer}; @@ -117,7 +117,7 @@ impl CallbackSigner { // For Ed25519, the key is 32 bytes long, so we skip the first 16 bytes of the PEM data let key_bytes = &pem.contents()[16..]; let signing_key = - SigningKey::try_from(key_bytes).map_err(|e| Error::OtherError(Box::new(e)))?; + SigningKey::try_from(key_bytes).map_err(|e| Error::OtherError(Box::new(e)))?; // Sign the data let signature: Signature = signing_key.sign(data); diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 0ae88e4d1..a69186a68 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -35,7 +35,6 @@ use crate::{ cose_timestamp_countersign, cose_timestamp_countersign_async, make_cose_timestamp, }, trust_handler::TrustHandlerConfig, - utils::sig_utils::der_to_p1363, AsyncSigner, Error, Result, Signer, }; @@ -206,6 +205,63 @@ pub(crate) fn cose_sign(signer: &dyn Signer, data: &[u8], box_size: usize) -> Re Ok(c2pa_sig_data) } +fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result> { + // P1363 format: r | s + + let (_, p) = parse_ec_der_sig(data).map_err(|_err| Error::InvalidEcdsaSignature)?; + + let mut r = extfmt::Hexlify(p.r).to_string(); + let mut s = extfmt::Hexlify(p.s).to_string(); + + let sig_len: usize = match alg { + SigningAlg::Es256 => 64, + SigningAlg::Es384 => 96, + SigningAlg::Es512 => 132, + _ => return Err(Error::UnsupportedType), + }; + + // 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 Err(Error::InvalidEcdsaSignature); + } + + // 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 + (0..new_sig.len()) + .step_by(2) + .map(|i| { + u8::from_str_radix(&new_sig[i..i + 2], 16).map_err(|_err| Error::InvalidEcdsaSignature) + }) + .collect() +} + #[async_generic(async_signature(signer: &dyn AsyncSigner, data: &[u8], alg: SigningAlg))] fn build_headers(signer: &dyn Signer, data: &[u8], alg: SigningAlg) -> Result<(Header, Header)> { let mut protected_h = match alg { @@ -359,7 +415,7 @@ mod tests { #![allow(clippy::unwrap_used)] use super::sign_claim; - use crate::{claim::Claim, utils::test::temp_signer, Signer, Result}; + use crate::{claim::Claim, utils::test::temp_signer, Result, Signer}; #[test] fn test_sign_claim() { diff --git a/sdk/src/create_signer.rs b/sdk/src/create_signer.rs index 9b2767d2c..83910e8e7 100644 --- a/sdk/src/create_signer.rs +++ b/sdk/src/create_signer.rs @@ -22,7 +22,7 @@ use c2pa_crypto::{raw_signature::signer_from_cert_chain_and_private_key, Signing use crate::{ error::Result, - openssl::{EcSigner, EdSigner}, + openssl::EdSigner, signer::{ConfigurableSigner, RawSignerWrapper}, Signer, }; @@ -46,13 +46,14 @@ pub fn from_keys( tsa_url: Option, ) -> Result> { Ok(match alg { - SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Box::new(RawSignerWrapper( - signer_from_cert_chain_and_private_key(signcert, pkey, alg, tsa_url)?, - )), - - SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Box::new( - EcSigner::from_signcert_and_pkey(signcert, pkey, alg, tsa_url)?, - ), + SigningAlg::Es256 + | SigningAlg::Es384 + | SigningAlg::Es512 + | SigningAlg::Ps256 + | SigningAlg::Ps384 + | SigningAlg::Ps512 => Box::new(RawSignerWrapper(signer_from_cert_chain_and_private_key( + signcert, pkey, alg, tsa_url, + )?)), SigningAlg::Ed25519 => Box::new(EdSigner::from_signcert_and_pkey( signcert, pkey, alg, tsa_url, @@ -77,7 +78,12 @@ pub fn from_files>( tsa_url: Option, ) -> Result> { Ok(match alg { - SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => { + SigningAlg::Es256 + | SigningAlg::Es384 + | SigningAlg::Es512 + | SigningAlg::Ps256 + | SigningAlg::Ps384 + | SigningAlg::Ps512 => { let cert_chain = std::fs::read(signcert_path)?; let private_key = std::fs::read(pkey_path)?; @@ -89,10 +95,6 @@ pub fn from_files>( )?)) } - SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Box::new( - EcSigner::from_files(&signcert_path, &pkey_path, alg, tsa_url)?, - ), - SigningAlg::Ed25519 => Box::new(EdSigner::from_files( &signcert_path, &pkey_path, diff --git a/sdk/src/openssl/ec_signer.rs b/sdk/src/openssl/ec_signer.rs deleted file mode 100644 index 51ecbe5a6..000000000 --- a/sdk/src/openssl/ec_signer.rs +++ /dev/null @@ -1,166 +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 c2pa_crypto::{openssl::OpenSslMutex, SigningAlg}; -use openssl::{ - ec::EcKey, - hash::MessageDigest, - pkey::{PKey, Private}, - x509::X509, -}; - -use super::check_chain_order; -use crate::{error::Error, signer::ConfigurableSigner, utils::sig_utils::der_to_p1363, Signer}; - -/// Implements `Signer` trait using OpenSSL's implementation of -/// ECDSA encryption. -pub struct EcSigner { - signcerts: Vec, - pkey: EcKey, - - certs_size: usize, - timestamp_size: usize, - - alg: SigningAlg, - tsa_url: Option, -} - -impl ConfigurableSigner for EcSigner { - fn from_signcert_and_pkey( - signcert: &[u8], - pkey: &[u8], - alg: SigningAlg, - tsa_url: Option, - ) -> crate::Result { - let _openssl = OpenSslMutex::acquire()?; - - let certs_size = signcert.len(); - let pkey = EcKey::private_key_from_pem(pkey).map_err(Error::OpenSslError)?; - let signcerts = X509::stack_from_pem(signcert).map_err(Error::OpenSslError)?; - - // make sure cert chains are in order - if !check_chain_order(&signcerts) { - return Err(Error::BadParam( - "certificate chain is not in correct order".to_string(), - )); - } - - Ok(EcSigner { - signcerts, - pkey, - certs_size, - timestamp_size: 10000, /* todo: call out to TSA to get actual timestamp and use that size */ - alg, - tsa_url, - }) - } -} - -impl Signer for EcSigner { - fn sign(&self, data: &[u8]) -> crate::Result> { - let _openssl = OpenSslMutex::acquire()?; - - let key = PKey::from_ec_key(self.pkey.clone()).map_err(Error::OpenSslError)?; - - let mut signer = match self.alg { - SigningAlg::Es256 => openssl::sign::Signer::new(MessageDigest::sha256(), &key)?, - SigningAlg::Es384 => openssl::sign::Signer::new(MessageDigest::sha384(), &key)?, - SigningAlg::Es512 => openssl::sign::Signer::new(MessageDigest::sha512(), &key)?, - _ => return Err(Error::UnsupportedType), - }; - - signer.update(data).map_err(Error::OpenSslError)?; - let der_sig = signer.sign_to_vec().map_err(Error::OpenSslError)?; - - der_to_p1363(&der_sig, self.alg) - } - - fn alg(&self) -> SigningAlg { - self.alg - } - - fn certs(&self) -> crate::Result>> { - let _openssl = OpenSslMutex::acquire()?; - - let mut certs: Vec> = Vec::new(); - - for c in &self.signcerts { - let cert = c.to_der().map_err(Error::OpenSslError)?; - certs.push(cert); - } - - Ok(certs) - } - - fn time_authority_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 - } -} - -#[cfg(test)] -#[cfg(feature = "file_io")] -mod tests { - #![allow(clippy::unwrap_used)] - - use super::*; - use crate::{openssl::temp_signer, utils::test::fixture_path}; - - #[test] - fn es256_signer() { - let cert_dir = fixture_path("certs"); - - let (signer, _) = temp_signer::get_ec_signer(cert_dir, SigningAlg::Es256, None); - - let data = b"some sample content to sign"; - println!("data len = {}", data.len()); - - let signature = signer.sign(data).unwrap(); - println!("signature.len = {}", signature.len()); - assert!(signature.len() >= 64); - assert!(signature.len() <= signer.reserve_size()); - } - - #[test] - fn es384_signer() { - let cert_dir = fixture_path("certs"); - - let (signer, _) = temp_signer::get_ec_signer(cert_dir, SigningAlg::Es384, None); - - let data = b"some sample content to sign"; - println!("data len = {}", data.len()); - - let signature = signer.sign(data).unwrap(); - println!("signature.len = {}", signature.len()); - assert!(signature.len() >= 64); - assert!(signature.len() <= signer.reserve_size()); - } - - #[test] - fn es512_signer() { - let cert_dir = fixture_path("certs"); - - let (signer, _) = temp_signer::get_ec_signer(cert_dir, SigningAlg::Es512, None); - - let data = b"some sample content to sign"; - println!("data len = {}", data.len()); - - let signature = signer.sign(data).unwrap(); - println!("signature.len = {}", signature.len()); - assert!(signature.len() >= 64); - assert!(signature.len() <= signer.reserve_size()); - } -} diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs index c4b9c21e2..a661f8f75 100644 --- a/sdk/src/openssl/mod.rs +++ b/sdk/src/openssl/mod.rs @@ -11,11 +11,6 @@ // specific language governing permissions and limitations under // each license. -#[cfg(feature = "openssl_sign")] -mod ec_signer; -#[cfg(feature = "openssl_sign")] -pub(crate) use ec_signer::EcSigner; - #[cfg(feature = "openssl_sign")] mod ed_signer; #[cfg(feature = "openssl_sign")] diff --git a/sdk/src/openssl/temp_signer.rs b/sdk/src/openssl/temp_signer.rs index 840f402ac..9ea416b09 100644 --- a/sdk/src/openssl/temp_signer.rs +++ b/sdk/src/openssl/temp_signer.rs @@ -38,10 +38,7 @@ use std::path::{Path, PathBuf}; use c2pa_crypto::SigningAlg; #[cfg(feature = "file_io")] -use crate::{ - openssl::{EcSigner, EdSigner}, - signer::ConfigurableSigner, -}; +use crate::{openssl::EdSigner, signer::ConfigurableSigner}; /// Create an OpenSSL ES256 signer that can be used for testing purposes. /// @@ -66,7 +63,7 @@ pub fn get_ec_signer>( path: P, alg: SigningAlg, tsa_url: Option, -) -> (EcSigner, PathBuf) { +) -> (Box, PathBuf) { match alg { SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => (), _ => { @@ -83,7 +80,7 @@ pub fn get_ec_signer>( pem_key_path.set_extension("pem"); ( - EcSigner::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), + crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), sign_cert_path, ) } diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs index bea78b330..2e0df3a5a 100644 --- a/sdk/src/openssl/temp_signer_async.rs +++ b/sdk/src/openssl/temp_signer_async.rs @@ -22,8 +22,7 @@ #[cfg(feature = "openssl_sign")] use c2pa_crypto::SigningAlg; -use crate::AsyncSigner; -use crate::Result; +use crate::{AsyncSigner, Result}; #[cfg(feature = "openssl_sign")] fn get_local_signer(alg: SigningAlg) -> Box { diff --git a/sdk/src/utils/mod.rs b/sdk/src/utils/mod.rs index e63b3a037..89e5ef375 100644 --- a/sdk/src/utils/mod.rs +++ b/sdk/src/utils/mod.rs @@ -19,7 +19,6 @@ pub(crate) mod merkle; pub(crate) mod mime; #[allow(dead_code)] // for wasm build pub(crate) mod patch; -pub(crate) mod sig_utils; #[cfg(feature = "add_thumbnails")] pub(crate) mod thumbnail; pub(crate) mod time_it; diff --git a/sdk/src/utils/sig_utils.rs b/sdk/src/utils/sig_utils.rs deleted file mode 100644 index 4bd60ceb7..000000000 --- a/sdk/src/utils/sig_utils.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2024 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 c2pa_crypto::{p1363::parse_ec_der_sig, raw_signature::RawSignerError, SigningAlg}; - -use crate::{Error, Result}; - -pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result> { - // P1363 format: r | s - - let (_, p) = parse_ec_der_sig(data) - .map_err(|_err| Error::InvalidEcdsaSignature)?; - - let mut r = extfmt::Hexlify(p.r).to_string(); - let mut s = extfmt::Hexlify(p.s).to_string(); - - let sig_len: usize = match alg { - SigningAlg::Es256 => 64, - SigningAlg::Es384 => 96, - SigningAlg::Es512 => 132, - _ => return Err(Error::UnsupportedType), - }; - - // 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 Err(Error::InvalidEcdsaSignature); - } - - // 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 - (0..new_sig.len()) - .step_by(2) - .map(|i| { - u8::from_str_radix(&new_sig[i..i + 2], 16) - .map_err(|_err| Error::InvalidEcdsaSignature) - }) - .collect() -} From 8031df0ebe8d07516f17c595efac75c23b27f7df Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 17:05:10 -0800 Subject: [PATCH 17/32] Fix WASM build issues --- internal/crypto/src/p1363.rs | 6 ++++-- sdk/src/signer.rs | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/crypto/src/p1363.rs b/internal/crypto/src/p1363.rs index 5c0c676e4..473f24e56 100644 --- a/internal/crypto/src/p1363.rs +++ b/internal/crypto/src/p1363.rs @@ -19,8 +19,6 @@ use x509_parser::der_parser::{ error::BerResult, }; -use crate::{raw_signature::RawSignerError, SigningAlg}; - /// Parse an ASN.1 DER object that contains a P1363 format into its components. /// /// This format is used by C2PA to describe ECDSA signature keys. @@ -46,6 +44,10 @@ pub struct EcSigComps<'a> { pub s: &'a [u8], } +#[cfg(not(target_arch = "wasm32"))] // Maye will be used later? +use crate::{raw_signature::RawSignerError, SigningAlg}; + +#[cfg(not(target_arch = "wasm32"))] // Maye will be used later? pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result, RawSignerError> { // P1363 format: r | s diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 3cf367da6..972c28cdf 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -14,7 +14,7 @@ use async_trait::async_trait; use c2pa_crypto::{raw_signature::RawSigner, SigningAlg}; -use crate::{DynamicAssertion, Error, Result}; +use crate::{DynamicAssertion, Result}; /// The `Signer` trait generates a cryptographic signature over a byte array. /// @@ -107,8 +107,8 @@ pub(crate) trait ConfigurableSigner: Signer + Sized { alg: SigningAlg, tsa_url: Option, ) -> Result { - let signcert = std::fs::read(signcert_path).map_err(Error::IoError)?; - let pkey = std::fs::read(pkey_path).map_err(Error::IoError)?; + let signcert = std::fs::read(signcert_path).map_err(crate::Error::IoError)?; + let pkey = std::fs::read(pkey_path).map_err(crate::Error::IoError)?; Self::from_signcert_and_pkey(&signcert, &pkey, alg, tsa_url) } From 4da05ed7883f855f7f1f1eb939dd5c3b1470c528 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 18:16:29 -0800 Subject: [PATCH 18/32] Move Ed25519 signer into c2pa_crypto --- .../src/openssl/signers/ed25519_signer.rs | 105 ++++++++++++++ internal/crypto/src/openssl/signers/mod.rs | 13 +- .../tests/fixtures/raw_signature/ed25519.priv | 3 + sdk/src/create_signer.rs | 51 +------ sdk/src/openssl/ed_signer.rs | 128 ------------------ sdk/src/openssl/mod.rs | 64 --------- sdk/src/openssl/temp_signer.rs | 7 +- 7 files changed, 126 insertions(+), 245 deletions(-) create mode 100644 internal/crypto/src/openssl/signers/ed25519_signer.rs create mode 100644 internal/crypto/src/tests/fixtures/raw_signature/ed25519.priv delete mode 100644 sdk/src/openssl/ed_signer.rs diff --git a/internal/crypto/src/openssl/signers/ed25519_signer.rs b/internal/crypto/src/openssl/signers/ed25519_signer.rs new file mode 100644 index 000000000..63e6f574f --- /dev/null +++ b/internal/crypto/src/openssl/signers/ed25519_signer.rs @@ -0,0 +1,105 @@ +// 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 openssl::{ + pkey::{PKey, Private}, + sign::Signer, + x509::X509, +}; + +use crate::{ + openssl::{cert_chain::check_chain_order, OpenSslMutex}, + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, + SigningAlg, +}; + +/// Implements `RawSigner` trait using OpenSSL's implementation of +/// Edwards Curve encryption. +pub struct Ed25519Signer { + cert_chain: Vec, + cert_chain_len: usize, + + private_key: PKey, + + time_stamp_service_url: Option, + time_stamp_size: usize, +} + +impl Ed25519Signer { + pub(crate) fn from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + time_stamp_service_url: Option, + ) -> Result { + let _openssl = OpenSslMutex::acquire()?; + + let cert_chain = X509::stack_from_pem(cert_chain)?; + let cert_chain_len = cert_chain.len(); + + if !check_chain_order(&cert_chain) { + return Err(RawSignerError::InvalidSigningCredentials( + "certificate chain in incorrect order".to_string(), + )); + } + + let private_key = PKey::private_key_from_pem(private_key)?; + + Ok(Ed25519Signer { + cert_chain, + cert_chain_len, + + private_key, + + time_stamp_service_url, + time_stamp_size: 10000, + // TO DO: Call out to time stamp service to get actual time stamp and use that size? + }) + } +} + +impl RawSigner for Ed25519Signer { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { + let _openssl = OpenSslMutex::acquire()?; + + let mut signer = Signer::new_without_digest(&self.private_key)?; + + Ok(signer.sign_oneshot_to_vec(data)?) + } + + fn alg(&self) -> SigningAlg { + SigningAlg::Ed25519 + } + + fn reserve_size(&self) -> usize { + 1024 + self.cert_chain_len + self.time_stamp_size + } + + fn cert_chain(&self) -> Result>, RawSignerError> { + let _openssl = OpenSslMutex::acquire()?; + + self.cert_chain + .iter() + .map(|cert| { + cert.to_der() + .map_err(|e| RawSignerError::OpenSslError(e.to_string())) + }) + .collect() + } +} + +impl TimeStampProvider for Ed25519Signer { + fn time_stamp_service_url(&self) -> Option { + self.time_stamp_service_url.clone() + } +} diff --git a/internal/crypto/src/openssl/signers/mod.rs b/internal/crypto/src/openssl/signers/mod.rs index 8efca4072..8f47a075a 100644 --- a/internal/crypto/src/openssl/signers/mod.rs +++ b/internal/crypto/src/openssl/signers/mod.rs @@ -20,6 +20,7 @@ use crate::{ }; mod ecdsa_signer; +mod ed25519_signer; mod rsa_signer; /// Return a built-in [`RawSigner`] instance using the provided signing @@ -46,6 +47,14 @@ pub(crate) fn signer_from_cert_chain_and_private_key( )?, )), + SigningAlg::Ed25519 => Ok(Box::new( + ed25519_signer::Ed25519Signer::from_cert_chain_and_private_key( + cert_chain, + private_key, + time_stamp_service_url, + )?, + )), + SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => Ok(Box::new( rsa_signer::RsaSigner::from_cert_chain_and_private_key( cert_chain, @@ -54,9 +63,5 @@ pub(crate) fn signer_from_cert_chain_and_private_key( time_stamp_service_url, )?, )), - - _ => Err(RawSignerError::InvalidSigningCredentials( - "unsupported algorithm".to_string(), - )), } } diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ed25519.priv b/internal/crypto/src/tests/fixtures/raw_signature/ed25519.priv new file mode 100644 index 000000000..fda14a840 --- /dev/null +++ b/internal/crypto/src/tests/fixtures/raw_signature/ed25519.priv @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIL2+9INLPNSLH3STzKQJ3Wen9R6uPbIYOIKA2574YQ4O +-----END PRIVATE KEY----- diff --git a/sdk/src/create_signer.rs b/sdk/src/create_signer.rs index 83910e8e7..67fb1887f 100644 --- a/sdk/src/create_signer.rs +++ b/sdk/src/create_signer.rs @@ -20,12 +20,7 @@ use std::path::Path; use c2pa_crypto::{raw_signature::signer_from_cert_chain_and_private_key, SigningAlg}; -use crate::{ - error::Result, - openssl::EdSigner, - signer::{ConfigurableSigner, RawSignerWrapper}, - Signer, -}; +use crate::{error::Result, signer::RawSignerWrapper, Signer}; /// Creates a [`Signer`] instance using signing certificate and private key /// as byte slices. @@ -45,20 +40,9 @@ pub fn from_keys( alg: SigningAlg, tsa_url: Option, ) -> Result> { - Ok(match alg { - SigningAlg::Es256 - | SigningAlg::Es384 - | SigningAlg::Es512 - | SigningAlg::Ps256 - | SigningAlg::Ps384 - | SigningAlg::Ps512 => Box::new(RawSignerWrapper(signer_from_cert_chain_and_private_key( - signcert, pkey, alg, tsa_url, - )?)), - - SigningAlg::Ed25519 => Box::new(EdSigner::from_signcert_and_pkey( - signcert, pkey, alg, tsa_url, - )?), - }) + Ok(Box::new(RawSignerWrapper( + signer_from_cert_chain_and_private_key(signcert, pkey, alg, tsa_url)?, + ))) } /// Creates a [`Signer`] instance using signing certificate and @@ -77,29 +61,8 @@ pub fn from_files>( alg: SigningAlg, tsa_url: Option, ) -> Result> { - Ok(match alg { - SigningAlg::Es256 - | SigningAlg::Es384 - | SigningAlg::Es512 - | SigningAlg::Ps256 - | SigningAlg::Ps384 - | SigningAlg::Ps512 => { - let cert_chain = std::fs::read(signcert_path)?; - let private_key = std::fs::read(pkey_path)?; - - Box::new(RawSignerWrapper(signer_from_cert_chain_and_private_key( - &cert_chain, - &private_key, - alg, - tsa_url, - )?)) - } + let cert_chain = std::fs::read(signcert_path)?; + let private_key = std::fs::read(pkey_path)?; - SigningAlg::Ed25519 => Box::new(EdSigner::from_files( - &signcert_path, - &pkey_path, - alg, - tsa_url, - )?), - }) + from_keys(&cert_chain, &private_key, alg, tsa_url) } diff --git a/sdk/src/openssl/ed_signer.rs b/sdk/src/openssl/ed_signer.rs deleted file mode 100644 index efcc54e2c..000000000 --- a/sdk/src/openssl/ed_signer.rs +++ /dev/null @@ -1,128 +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 c2pa_crypto::{openssl::OpenSslMutex, SigningAlg}; -use openssl::{ - pkey::{PKey, Private}, - x509::X509, -}; - -use super::check_chain_order; -use crate::{signer::ConfigurableSigner, Error, Signer}; - -/// Implements `Signer` trait using OpenSSL's implementation of -/// Edwards Curve encryption. -pub struct EdSigner { - signcerts: Vec, - pkey: PKey, - - certs_size: usize, - timestamp_size: usize, - - alg: SigningAlg, - tsa_url: Option, -} - -impl ConfigurableSigner for EdSigner { - fn from_signcert_and_pkey( - signcert: &[u8], - pkey: &[u8], - alg: SigningAlg, - tsa_url: Option, - ) -> crate::Result { - let _openssl = OpenSslMutex::acquire()?; - - let certs_size = signcert.len(); - let signcerts = X509::stack_from_pem(signcert).map_err(Error::OpenSslError)?; - let pkey = PKey::private_key_from_pem(pkey).map_err(Error::OpenSslError)?; - - if alg != SigningAlg::Ed25519 { - return Err(Error::UnsupportedType); // only ed25519 is supported by C2PA - } - - // make sure cert chains are in order - if !check_chain_order(&signcerts) { - return Err(Error::BadParam( - "certificate chain is not in correct order".to_string(), - )); - } - - Ok(EdSigner { - signcerts, - pkey, - certs_size, - timestamp_size: 10000, /* todo: call out to TSA to get actual timestamp and use that size */ - alg, - tsa_url, - }) - } -} - -impl Signer for EdSigner { - fn sign(&self, data: &[u8]) -> crate::Result> { - let _openssl = OpenSslMutex::acquire()?; - - let mut signer = openssl::sign::Signer::new_without_digest(&self.pkey)?; - - let signed_data = signer.sign_oneshot_to_vec(data)?; - Ok(signed_data) - } - - fn alg(&self) -> SigningAlg { - self.alg - } - - fn certs(&self) -> crate::Result>> { - let _openssl = OpenSslMutex::acquire()?; - - let mut certs: Vec> = Vec::new(); - - for c in &self.signcerts { - let cert = c.to_der()?; - certs.push(cert); - } - - Ok(certs) - } - - fn time_authority_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 - } -} - -#[cfg(test)] -#[cfg(feature = "file_io")] -mod tests { - #![allow(clippy::unwrap_used)] - use super::*; - use crate::{openssl::temp_signer, utils::test::fixture_path}; - - #[test] - fn ed25519_signer() { - let cert_dir = fixture_path("certs"); - - let (signer, _) = temp_signer::get_ed_signer(cert_dir, SigningAlg::Ed25519, None); - - let data = b"some sample content to sign"; - println!("data len = {}", data.len()); - - let signature = signer.sign(data).unwrap(); - println!("signature.len = {}", signature.len()); - assert!(signature.len() >= 64); - assert!(signature.len() <= signer.reserve_size()); - } -} diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs index a661f8f75..e2f797d5d 100644 --- a/sdk/src/openssl/mod.rs +++ b/sdk/src/openssl/mod.rs @@ -11,11 +11,6 @@ // specific language governing permissions and limitations under // each license. -#[cfg(feature = "openssl_sign")] -mod ed_signer; -#[cfg(feature = "openssl_sign")] -pub(crate) use ed_signer::EdSigner; - #[cfg(feature = "openssl")] mod openssl_trust_handler; #[cfg(test)] @@ -29,66 +24,7 @@ pub(crate) use openssl_trust_handler::OpenSSLTrustHandlerConfig; #[cfg(test)] pub(crate) mod temp_signer_async; -#[cfg(feature = "openssl")] -use openssl::x509::X509; #[cfg(test)] #[allow(unused_imports)] #[cfg(feature = "openssl")] pub(crate) use temp_signer_async::AsyncSignerAdapter; - -#[cfg(feature = "openssl")] -fn check_chain_order(certs: &[X509]) -> bool { - // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please - // don't make this pub or pub(crate) without finding a way to ensure that - // precondition. - - { - if certs.len() > 1 { - for (i, c) in certs.iter().enumerate() { - if let Some(next_c) = certs.get(i + 1) { - if let Ok(pkey) = next_c.public_key() { - if let Ok(verified) = c.verify(&pkey) { - if !verified { - return false; - } - } else { - return false; - } - } else { - return false; - } - } - } - } - true - } -} - -#[cfg(not(feature = "openssl"))] -fn check_chain_order(certs: &[X509]) -> bool { - true -} - -#[cfg(feature = "openssl")] -#[allow(dead_code)] -fn check_chain_order_der(cert_ders: &[Vec]) -> bool { - // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please - // don't make this pub or pub(crate) without finding a way to ensure that - // precondition. - - let mut certs: Vec = Vec::new(); - for cert_der in cert_ders { - if let Ok(cert) = X509::from_der(cert_der) { - certs.push(cert); - } else { - return false; - } - } - - check_chain_order(&certs) -} - -#[cfg(not(feature = "openssl"))] -fn check_chain_order_der(cert_ders: &[Vec]) -> bool { - true -} diff --git a/sdk/src/openssl/temp_signer.rs b/sdk/src/openssl/temp_signer.rs index 9ea416b09..1646f3e6b 100644 --- a/sdk/src/openssl/temp_signer.rs +++ b/sdk/src/openssl/temp_signer.rs @@ -37,9 +37,6 @@ use std::path::{Path, PathBuf}; #[cfg(feature = "file_io")] use c2pa_crypto::SigningAlg; -#[cfg(feature = "file_io")] -use crate::{openssl::EdSigner, signer::ConfigurableSigner}; - /// Create an OpenSSL ES256 signer that can be used for testing purposes. /// /// # Arguments @@ -108,7 +105,7 @@ pub fn get_ed_signer>( path: P, alg: SigningAlg, tsa_url: Option, -) -> (EdSigner, PathBuf) { +) -> (Box, PathBuf) { if alg != SigningAlg::Ed25519 { panic!("Unknown ED signer alg {alg:#?}"); } @@ -122,7 +119,7 @@ pub fn get_ed_signer>( pem_key_path.set_extension("pem"); ( - EdSigner::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), + crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), sign_cert_path, ) } From 5da65eb054ae4b97f855d8ab39cab6239d1805ea Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 19:58:23 -0800 Subject: [PATCH 19/32] Remove one use of temp_signer --- sdk/src/cose_validator.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index d2bde5361..dc1b0406c 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -1394,39 +1394,31 @@ pub mod tests { #[test] #[cfg(feature = "openssl_sign")] fn test_cert_algorithms() { - let cert_dir = crate::utils::test::fixture_path("certs"); let th = crate::openssl::OpenSSLTrustHandlerConfig::new(); let mut validation_log = DetailedStatusTracker::default(); - let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let es256_cert = std::fs::read(cert_path).unwrap(); + let es256_cert = include_bytes!("../tests/fixtures/certs/es256.pub"); + let es384_cert = include_bytes!("../tests/fixtures/certs/es384.pub"); + let es512_cert = include_bytes!("../tests/fixtures/certs/es512.pub"); + let rsa_pss256_cert = include_bytes!("../tests/fixtures/certs/ps256.pub"); - let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let es384_cert = std::fs::read(cert_path).unwrap(); - - let (_, cert_path) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let es512_cert = std::fs::read(cert_path).unwrap(); - - let (_, cert_path) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let rsa_pss256_cert = std::fs::read(cert_path).unwrap(); - - if let Ok(signcert) = openssl::x509::X509::from_pem(&es256_cert) { + if let Ok(signcert) = openssl::x509::X509::from_pem(es256_cert) { let der_bytes = signcert.to_der().unwrap(); assert!(check_cert(&der_bytes, &th, &mut validation_log, None).is_ok()); } - if let Ok(signcert) = openssl::x509::X509::from_pem(&es384_cert) { + if let Ok(signcert) = openssl::x509::X509::from_pem(es384_cert) { let der_bytes = signcert.to_der().unwrap(); assert!(check_cert(&der_bytes, &th, &mut validation_log, None).is_ok()); } - if let Ok(signcert) = openssl::x509::X509::from_pem(&es512_cert) { + if let Ok(signcert) = openssl::x509::X509::from_pem(es512_cert) { let der_bytes = signcert.to_der().unwrap(); assert!(check_cert(&der_bytes, &th, &mut validation_log, None).is_ok()); } - if let Ok(signcert) = openssl::x509::X509::from_pem(&rsa_pss256_cert) { + if let Ok(signcert) = openssl::x509::X509::from_pem(rsa_pss256_cert) { let der_bytes = signcert.to_der().unwrap(); assert!(check_cert(&der_bytes, &th, &mut validation_log, None).is_ok()); } From 2032e43862df83ac8980bae8716f528827140a3c Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 20:03:55 -0800 Subject: [PATCH 20/32] Simplify temp_signer API now that we don't need path any more --- sdk/src/openssl/openssl_trust_handler.rs | 56 ++++++++++++------------ sdk/src/openssl/temp_signer.rs | 23 +++------- sdk/src/openssl/temp_signer_async.rs | 6 +-- 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/sdk/src/openssl/openssl_trust_handler.rs b/sdk/src/openssl/openssl_trust_handler.rs index 9d5ae56b9..00a65d256 100644 --- a/sdk/src/openssl/openssl_trust_handler.rs +++ b/sdk/src/openssl/openssl_trust_handler.rs @@ -317,13 +317,13 @@ pub mod tests { th.load_default_trust().unwrap(); // test all the certs - let (ps256, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let (ps384, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let (ps512, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let (es256, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let (es384, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); + let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); + let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); + let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); + let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); + let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); + let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); @@ -355,13 +355,13 @@ pub mod tests { th.load_trust_anchors_from_data(&mut reader).unwrap(); // test all the certs - let (ps256, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let (ps384, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let (ps512, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let (es256, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let (es384, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); + let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); + let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); + let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); + let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); + let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); + let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); @@ -397,13 +397,13 @@ pub mod tests { th.load_allowed_list(&mut allowed_list).unwrap(); // test all the certs - let (ps256, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let (ps384, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let (ps512, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let (es256, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let (es384, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); + let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); + let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); + let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); + let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); + let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); + let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); @@ -438,13 +438,13 @@ pub mod tests { th.load_allowed_list(&mut allowed_list).unwrap(); // test all the certs - let (ps256, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let (ps384, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let (ps512, _) = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let (es256, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let (es384, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let (es512, _) = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let (ed25519, _) = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); + let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); + let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); + let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); + let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); + let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); + let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); diff --git a/sdk/src/openssl/temp_signer.rs b/sdk/src/openssl/temp_signer.rs index 1646f3e6b..d6a554455 100644 --- a/sdk/src/openssl/temp_signer.rs +++ b/sdk/src/openssl/temp_signer.rs @@ -32,7 +32,7 @@ #![allow(clippy::unwrap_used)] #[cfg(feature = "file_io")] -use std::path::{Path, PathBuf}; +use std::path::Path; #[cfg(feature = "file_io")] use c2pa_crypto::SigningAlg; @@ -60,7 +60,7 @@ pub fn get_ec_signer>( path: P, alg: SigningAlg, tsa_url: Option, -) -> (Box, PathBuf) { +) -> Box { match alg { SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => (), _ => { @@ -76,10 +76,7 @@ pub fn get_ec_signer>( pem_key_path.push(alg.to_string()); pem_key_path.set_extension("pem"); - ( - crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), - sign_cert_path, - ) + crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap() } /// Create an OpenSSL ES256 signer that can be used for testing purposes. @@ -105,7 +102,7 @@ pub fn get_ed_signer>( path: P, alg: SigningAlg, tsa_url: Option, -) -> (Box, PathBuf) { +) -> Box { if alg != SigningAlg::Ed25519 { panic!("Unknown ED signer alg {alg:#?}"); } @@ -118,10 +115,7 @@ pub fn get_ed_signer>( pem_key_path.push(alg.to_string()); pem_key_path.set_extension("pem"); - ( - crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), - sign_cert_path, - ) + crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap() } /// Create an OpenSSL SHA+RSA signer that can be used for testing purposes. @@ -147,7 +141,7 @@ pub fn get_rsa_signer>( path: P, alg: SigningAlg, tsa_url: Option, -) -> (Box, PathBuf) { +) -> Box { match alg { SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => (), _ => { @@ -171,8 +165,5 @@ pub fn get_rsa_signer>( ); } - ( - crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap(), - sign_cert_path, - ) + crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap() } diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs index 2e0df3a5a..233e4d02d 100644 --- a/sdk/src/openssl/temp_signer_async.rs +++ b/sdk/src/openssl/temp_signer_async.rs @@ -30,15 +30,15 @@ fn get_local_signer(alg: SigningAlg) -> Box { match alg { SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => { - let (s, _k) = super::temp_signer::get_rsa_signer(&cert_dir, alg, None); + let s = super::temp_signer::get_rsa_signer(&cert_dir, alg, None); s } SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => { - let (s, _k) = super::temp_signer::get_ec_signer(&cert_dir, alg, None); + let s = super::temp_signer::get_ec_signer(&cert_dir, alg, None); Box::new(s) } SigningAlg::Ed25519 => { - let (s, _k) = super::temp_signer::get_ed_signer(&cert_dir, alg, None); + let s = super::temp_signer::get_ed_signer(&cert_dir, alg, None); Box::new(s) } } From 1cd4d5dedcf8a849726d1d7be13b34afd18da01d Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 20:27:03 -0800 Subject: [PATCH 21/32] Remove psxxx_ocsp_stapling_experimental; conflicts with upcoming AsyncRawSignerAdapter --- docs/usage.md | 1 - internal/crypto/Cargo.toml | 1 - .../crypto/src/openssl/signers/rsa_signer.rs | 95 +++---------------- sdk/Cargo.toml | 1 - 4 files changed, 11 insertions(+), 87 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 417857ed5..54ce6bfc0 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -42,7 +42,6 @@ The Rust library crate provides the following capabilities: * `no_interleaved_io` forces fully-synchronous I/O; otherwise, the library uses threaded I/O for some operations to improve performance. * `fetch_remote_manifests` enables the verification step to retrieve externally referenced manifest stores. External manifests are only fetched if there is no embedded manifest store and no locally adjacent .c2pa manifest store file of the same name. * `json_schema` is used by `make schema` to produce a JSON schema document that represents the `ManifestStore` data structures. -* `psxxx_ocsp_stapling_experimental` enables a demonstration feature that attempts to fetch the OCSP data from the OCSP responders listed in the manifest signing certificate. The response becomes part of the manifest and is used to prove the certificate was not revoked at the time of signing. This is only implemented for PS256, PS384 and PS512 signatures and is intended as a demonstration. * `openssl_ffi_mutex` prevents multiple threads from accessing the C OpenSSL library simultaneously. (This library is not re-entrant.) In a multi-threaded process (such as Cargo's test runner), this can lead to unpredictable behavior. ### New API diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 2cf5d8759..37ae9c915 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -28,7 +28,6 @@ rustdoc-args = ["--cfg", "docsrs"] [features] json_schema = ["dep:schemars"] openssl = ["dep:openssl"] -psxxx_ocsp_stapling_experimental = ["dep:openssl"] [dependencies] async-generic = "1.1" diff --git a/internal/crypto/src/openssl/signers/rsa_signer.rs b/internal/crypto/src/openssl/signers/rsa_signer.rs index 7f3d4775f..98e58cd26 100644 --- a/internal/crypto/src/openssl/signers/rsa_signer.rs +++ b/internal/crypto/src/openssl/signers/rsa_signer.rs @@ -11,8 +11,6 @@ // specific language governing permissions and limitations under // each license. -use std::cell::Cell; - use openssl::{ hash::MessageDigest, pkey::{PKey, Private}, @@ -22,7 +20,6 @@ use openssl::{ }; use crate::{ - ocsp::OcspResponse, openssl::{cert_chain::check_chain_order, OpenSslMutex}, raw_signature::{RawSigner, RawSignerError}, time_stamp::TimeStampProvider, @@ -47,9 +44,6 @@ pub(crate) struct RsaSigner { time_stamp_service_url: Option, time_stamp_size: usize, - - ocsp_size: Cell, - ocsp_response: Cell, } impl RsaSigner { @@ -115,7 +109,7 @@ impl RsaSigner { } }; - let signer = RsaSigner { + Ok(RsaSigner { alg, cert_chain, private_key, @@ -123,65 +117,7 @@ impl RsaSigner { time_stamp_service_url, time_stamp_size: 10000, // TO DO: Call out to time stamp service to get actual time stamp and use that size? - ocsp_size: Cell::new(0), - ocsp_response: Cell::new(OcspResponse::default()), - }; - - // Acquire OCSP response if possible. - signer.update_ocsp(); - - Ok(signer) - } - - // Sample of OCSP stapling while signing. This code is only for demo purposes - // and not for production use since there is no caching in the SDK and fetching - // is expensive. This is behind the feature flag - // 'psxxx_ocsp_stapling_experimental'. - fn update_ocsp(&self) { - // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please - // don't make this pub or pub(crate) without finding a way to ensure that - // precondition. - - // Is it time for an OCSP update? - - #[cfg(feature = "psxxx_ocsp_stapling_experimental")] - { - let ocsp_data = self.ocsp_response.take(); - let next_update = ocsp_data.next_update; - self.ocsp_response.set(ocsp_data); - - let now = chrono::offset::Utc::now(); - if now > next_update { - if let Ok(certs) = self.certs_internal() { - if let Some(ocsp_rsp) = crate::ocsp::fetch_ocsp_response(&certs) { - self.ocsp_size.set(ocsp_rsp.len()); - - let mut validation_log = - c2pa_status_tracker::DetailedStatusTracker::default(); - - if let Ok(ocsp_response) = - OcspResponse::from_der_checked(&ocsp_rsp, None, &mut validation_log) - { - self.ocsp_response.set(ocsp_response); - } - } - } - } - } - } - - fn certs_internal(&self) -> Result>, RawSignerError> { - // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please - // don't make this pub or pub(crate) without finding a way to ensure that - // precondition. - - self.cert_chain - .iter() - .map(|cert| { - cert.to_der() - .map_err(|e| RawSignerError::OpenSslError(e.to_string())) - }) - .collect() + }) } } @@ -219,13 +155,19 @@ impl RawSigner for RsaSigner { } fn reserve_size(&self) -> usize { - 1024 + self.cert_chain_len + self.time_stamp_size + self.ocsp_size.get() - // The Cose_Sign1 contains complete certs, timestamps, and OCSP. + 1024 + self.cert_chain_len + self.time_stamp_size } fn cert_chain(&self) -> Result>, RawSignerError> { let _openssl = OpenSslMutex::acquire()?; - self.certs_internal() + + self.cert_chain + .iter() + .map(|cert| { + cert.to_der() + .map_err(|e| RawSignerError::OpenSslError(e.to_string())) + }) + .collect() } fn alg(&self) -> SigningAlg { @@ -235,21 +177,6 @@ impl RawSigner for RsaSigner { RsaSigningAlg::Ps512 => SigningAlg::Ps512, } } - - fn ocsp_response(&self) -> Option> { - let _openssl = OpenSslMutex::acquire().ok()?; - - self.update_ocsp(); - - let ocsp_data = self.ocsp_response.take(); - let ocsp_response = ocsp_data.ocsp_der.clone(); - self.ocsp_response.set(ocsp_data); - if !ocsp_response.is_empty() { - Some(ocsp_response) - } else { - None - } - } } impl TimeStampProvider for RsaSigner { diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index a5f2f490a..324ea93e2 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -28,7 +28,6 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["v1_api"] add_thumbnails = ["image"] -psxxx_ocsp_stapling_experimental = [] file_io = ["openssl_sign"] serialize_thumbnails = [] no_interleaved_io = ["file_io"] From 0210d178f5d4e484739ecdf6140267245632060b Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 20:42:23 -0800 Subject: [PATCH 22/32] Add test coverage for ed25519 signer --- .../tests/openssl/signers/ed25519_signer.rs | 44 +++++++++++++++++++ .../crypto/src/tests/openssl/signers/mod.rs | 1 + 2 files changed, 45 insertions(+) create mode 100644 internal/crypto/src/tests/openssl/signers/ed25519_signer.rs diff --git a/internal/crypto/src/tests/openssl/signers/ed25519_signer.rs b/internal/crypto/src/tests/openssl/signers/ed25519_signer.rs new file mode 100644 index 000000000..8c59cd198 --- /dev/null +++ b/internal/crypto/src/tests/openssl/signers/ed25519_signer.rs @@ -0,0 +1,44 @@ +// Copyright 2024 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 openssl::x509::X509; + +use crate::{ + openssl::{signers::signer_from_cert_chain_and_private_key, validators::Ed25519Validator}, + raw_signature::RawSignatureValidator, + SigningAlg, +}; + +#[test] +fn ed25519() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ed25519.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ed25519.priv"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Ed25519, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + Ed25519Validator {} + .validate(&signature, data, &pub_key) + .unwrap(); +} diff --git a/internal/crypto/src/tests/openssl/signers/mod.rs b/internal/crypto/src/tests/openssl/signers/mod.rs index 6dafca373..c6d556b09 100644 --- a/internal/crypto/src/tests/openssl/signers/mod.rs +++ b/internal/crypto/src/tests/openssl/signers/mod.rs @@ -12,4 +12,5 @@ // each license. mod ecdsa_signer; +mod ed25519_signer; mod rsa_signer; From e9b8ecdf328c8b750c0059c1e9fa414a7f9a2799 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 20:52:13 -0800 Subject: [PATCH 23/32] Add ability to request async signers --- internal/crypto/Cargo.toml | 3 + internal/crypto/src/openssl/signers/mod.rs | 2 +- internal/crypto/src/raw_signature/mod.rs | 3 +- internal/crypto/src/raw_signature/signer.rs | 81 ++++++++++++++++- .../src/tests/openssl/signers/ecdsa_signer.rs | 86 ++++++++++++++++++- .../tests/openssl/signers/ed25519_signer.rs | 30 ++++++- .../src/tests/openssl/signers/rsa_signer.rs | 86 ++++++++++++++++++- 7 files changed, 284 insertions(+), 7 deletions(-) diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 37ae9c915..791ddd230 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -96,5 +96,8 @@ web-time = "1.1" getrandom = { version = "0.2.7", features = ["js"] } js-sys = "0.3.58" +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +actix = "0.13.1" + [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.31" diff --git a/internal/crypto/src/openssl/signers/mod.rs b/internal/crypto/src/openssl/signers/mod.rs index 8f47a075a..585a12880 100644 --- a/internal/crypto/src/openssl/signers/mod.rs +++ b/internal/crypto/src/openssl/signers/mod.rs @@ -36,7 +36,7 @@ pub(crate) fn signer_from_cert_chain_and_private_key( private_key: &[u8], alg: SigningAlg, time_stamp_service_url: Option, -) -> Result, RawSignerError> { +) -> Result, RawSignerError> { match alg { SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Ok(Box::new( ecdsa_signer::EcdsaSigner::from_cert_chain_and_private_key( diff --git a/internal/crypto/src/raw_signature/mod.rs b/internal/crypto/src/raw_signature/mod.rs index 7381e906f..b5a54e86e 100644 --- a/internal/crypto/src/raw_signature/mod.rs +++ b/internal/crypto/src/raw_signature/mod.rs @@ -15,7 +15,8 @@ pub(crate) mod signer; pub use signer::{ - signer_from_cert_chain_and_private_key, AsyncRawSigner, RawSigner, RawSignerError, + async_signer_from_cert_chain_and_private_key, signer_from_cert_chain_and_private_key, + AsyncRawSigner, RawSigner, RawSignerError, }; pub(crate) mod oids; diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index 92cffa1eb..bcece923b 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -15,7 +15,7 @@ use async_trait::async_trait; use thiserror::Error; use crate::{ - time_stamp::{AsyncTimeStampProvider, TimeStampProvider}, + time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider}, SigningAlg, }; @@ -165,7 +165,7 @@ pub fn signer_from_cert_chain_and_private_key( private_key: &[u8], alg: SigningAlg, time_stamp_service_url: Option, -) -> Result, RawSignerError> { +) -> Result, RawSignerError> { #[cfg(feature = "openssl")] { return crate::openssl::signers::signer_from_cert_chain_and_private_key( @@ -188,3 +188,80 @@ pub fn signer_from_cert_chain_and_private_key( "unsupported algorithm: {alg}" ))) } + +/// Return a built-in [`AsyncRawSigner`] instance using the provided signing +/// certificate and private key. +/// +/// Which signers are available may vary depending on the platform and which +/// crate features were enabled. +/// +/// Returns `None` if the signing algorithm is unsupported. May return an `Err` +/// response if the certificate chain or private key are invalid. +#[allow(unused)] // arguments may or may not be used depending on crate features +pub fn async_signer_from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + alg: SigningAlg, + time_stamp_service_url: Option, +) -> Result, RawSignerError> { + // TO DO: Preferentially use WASM-based signers, some of which are necessarily + // async. + + let sync_signer = signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + alg, + time_stamp_service_url, + )?; + + Ok(Box::new(AsyncRawSignerWrapper(sync_signer))) +} + +struct AsyncRawSignerWrapper(Box); + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl AsyncRawSigner for AsyncRawSignerWrapper { + async fn sign(&self, data: Vec) -> Result, RawSignerError> { + self.0.sign(&data) + } + + fn alg(&self) -> SigningAlg { + self.0.alg() + } + + fn cert_chain(&self) -> Result>, RawSignerError> { + self.0.cert_chain() + } + + fn reserve_size(&self) -> usize { + self.0.reserve_size() + } + + async fn ocsp_response(&self) -> Option> { + self.0.ocsp_response() + } +} + +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +impl AsyncTimeStampProvider for AsyncRawSignerWrapper { + fn time_stamp_service_url(&self) -> Option { + self.0.time_stamp_service_url() + } + + fn time_stamp_request_headers(&self) -> Option> { + self.0.time_stamp_request_headers() + } + + fn time_stamp_request_body(&self, message: &[u8]) -> Result, TimeStampError> { + 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) + } +} diff --git a/internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs b/internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs index a8bba0514..858418deb 100644 --- a/internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs +++ b/internal/crypto/src/tests/openssl/signers/ecdsa_signer.rs @@ -15,7 +15,7 @@ use openssl::x509::X509; use crate::{ openssl::{signers::signer_from_cert_chain_and_private_key, validators::EcdsaValidator}, - raw_signature::RawSignatureValidator, + raw_signature::{async_signer_from_cert_chain_and_private_key, RawSignatureValidator}, SigningAlg, }; @@ -43,6 +43,34 @@ fn es256() { .unwrap(); } +#[actix::test] +async fn es256_async() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/es256.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/es256.priv"); + + let signer = async_signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + SigningAlg::Es256, + None, + ) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data.to_vec()).await.unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + EcdsaValidator::Es256 + .validate(&signature, data, &pub_key) + .unwrap(); +} + #[test] fn es384() { let cert_chain = include_bytes!("../../fixtures/raw_signature/es384.pub"); @@ -67,6 +95,34 @@ fn es384() { .unwrap(); } +#[actix::test] +async fn es384_async() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/es384.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/es384.priv"); + + let signer = async_signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + SigningAlg::Es384, + None, + ) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data.to_vec()).await.unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + EcdsaValidator::Es384 + .validate(&signature, data, &pub_key) + .unwrap(); +} + #[test] fn es512() { let cert_chain = include_bytes!("../../fixtures/raw_signature/es512.pub"); @@ -90,3 +146,31 @@ fn es512() { .validate(&signature, data, &pub_key) .unwrap(); } + +#[actix::test] +async fn es512_async() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/es512.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/es512.priv"); + + let signer = async_signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + SigningAlg::Es512, + None, + ) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data.to_vec()).await.unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + EcdsaValidator::Es512 + .validate(&signature, data, &pub_key) + .unwrap(); +} diff --git a/internal/crypto/src/tests/openssl/signers/ed25519_signer.rs b/internal/crypto/src/tests/openssl/signers/ed25519_signer.rs index 8c59cd198..d7a2de19f 100644 --- a/internal/crypto/src/tests/openssl/signers/ed25519_signer.rs +++ b/internal/crypto/src/tests/openssl/signers/ed25519_signer.rs @@ -15,7 +15,7 @@ use openssl::x509::X509; use crate::{ openssl::{signers::signer_from_cert_chain_and_private_key, validators::Ed25519Validator}, - raw_signature::RawSignatureValidator, + raw_signature::{async_signer_from_cert_chain_and_private_key, RawSignatureValidator}, SigningAlg, }; @@ -42,3 +42,31 @@ fn ed25519() { .validate(&signature, data, &pub_key) .unwrap(); } + +#[actix::test] +async fn ed25519_async() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ed25519.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ed25519.priv"); + + let signer = async_signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + SigningAlg::Ed25519, + None, + ) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data.to_vec()).await.unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + Ed25519Validator {} + .validate(&signature, data, &pub_key) + .unwrap(); +} diff --git a/internal/crypto/src/tests/openssl/signers/rsa_signer.rs b/internal/crypto/src/tests/openssl/signers/rsa_signer.rs index c35840996..74ae59f35 100644 --- a/internal/crypto/src/tests/openssl/signers/rsa_signer.rs +++ b/internal/crypto/src/tests/openssl/signers/rsa_signer.rs @@ -15,7 +15,7 @@ use openssl::x509::X509; use crate::{ openssl::{signers::signer_from_cert_chain_and_private_key, validators::RsaValidator}, - raw_signature::RawSignatureValidator, + raw_signature::{async_signer_from_cert_chain_and_private_key, RawSignatureValidator}, SigningAlg, }; @@ -43,6 +43,34 @@ fn ps256() { .unwrap(); } +#[actix::test] +async fn ps256_async() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ps256.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ps256.priv"); + + let signer = async_signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + SigningAlg::Ps256, + None, + ) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data.to_vec()).await.unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + RsaValidator::Ps256 + .validate(&signature, data, &pub_key) + .unwrap(); +} + #[test] fn ps384() { let cert_chain = include_bytes!("../../fixtures/raw_signature/ps384.pub"); @@ -67,6 +95,34 @@ fn ps384() { .unwrap(); } +#[actix::test] +async fn ps384_async() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ps384.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ps384.priv"); + + let signer = async_signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + SigningAlg::Ps384, + None, + ) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data.to_vec()).await.unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + RsaValidator::Ps384 + .validate(&signature, data, &pub_key) + .unwrap(); +} + #[test] fn ps512() { let cert_chain = include_bytes!("../../fixtures/raw_signature/ps512.pub"); @@ -90,3 +146,31 @@ fn ps512() { .validate(&signature, data, &pub_key) .unwrap(); } + +#[actix::test] +async fn ps512_async() { + let cert_chain = include_bytes!("../../fixtures/raw_signature/ps512.pub"); + let private_key = include_bytes!("../../fixtures/raw_signature/ps512.priv"); + + let signer = async_signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + SigningAlg::Ps512, + None, + ) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data.to_vec()).await.unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let cert = X509::from_pem(cert_chain).unwrap(); + let pub_key = cert.public_key().unwrap(); + let pub_key = pub_key.public_key_to_der().unwrap(); + + RsaValidator::Ps512 + .validate(&signature, data, &pub_key) + .unwrap(); +} From 86b5901e3a68ba9ff5c01d58a61a8d7fc628cc84 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Fri, 6 Dec 2024 21:14:19 -0800 Subject: [PATCH 24/32] Start replacing temp_signer with test_signer --- sdk/src/builder.rs | 20 ++++++----- sdk/src/utils/mod.rs | 3 ++ sdk/src/utils/test_signer.rs | 64 ++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 sdk/src/utils/test_signer.rs diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index beff52879..efbf23e1a 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1083,6 +1083,7 @@ mod tests { #![allow(clippy::unwrap_used)] use std::io::Cursor; + use c2pa_crypto::SigningAlg; use serde_json::json; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -1092,7 +1093,10 @@ mod tests { assertions::BoxHash, asset_handlers::jpeg_io::JpegIO, hash_stream_by_alg, - utils::test::{temp_signer, write_jpeg_placeholder_stream}, + utils::{ + test::{temp_signer, write_jpeg_placeholder_stream}, + test_signer::test_signer, + }, Reader, }; @@ -1305,7 +1309,7 @@ mod tests { let mut _builder = Builder::from_archive(&mut zipped).unwrap(); // sign and write to the output stream - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); builder .sign(signer.as_ref(), format, &mut source, &mut dest) .unwrap(); @@ -1337,7 +1341,7 @@ mod tests { .unwrap(); // sign and write to the output stream - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); builder.sign_file(signer.as_ref(), source, &dest).unwrap(); // read and validate the signed manifest store @@ -1389,7 +1393,7 @@ mod tests { .unwrap(); // sign and write to the output stream - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); builder .sign(signer.as_ref(), format, &mut source, &mut dest) .unwrap(); @@ -1472,7 +1476,7 @@ mod tests { .unwrap(); // sign the ManifestStoreBuilder and write it to the output stream - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let manifest_data = builder .sign(signer.as_ref(), "image/jpeg", &mut source, &mut dest) .unwrap(); @@ -1496,7 +1500,7 @@ mod tests { const CLOUD_IMAGE: &[u8] = include_bytes!("../tests/fixtures/cloud.jpg"); let mut input_stream = Cursor::new(CLOUD_IMAGE); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut builder = Builder::from_json(&simple_manifest()).unwrap(); @@ -1633,7 +1637,7 @@ mod tests { let mut builder = Builder::from_archive(&mut zipped).unwrap(); // sign the ManifestStoreBuilder and write it to the output stream - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let _manifest_data = builder .sign(signer.as_ref(), "image/jpeg", &mut source, &mut dest) .unwrap(); @@ -1809,7 +1813,7 @@ mod tests { // convert buffer to cursor with Read/Write/Seek capability let mut input = Cursor::new(image.to_vec()); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Embed a manifest using the signer. let mut output = Cursor::new(Vec::new()); builder diff --git a/sdk/src/utils/mod.rs b/sdk/src/utils/mod.rs index 89e5ef375..5c2887a31 100644 --- a/sdk/src/utils/mod.rs +++ b/sdk/src/utils/mod.rs @@ -28,3 +28,6 @@ pub(crate) mod xmp_inmemory_utils; #[cfg(test)] #[allow(dead_code)] // for wasm build pub mod test; + +#[cfg(test)] +pub(crate) mod test_signer; diff --git a/sdk/src/utils/test_signer.rs b/sdk/src/utils/test_signer.rs new file mode 100644 index 000000000..b8693b277 --- /dev/null +++ b/sdk/src/utils/test_signer.rs @@ -0,0 +1,64 @@ +// 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 c2pa_crypto::{raw_signature::signer_from_cert_chain_and_private_key, SigningAlg}; + +use crate::{signer::RawSignerWrapper, Signer}; + +/// Creates a [`Signer`] instance for testing purposes using test credentials. +pub(crate) fn test_signer(alg: SigningAlg) -> Box { + let (cert_chain, private_key) = cert_chain_and_private_key_for_alg(alg); + + Box::new(RawSignerWrapper( + signer_from_cert_chain_and_private_key(&cert_chain, &private_key, alg, None).unwrap(), + )) +} + +fn cert_chain_and_private_key_for_alg(alg: SigningAlg) -> (Vec, Vec) { + match alg { + SigningAlg::Ps256 => ( + include_bytes!("../../tests/fixtures/certs/ps256.pub").to_vec(), + include_bytes!("../../tests/fixtures/certs/ps256.pem").to_vec(), + ), + + SigningAlg::Ps384 => ( + include_bytes!("../../tests/fixtures/certs/ps384.pub").to_vec(), + include_bytes!("../../tests/fixtures/certs/ps384.pem").to_vec(), + ), + + SigningAlg::Ps512 => ( + include_bytes!("../../tests/fixtures/certs/ps512.pub").to_vec(), + include_bytes!("../../tests/fixtures/certs/ps512.pem").to_vec(), + ), + + SigningAlg::Es256 => ( + include_bytes!("../../tests/fixtures/certs/es256.pub").to_vec(), + include_bytes!("../../tests/fixtures/certs/es256.pem").to_vec(), + ), + + SigningAlg::Es384 => ( + include_bytes!("../../tests/fixtures/certs/es384.pub").to_vec(), + include_bytes!("../../tests/fixtures/certs/es384.pem").to_vec(), + ), + + SigningAlg::Es512 => ( + include_bytes!("../../tests/fixtures/certs/es512.pub").to_vec(), + include_bytes!("../../tests/fixtures/certs/es512.pem").to_vec(), + ), + + SigningAlg::Ed25519 => ( + include_bytes!("../../tests/fixtures/certs/ed25519.pub").to_vec(), + include_bytes!("../../tests/fixtures/certs/ed25519.pem").to_vec(), + ), + } +} From 5fe275a0a2530465960fae874f5a059d151e5730 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sun, 8 Dec 2024 13:32:40 -0800 Subject: [PATCH 25/32] Remove remaining instances of temp_signer --- internal/crypto/src/openssl/signers/mod.rs | 2 +- internal/crypto/src/raw_signature/signer.rs | 6 +- sdk/src/asset_handlers/c2pa_io.rs | 8 +- sdk/src/builder.rs | 7 +- sdk/src/cose_sign.rs | 17 +- sdk/src/cose_validator.rs | 5 +- sdk/src/jumbf_io.rs | 6 +- sdk/src/manifest.rs | 56 ++++--- sdk/src/openssl/mod.rs | 10 -- sdk/src/openssl/openssl_trust_handler.rs | 68 ++++---- sdk/src/openssl/temp_signer.rs | 169 -------------------- sdk/src/openssl/temp_signer_async.rs | 102 ------------ sdk/src/resource_store.rs | 7 +- sdk/src/signer.rs | 97 ++++++++++- sdk/src/store.rs | 67 ++++---- sdk/src/utils/test.rs | 76 +-------- sdk/src/utils/test_signer.rs | 84 +++++++++- sdk/src/wasm/rsa_wasm_signer.rs | 5 +- 18 files changed, 311 insertions(+), 481 deletions(-) delete mode 100644 sdk/src/openssl/temp_signer.rs delete mode 100644 sdk/src/openssl/temp_signer_async.rs diff --git a/internal/crypto/src/openssl/signers/mod.rs b/internal/crypto/src/openssl/signers/mod.rs index 585a12880..639d0a1c5 100644 --- a/internal/crypto/src/openssl/signers/mod.rs +++ b/internal/crypto/src/openssl/signers/mod.rs @@ -36,7 +36,7 @@ pub(crate) fn signer_from_cert_chain_and_private_key( private_key: &[u8], alg: SigningAlg, time_stamp_service_url: Option, -) -> Result, RawSignerError> { +) -> Result, RawSignerError> { match alg { SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => Ok(Box::new( ecdsa_signer::EcdsaSigner::from_cert_chain_and_private_key( diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index bcece923b..633b19693 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -165,7 +165,7 @@ pub fn signer_from_cert_chain_and_private_key( private_key: &[u8], alg: SigningAlg, time_stamp_service_url: Option, -) -> Result, RawSignerError> { +) -> Result, RawSignerError> { #[cfg(feature = "openssl")] { return crate::openssl::signers::signer_from_cert_chain_and_private_key( @@ -203,7 +203,7 @@ pub fn async_signer_from_cert_chain_and_private_key( private_key: &[u8], alg: SigningAlg, time_stamp_service_url: Option, -) -> Result, RawSignerError> { +) -> Result, RawSignerError> { // TO DO: Preferentially use WASM-based signers, some of which are necessarily // async. @@ -217,7 +217,7 @@ pub fn async_signer_from_cert_chain_and_private_key( Ok(Box::new(AsyncRawSignerWrapper(sync_signer))) } -struct AsyncRawSignerWrapper(Box); +struct AsyncRawSignerWrapper(Box); #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] diff --git a/sdk/src/asset_handlers/c2pa_io.rs b/sdk/src/asset_handlers/c2pa_io.rs index 400122525..4dca3475e 100644 --- a/sdk/src/asset_handlers/c2pa_io.rs +++ b/sdk/src/asset_handlers/c2pa_io.rs @@ -146,13 +146,17 @@ pub mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] + use c2pa_crypto::SigningAlg; use c2pa_status_tracker::OneShotStatusTracker; use tempfile::tempdir; use super::{AssetIO, C2paIO, CAIReader, CAIWriter}; use crate::{ store::Store, - utils::test::{fixture_path, temp_dir_path, temp_signer}, + utils::{ + test::{fixture_path, temp_dir_path}, + test_signer::test_signer, + }, }; #[test] @@ -171,7 +175,7 @@ pub mod tests { let store = Store::load_from_asset(&temp_path, false, &mut OneShotStatusTracker::default()) .expect("loading store"); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let manifest2 = store.to_jumbf(signer.as_ref()).expect("to_jumbf"); assert_eq!(&manifest, &manifest2); diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index efbf23e1a..6692b18f6 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1093,10 +1093,7 @@ mod tests { assertions::BoxHash, asset_handlers::jpeg_io::JpegIO, hash_stream_by_alg, - utils::{ - test::{temp_signer, write_jpeg_placeholder_stream}, - test_signer::test_signer, - }, + utils::{test::write_jpeg_placeholder_stream, test_signer::test_signer}, Reader, }; @@ -1573,7 +1570,7 @@ mod tests { builder.add_assertion(labels::BOX_HASH, &box_hash).unwrap(); - let signer = crate::utils::test::temp_async_signer(); + let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); let manifest_bytes = builder .sign_box_hashed_embeddable_async(signer.as_ref(), "image/jpeg") diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index a69186a68..2a1e786aa 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -413,9 +413,14 @@ fn pad_cose_sig(sign1: &mut CoseSign1, end_size: usize) -> Result> { #[cfg(test)] mod tests { #![allow(clippy::unwrap_used)] + use c2pa_crypto::SigningAlg; use super::sign_claim; - use crate::{claim::Claim, utils::test::temp_signer, Result, Signer}; + use crate::{ + claim::Claim, + utils::test_signer::{async_test_signer, test_signer}, + Result, Signer, + }; #[test] fn test_sign_claim() { @@ -424,7 +429,7 @@ mod tests { let claim_bytes = claim.data().unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let box_size = signer.reserve_size(); let cose_sign1 = sign_claim(&claim_bytes, signer.as_ref(), box_size).unwrap(); @@ -436,16 +441,16 @@ mod tests { #[cfg(feature = "openssl")] #[actix::test] async fn test_sign_claim_async() { - use crate::{ - cose_sign::sign_claim_async, openssl::AsyncSignerAdapter, AsyncSigner, SigningAlg, - }; + use c2pa_crypto::SigningAlg; + + use crate::{cose_sign::sign_claim_async, AsyncSigner}; let mut claim = Claim::new("extern_sign_test", Some("contentauth")); claim.build().unwrap(); let claim_bytes = claim.data().unwrap(); - let signer = AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = async_test_signer(SigningAlg::Ps256); let box_size = signer.reserve_size(); let cose_sign1 = sign_claim_async(&claim_bytes, &signer, box_size) diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index dc1b0406c..00b3885f9 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -1361,11 +1361,12 @@ fn gt_to_datetime( #[cfg(feature = "openssl_sign")] #[cfg(test)] pub mod tests { + use c2pa_crypto::SigningAlg; use c2pa_status_tracker::DetailedStatusTracker; use sha2::digest::generic_array::sequence::Shorten; use super::*; - use crate::{openssl::temp_signer, signer::ConfigurableSigner, Signer, SigningAlg}; + use crate::{utils::test_signer::test_signer, Signer}; #[test] #[cfg(feature = "file_io")] @@ -1435,7 +1436,7 @@ pub mod tests { let box_size = 10000; - let signer = crate::utils::test::temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let cose_bytes = crate::cose_sign::sign_claim(&claim_bytes, signer.as_ref(), box_size).unwrap(); diff --git a/sdk/src/jumbf_io.rs b/sdk/src/jumbf_io.rs index 677ec9be7..7e11ae36e 100644 --- a/sdk/src/jumbf_io.rs +++ b/sdk/src/jumbf_io.rs @@ -349,10 +349,12 @@ pub mod tests { use std::io::Seek; + use c2pa_crypto::SigningAlg; + use super::*; use crate::{ asset_io::RemoteRefEmbedType, - utils::test::{create_test_store, temp_signer}, + utils::{test::create_test_store, test_signer::test_signer}, }; #[test] @@ -447,7 +449,7 @@ pub mod tests { fn test_jumbf(asset_type: &str, reader: &mut dyn CAIRead) { let mut writer = Cursor::new(Vec::new()); let store = create_test_store().unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let jumbf = store.to_jumbf(&*signer).unwrap(); save_jumbf_to_stream(asset_type, reader, &mut writer, &jumbf).unwrap(); writer.set_position(0); diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 774798e6f..26c956643 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1504,6 +1504,8 @@ pub(crate) mod tests { use std::io::Cursor; + #[cfg(feature = "file_io")] + use c2pa_crypto::SigningAlg; #[cfg(feature = "file_io")] use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; #[cfg(feature = "file_io")] @@ -1520,7 +1522,8 @@ pub(crate) mod tests { ingredient::Ingredient, reader::Reader, store::Store, - utils::test::{temp_remote_signer, temp_signer, TEST_VC}, + utils::test::{temp_remote_signer, TEST_VC}, + utils::test_signer::test_signer, Manifest, Result, }; #[cfg(feature = "file_io")] @@ -1601,7 +1604,7 @@ pub(crate) mod tests { let test_output = dir.path().join("wc_embed_test.jpg"); //embed a claim generated from this manifest - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let _store = manifest .embed(&source_path, &test_output, signer.as_ref()) @@ -1772,7 +1775,7 @@ pub(crate) mod tests { ) .expect("add_assertion"); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let c2pa_data = manifest .embed(&output, &output, signer.as_ref()) @@ -1797,7 +1800,7 @@ pub(crate) mod tests { .expect("add_redaction"); //embed a claim in output2 - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let _store2 = manifest2 .embed(&output2, &output2, signer.as_ref()) .expect("embed"); @@ -1838,7 +1841,7 @@ pub(crate) mod tests { .add_assertion(&actions) .expect("add_assertion"); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); parent_manifest .embed(&parent_output, &parent_output, signer.as_ref()) .expect("embed"); @@ -1895,8 +1898,7 @@ pub(crate) mod tests { let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); - let async_signer = - crate::openssl::temp_signer_async::AsyncSignerAdapter::new(crate::SigningAlg::Ps256); + let async_signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); let mut manifest = test_manifest(); manifest @@ -1938,7 +1940,7 @@ pub(crate) mod tests { let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut manifest = test_manifest(); manifest.set_label("MyLabel"); @@ -1963,7 +1965,7 @@ pub(crate) mod tests { let fp = format!("file:/{}", sidecar.to_str().unwrap()); let url = url::Url::parse(&fp).unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut manifest = test_manifest(); manifest.set_label("MyLabel"); @@ -2104,7 +2106,8 @@ pub(crate) mod tests { )) .unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); + let mut output = Cursor::new(Vec::new()); // Embed a manifest using the signer. manifest @@ -2126,7 +2129,7 @@ pub(crate) mod tests { #[cfg_attr(feature = "openssl_sign", actix::test)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] async fn test_embed_from_memory_async() { - use crate::{assertions::User, utils::test::temp_async_signer}; + use crate::assertions::User; let image = include_bytes!("../tests/fixtures/earth_apollo17.jpg"); // convert buffer to cursor with Read/Write/Seek capability let mut stream = std::io::Cursor::new(image.to_vec()); @@ -2142,8 +2145,9 @@ pub(crate) mod tests { )) .unwrap(); - let signer = temp_async_signer(); + let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); let mut output = Cursor::new(Vec::new()); + // Embed a manifest using the signer. manifest .embed_to_stream_async("jpeg", &mut stream, &mut output, signer.as_ref()) @@ -2171,7 +2175,7 @@ pub(crate) mod tests { let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut manifest = test_manifest(); let ingredient = @@ -2208,7 +2212,7 @@ pub(crate) mod tests { let fp = format!("file:/{}", sidecar.to_str().unwrap()); let url = url::Url::parse(&fp).unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let parent = Ingredient::from_file(fixture_path("XCA.jpg")).expect("getting parent"); let mut manifest = test_manifest(); @@ -2235,7 +2239,7 @@ pub(crate) mod tests { let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut manifest = test_manifest(); let thumb_data = vec![1, 2, 3]; @@ -2397,7 +2401,8 @@ pub(crate) mod tests { // convert buffer to cursor with Read/Write/Seek capability let mut input = std::io::Cursor::new(image.to_vec()); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); + // Embed a manifest using the signer. let mut output = Cursor::new(Vec::new()); manifest @@ -2464,7 +2469,8 @@ pub(crate) mod tests { let image = include_bytes!("../tests/fixtures/earth_apollo17.jpg"); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); + // Embed a manifest using the signer. let output_image = manifest .embed_from_memory("jpeg", image, signer.as_ref()) @@ -2529,7 +2535,7 @@ pub(crate) mod tests { let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut manifest = Manifest::from_json(MANIFEST_JSON).expect("from_json"); manifest.with_base_path(fixtures).expect("with_base"); @@ -2556,7 +2562,7 @@ pub(crate) mod tests { let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_WEBP); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut manifest = Manifest::from_json(MANIFEST_JSON).expect("from_json"); manifest.with_base_path(fixtures).expect("with_base"); @@ -2594,7 +2600,7 @@ pub(crate) mod tests { .is_ok()); assert!(manifest.thumbnail_ref().is_some()); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); manifest .embed(&output, &output, signer.as_ref()) .expect("embed"); @@ -2621,7 +2627,7 @@ pub(crate) mod tests { // verify there is no thumbnail assert!(manifest.thumbnail().is_none()); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); manifest .embed(&output, &output, signer.as_ref()) .expect("embed"); @@ -2650,9 +2656,11 @@ pub(crate) mod tests { let mut source = std::io::Cursor::new(vec![1, 2, 3]); let mut dest = std::io::Cursor::new(Vec::new()); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); + let result = manifest.embed_to_stream("image/jpeg", &mut source, &mut dest, signer.as_ref()); + assert!(result.is_err()); assert!(result .unwrap_err() @@ -2666,7 +2674,7 @@ pub(crate) mod tests { fn test_data_hash_embeddable_manifest() { let ap = fixture_path("cloud.jpg"); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let mut manifest = Manifest::new("claim_generator"); @@ -2792,7 +2800,7 @@ pub(crate) mod tests { .add_labeled_assertion(crate::assertions::labels::BOX_HASH, &box_hash) .unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let embeddable = manifest .box_hash_embeddable_manifest(signer.as_ref(), None) diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs index e2f797d5d..43c7b4a8f 100644 --- a/sdk/src/openssl/mod.rs +++ b/sdk/src/openssl/mod.rs @@ -13,18 +13,8 @@ #[cfg(feature = "openssl")] mod openssl_trust_handler; -#[cfg(test)] -pub(crate) mod temp_signer; #[cfg(feature = "openssl")] pub(crate) use openssl_trust_handler::verify_trust; #[cfg(feature = "openssl")] pub(crate) use openssl_trust_handler::OpenSSLTrustHandlerConfig; - -#[cfg(test)] -pub(crate) mod temp_signer_async; - -#[cfg(test)] -#[allow(unused_imports)] -#[cfg(feature = "openssl")] -pub(crate) use temp_signer_async::AsyncSignerAdapter; diff --git a/sdk/src/openssl/openssl_trust_handler.rs b/sdk/src/openssl/openssl_trust_handler.rs index 00a65d256..bfaabae35 100644 --- a/sdk/src/openssl/openssl_trust_handler.rs +++ b/sdk/src/openssl/openssl_trust_handler.rs @@ -302,28 +302,23 @@ pub mod tests { use c2pa_crypto::SigningAlg; use super::*; - use crate::{ - openssl::temp_signer::{self}, - Signer, - }; + use crate::{utils::test_signer::test_signer, Signer}; #[test] fn test_trust_store() { - let cert_dir = crate::utils::test::fixture_path("certs"); - let mut th = OpenSSLTrustHandlerConfig::new(); th.clear(); th.load_default_trust().unwrap(); // test all the certs - let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = test_signer(SigningAlg::Ps256); + let ps384 = test_signer(SigningAlg::Ps384); + let ps512 = test_signer(SigningAlg::Ps512); + let es256 = test_signer(SigningAlg::Es256); + let es384 = test_signer(SigningAlg::Es384); + let es512 = test_signer(SigningAlg::Es512); + let ed25519 = test_signer(SigningAlg::Ed25519); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); @@ -344,7 +339,6 @@ pub mod tests { #[test] fn test_broken_trust_chain() { - let cert_dir = crate::utils::test::fixture_path("certs"); let ta = include_bytes!("../../tests/fixtures/certs/trust/test_cert_root_bundle.pem"); let mut th = OpenSSLTrustHandlerConfig::new(); @@ -355,13 +349,13 @@ pub mod tests { th.load_trust_anchors_from_data(&mut reader).unwrap(); // test all the certs - let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = test_signer(SigningAlg::Ps256); + let ps384 = test_signer(SigningAlg::Ps384); + let ps512 = test_signer(SigningAlg::Ps512); + let es256 = test_signer(SigningAlg::Es256); + let es384 = test_signer(SigningAlg::Es384); + let es512 = test_signer(SigningAlg::Es512); + let ed25519 = test_signer(SigningAlg::Ed25519); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); @@ -383,8 +377,6 @@ pub mod tests { #[test] fn test_allowed_list() { - let cert_dir = crate::utils::test::fixture_path("certs"); - let mut th = OpenSSLTrustHandlerConfig::new(); th.clear(); @@ -397,13 +389,13 @@ pub mod tests { th.load_allowed_list(&mut allowed_list).unwrap(); // test all the certs - let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = test_signer(SigningAlg::Ps256); + let ps384 = test_signer(SigningAlg::Ps384); + let ps512 = test_signer(SigningAlg::Ps512); + let es256 = test_signer(SigningAlg::Es256); + let es384 = test_signer(SigningAlg::Es384); + let es512 = test_signer(SigningAlg::Es512); + let ed25519 = test_signer(SigningAlg::Ed25519); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); @@ -424,8 +416,6 @@ pub mod tests { #[test] fn test_allowed_list_hashes() { - let cert_dir = crate::utils::test::fixture_path("certs"); - let mut th = OpenSSLTrustHandlerConfig::new(); th.clear(); @@ -438,13 +428,13 @@ pub mod tests { th.load_allowed_list(&mut allowed_list).unwrap(); // test all the certs - let ps256 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps256, None); - let ps384 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps384, None); - let ps512 = temp_signer::get_rsa_signer(&cert_dir, SigningAlg::Ps512, None); - let es256 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es256, None); - let es384 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es384, None); - let es512 = temp_signer::get_ec_signer(&cert_dir, SigningAlg::Es512, None); - let ed25519 = temp_signer::get_ed_signer(&cert_dir, SigningAlg::Ed25519, None); + let ps256 = test_signer(SigningAlg::Ps256); + let ps384 = test_signer(SigningAlg::Ps384); + let ps512 = test_signer(SigningAlg::Ps512); + let es256 = test_signer(SigningAlg::Es256); + let es384 = test_signer(SigningAlg::Es384); + let es512 = test_signer(SigningAlg::Es512); + let ed25519 = test_signer(SigningAlg::Ed25519); let ps256_certs = ps256.certs().unwrap(); let ps384_certs = ps384.certs().unwrap(); diff --git a/sdk/src/openssl/temp_signer.rs b/sdk/src/openssl/temp_signer.rs deleted file mode 100644 index d6a554455..000000000 --- a/sdk/src/openssl/temp_signer.rs +++ /dev/null @@ -1,169 +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. - -#![deny(missing_docs)] - -//! Temporary signing instances for testing purposes. -//! -//! This module contains functions to create self-signed certificates -//! and provision [`Signer`] instances for each of the supported signature -//! formats. -//! -//! Private-key and signing certificate pairs are created in a directory -//! provided by the caller. It is recommended to use a temporary directory -//! that is deleted upon completion of the test. (We recommend using -//! the [tempfile](https://crates.io/crates/tempfile) crate.) -//! -//! This module should be used only for testing purposes. - -// Since this module is intended for testing purposes, all of -// its functions are allowed to panic. -#![allow(clippy::panic)] -#![allow(clippy::unwrap_used)] - -#[cfg(feature = "file_io")] -use std::path::Path; - -#[cfg(feature = "file_io")] -use c2pa_crypto::SigningAlg; - -/// Create an OpenSSL ES256 signer that can be used for testing purposes. -/// -/// # Arguments -/// -/// * `path` - A directory (which must already exist) to receive the temporary -/// private key / certificate pair. -/// * `alg` - A format for signing. Must be one of the `SigningAlg::Es*` variants. -/// * `tsa_url` - Optional URL for a timestamp authority. -/// -/// # Returns -/// -/// Returns a tuple of `(signer, sign_cert_path)` where `signer` is -/// the [`Signer`] instance and `sign_cert_path` is the path to the -/// signing certificate. -/// -/// # Panics -/// -/// Can panic if unable to invoke OpenSSL executable properly. -#[cfg(feature = "file_io")] -pub fn get_ec_signer>( - path: P, - alg: SigningAlg, - tsa_url: Option, -) -> Box { - match alg { - SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => (), - _ => { - panic!("Unknown EC signer alg {alg:#?}"); - } - } - - let mut sign_cert_path = path.as_ref().to_path_buf(); - sign_cert_path.push(alg.to_string()); - sign_cert_path.set_extension("pub"); - - let mut pem_key_path = path.as_ref().to_path_buf(); - pem_key_path.push(alg.to_string()); - pem_key_path.set_extension("pem"); - - crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap() -} - -/// Create an OpenSSL ES256 signer that can be used for testing purposes. -/// -/// # Arguments -/// -/// * `path` - A directory (which must already exist) to look for -/// private key / certificate pair. -/// * `alg` - A format for signing. Must be `ed25519`. -/// * `tsa_url` - Optional URL for a timestamp authority. -/// -/// # Returns -/// -/// Returns a tuple of `(signer, sign_cert_path)` where `signer` is -/// the [`Signer`] instance and `sign_cert_path` is the path to the -/// signing certificate. -/// -/// # Panics -/// -/// Can panic if unable to invoke OpenSSL executable properly. -#[cfg(feature = "file_io")] -pub fn get_ed_signer>( - path: P, - alg: SigningAlg, - tsa_url: Option, -) -> Box { - if alg != SigningAlg::Ed25519 { - panic!("Unknown ED signer alg {alg:#?}"); - } - - let mut sign_cert_path = path.as_ref().to_path_buf(); - sign_cert_path.push(alg.to_string()); - sign_cert_path.set_extension("pub"); - - let mut pem_key_path = path.as_ref().to_path_buf(); - pem_key_path.push(alg.to_string()); - pem_key_path.set_extension("pem"); - - crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap() -} - -/// Create an OpenSSL SHA+RSA signer that can be used for testing purposes. -/// -/// # Arguments -/// -/// * `path` - A directory (which must already exist) to receive the temporary -/// private key / certificate pair. -/// * `alg` - A format for signing. Must be one of the `SignerAlg::Ps*` options. -/// * `tsa_url` - Optional URL for a timestamp authority. -/// -/// # Returns -/// -/// Returns a tuple of `(signer, sign_cert_path)` where `signer` is -/// the [`Signer`] instance and `sign_cert_path` is the path to the -/// signing certificate. -/// -/// # Panics -/// -/// Can panic if unable to invoke OpenSSL executable properly. -#[cfg(feature = "file_io")] -pub fn get_rsa_signer>( - path: P, - alg: SigningAlg, - tsa_url: Option, -) -> Box { - match alg { - SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => (), - _ => { - panic!("Unknown RSA signer alg {alg:#?}"); - } - } - - let mut sign_cert_path = path.as_ref().to_path_buf(); - sign_cert_path.push(alg.to_string()); - sign_cert_path.set_extension("pub"); - - let mut pem_key_path = path.as_ref().to_path_buf(); - pem_key_path.push(alg.to_string()); - pem_key_path.set_extension("pem"); - - if !sign_cert_path.exists() || !pem_key_path.exists() { - panic!( - "path found: {}, {}", - sign_cert_path.display(), - pem_key_path.display() - ); - } - - crate::create_signer::from_files(&sign_cert_path, &pem_key_path, alg, tsa_url).unwrap() -} diff --git a/sdk/src/openssl/temp_signer_async.rs b/sdk/src/openssl/temp_signer_async.rs deleted file mode 100644 index 233e4d02d..000000000 --- a/sdk/src/openssl/temp_signer_async.rs +++ /dev/null @@ -1,102 +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. - -#![deny(missing_docs)] - -//! Temporary async signing instances for testing purposes. -//! -//! This is only a demonstration async Signer that is used to test -//! the asynchronous signing of claims. -//! This module should be used only for testing purposes. - -#[cfg(feature = "openssl_sign")] -use c2pa_crypto::SigningAlg; - -use crate::{AsyncSigner, Result}; - -#[cfg(feature = "openssl_sign")] -fn get_local_signer(alg: SigningAlg) -> Box { - let cert_dir = crate::utils::test::fixture_path("certs"); - - match alg { - SigningAlg::Ps256 | SigningAlg::Ps384 | SigningAlg::Ps512 => { - let s = super::temp_signer::get_rsa_signer(&cert_dir, alg, None); - s - } - SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => { - let s = super::temp_signer::get_ec_signer(&cert_dir, alg, None); - Box::new(s) - } - SigningAlg::Ed25519 => { - let s = super::temp_signer::get_ed_signer(&cert_dir, alg, None); - Box::new(s) - } - } -} - -#[cfg(feature = "openssl_sign")] -pub struct AsyncSignerAdapter { - alg: SigningAlg, - certs: Vec>, - reserve_size: usize, - tsa_url: Option, - ocsp_val: Option>, -} - -#[cfg(feature = "openssl_sign")] -impl AsyncSignerAdapter { - pub fn new(alg: SigningAlg) -> Self { - let signer = get_local_signer(alg); - - AsyncSignerAdapter { - alg, - certs: signer.certs().unwrap_or_default(), - reserve_size: signer.reserve_size(), - tsa_url: signer.time_authority_url(), - ocsp_val: signer.ocsp_val(), - } - } -} - -#[cfg(test)] -#[async_trait::async_trait] -impl AsyncSigner for AsyncSignerAdapter { - async fn sign(&self, data: Vec) -> Result> { - let signer = get_local_signer(self.alg); - signer.sign(&data) - } - - fn alg(&self) -> SigningAlg { - self.alg - } - - fn certs(&self) -> crate::Result>> { - let mut output: Vec> = Vec::new(); - for v in &self.certs { - output.push(v.clone()); - } - Ok(output) - } - - fn reserve_size(&self) -> usize { - self.reserve_size - } - - fn time_authority_url(&self) -> Option { - self.tsa_url.clone() - } - - async fn ocsp_val(&self) -> Option> { - self.ocsp_val.clone() - } -} diff --git a/sdk/src/resource_store.rs b/sdk/src/resource_store.rs index 6d6eff8f4..c5229d23b 100644 --- a/sdk/src/resource_store.rs +++ b/sdk/src/resource_store.rs @@ -404,8 +404,10 @@ mod tests { use std::io::Cursor; + use c2pa_crypto::SigningAlg; + use super::*; - use crate::{utils::test::temp_signer, Builder, Reader}; + use crate::{utils::test_signer::test_signer, Builder, Reader}; #[test] #[cfg(feature = "openssl_sign")] @@ -451,7 +453,8 @@ mod tests { let image = include_bytes!("../tests/fixtures/earth_apollo17.jpg"); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); + // Embed a manifest using the signer. let mut output_image = Cursor::new(Vec::new()); builder diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 972c28cdf..38e4e93f4 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -344,7 +344,102 @@ impl Signer for Box { } } -#[allow(dead_code)] // TEMPORARY: Not used on WASM +#[cfg(not(target_arch = "wasm32"))] +#[async_trait] +impl AsyncSigner for Box { + async fn sign(&self, data: Vec) -> Result> { + (**self).sign(data).await + } + + fn alg(&self) -> SigningAlg { + (**self).alg() + } + + fn certs(&self) -> Result>> { + (**self).certs() + } + + fn reserve_size(&self) -> usize { + (**self).reserve_size() + } + + fn time_authority_url(&self) -> Option { + (**self).time_authority_url() + } + + fn timestamp_request_headers(&self) -> Option> { + (**self).timestamp_request_headers() + } + + fn timestamp_request_body(&self, message: &[u8]) -> Result> { + (**self).timestamp_request_body(message) + } + + async fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + (**self).send_timestamp_request(message).await + } + + async fn ocsp_val(&self) -> Option> { + (**self).ocsp_val().await + } + + fn direct_cose_handling(&self) -> bool { + (**self).direct_cose_handling() + } + + fn dynamic_assertions(&self) -> Vec> { + (**self).dynamic_assertions() + } +} + +#[cfg(target_arch = "wasm32")] +#[async_trait(?Send)] +impl AsyncSigner for Box { + async fn sign(&self, data: Vec) -> Result> { + (**self).sign(data).await + } + + fn alg(&self) -> SigningAlg { + (**self).alg() + } + + fn certs(&self) -> Result>> { + (**self).certs() + } + + fn reserve_size(&self) -> usize { + (**self).reserve_size() + } + + fn time_authority_url(&self) -> Option { + (**self).time_authority_url() + } + + fn timestamp_request_headers(&self) -> Option> { + (**self).timestamp_request_headers() + } + + fn timestamp_request_body(&self, message: &[u8]) -> Result> { + (**self).timestamp_request_body(message) + } + + async fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + (**self).send_timestamp_request(message).await + } + + async fn ocsp_val(&self) -> Option> { + (**self).ocsp_val().await + } + + fn direct_cose_handling(&self) -> bool { + (**self).direct_cose_handling() + } + + fn dynamic_assertions(&self) -> Vec> { + (**self).dynamic_assertions() + } +} + pub(crate) struct RawSignerWrapper(pub(crate) Box); impl Signer for RawSignerWrapper { diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 2bf571ae0..31d56a5e0 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -3645,9 +3645,10 @@ pub mod tests { hash_utils::Hasher, patch::patch_file, test::{ - create_test_claim, fixture_path, temp_dir_path, temp_fixture_path, temp_signer, + create_test_claim, fixture_path, temp_dir_path, temp_fixture_path, write_jpeg_placeholder_file, }, + test_signer::{async_test_signer, test_signer}, }, }; @@ -3698,7 +3699,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Test generate JUMBF // Get labels for label test @@ -3811,7 +3812,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -3958,7 +3959,7 @@ pub mod tests { #[actix::test] async fn test_jumbf_generation_async() { - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = async_test_signer(SigningAlg::Ps256); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); @@ -4083,7 +4084,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -4179,7 +4180,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commmits store.commit_claim(claim1).unwrap(); @@ -4252,7 +4253,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commmits store.commit_claim(claim1).unwrap(); @@ -4326,7 +4327,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -4400,7 +4401,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -4474,7 +4475,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -4540,7 +4541,7 @@ pub mod tests { let claim1 = create_test_claim().unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -4584,7 +4585,7 @@ pub mod tests { let claim1 = create_test_claim().unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -4628,7 +4629,7 @@ pub mod tests { let claim1 = create_test_claim().unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -4754,7 +4755,7 @@ pub mod tests { fn test_verifiable_credentials() { use crate::utils::test::create_test_store; - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); @@ -4792,7 +4793,7 @@ pub mod tests { fn test_data_box_creation() { use crate::utils::test::create_test_store; - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); @@ -4847,7 +4848,7 @@ pub mod tests { fn test_update_manifest() { use crate::{hashed_uri::HashedUri, utils::test::create_test_store}; - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // test adding to actual image let ap = fixture_path("earth_apollo17.jpg"); @@ -5022,7 +5023,7 @@ pub mod tests { // Create a new claim. let claim1 = create_test_claim().unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. store.commit_claim(claim1).unwrap(); @@ -5052,7 +5053,7 @@ pub mod tests { // Create a new claim. let claim1 = create_test_claim().unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); let result: Vec = Vec::new(); let mut output_stream = Cursor::new(result); @@ -5114,7 +5115,7 @@ pub mod tests { claim.set_external_manifest(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); store.commit_claim(claim).unwrap(); @@ -5151,7 +5152,7 @@ pub mod tests { let mut claim = create_test_claim().unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // start with base url let fp = format!("file:/{}", sidecar.to_str().unwrap()); @@ -5219,7 +5220,7 @@ pub mod tests { let mut claim = create_test_claim().unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // start with base url let fp = format!("file:/{}", sidecar.to_str().unwrap()); @@ -5271,7 +5272,7 @@ pub mod tests { let mut claim = create_test_claim().unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // start with base url let fp = format!("file:/{}", sidecar.to_str().unwrap()); @@ -5327,7 +5328,7 @@ pub mod tests { // Create a new claim. let claim1 = create_test_claim().unwrap(); - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); store.commit_claim(claim1).unwrap(); @@ -5381,7 +5382,7 @@ pub mod tests { create_capture_claim(&mut claim_capture).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Move the claim to claims list. Note this is not real, the claims would have to be signed in between commits store.commit_claim(claim1).unwrap(); @@ -5451,7 +5452,7 @@ pub mod tests { store.commit_claim(claim).unwrap(); // Do we generate JUMBF? - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = async_test_signer(SigningAlg::Ps256); // get the embeddable manifest let em = store @@ -5537,7 +5538,7 @@ pub mod tests { store.commit_claim(claim).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // get the embeddable manifest let em = store @@ -5605,7 +5606,7 @@ pub mod tests { let ap = fixture_path("cloud.jpg"); // Do we generate JUMBF? - let signer = crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = async_test_signer(SigningAlg::Ps256); // Create claims store. let mut store = Store::new(); @@ -5674,7 +5675,7 @@ pub mod tests { let ap = fixture_path("cloud.jpg"); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Create claims store. let mut store = Store::new(); @@ -5745,7 +5746,7 @@ pub mod tests { let mut hasher = Hasher::SHA256(Sha256::new()); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // Create claims store. let mut store = Store::new(); @@ -5851,7 +5852,7 @@ pub mod tests { fn test_placed_manifest() { use crate::jumbf::labels::to_normalized_uri; - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // test adding to actual image let ap = fixture_path("C.jpg"); @@ -5971,7 +5972,7 @@ pub mod tests { impl DynamicSigner { fn new() -> Self { - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); DynamicSigner { alg: signer.alg(), certs: signer.certs().unwrap_or_default(), @@ -5985,7 +5986,7 @@ pub mod tests { #[async_trait::async_trait] impl crate::AsyncSigner for DynamicSigner { async fn sign(&self, data: Vec) -> crate::error::Result> { - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); signer.sign(&data) } @@ -6098,7 +6099,7 @@ pub mod tests { store.commit_claim(claim).unwrap(); // Do we generate JUMBF? - let signer = temp_signer(); + let signer = test_signer(SigningAlg::Ps256); // add manifest based on let new_output_path = output_path.join(init_dir.file_name().unwrap()); diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 9a69e7c2a..648d4f5dd 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -23,10 +23,6 @@ use std::{ use c2pa_crypto::SigningAlg; use tempfile::TempDir; -#[cfg(feature = "file_io")] -use crate::create_signer; -#[cfg(feature = "openssl_sign")] -use crate::openssl::AsyncSignerAdapter; use crate::{ assertions::{labels, Action, Actions, Ingredient, ReviewRating, SchemaDotOrg, Thumbnail}, asset_io::CAIReadWrite, @@ -333,72 +329,6 @@ impl AsyncSigner for AsyncTestGoodSigner { } } -/// Create a [`Signer`] instance that can be used for testing purposes using ps256 alg. -/// -/// # Returns -/// -/// Returns a boxed [`Signer`] instance. -#[cfg(test)] -pub(crate) fn temp_signer() -> Box { - #[cfg(feature = "openssl_sign")] - { - #![allow(clippy::expect_used)] - let sign_cert = include_bytes!("../../tests/fixtures/certs/ps256.pub").to_vec(); - let pem_key = include_bytes!("../../tests/fixtures/certs/ps256.pem").to_vec(); - - crate::create_signer::from_keys(&sign_cert, &pem_key, SigningAlg::Ps256, None) - .expect("get_temp_signer") - } - - // todo: the will be a RustTLS signer shortly - #[cfg(not(feature = "openssl_sign"))] - { - Box::new(TestGoodSigner {}) - } -} - -#[cfg(any(target_arch = "wasm32", feature = "openssl_sign"))] -pub fn temp_async_signer() -> Box { - #[cfg(feature = "openssl_sign")] - { - Box::new(AsyncSignerAdapter::new(SigningAlg::Es256)) - } - - #[cfg(target_arch = "wasm32")] - { - let sign_cert = include_str!("../../tests/fixtures/certs/es256.pub"); - let pem_key = include_str!("../../tests/fixtures/certs/es256.pem"); - let signer = WebCryptoSigner::new("es256", sign_cert, pem_key); - Box::new(signer) - } -} - -/// Create a [`Signer`] instance for a specific algorithm that can be used for testing purposes. -/// -/// # Returns -/// -/// Returns a boxed [`Signer`] instance. -/// -/// # Panics -/// -/// Can panic if the certs cannot be read. (This function should only -/// be used as part of testing infrastructure.) -#[cfg(feature = "file_io")] -pub fn temp_signer_with_alg(alg: SigningAlg) -> Box { - #![allow(clippy::expect_used)] - // sign and embed into the target file - let mut sign_cert_path = fixture_path("certs"); - sign_cert_path.push(alg.to_string()); - sign_cert_path.set_extension("pub"); - - let mut pem_key_path = fixture_path("certs"); - pem_key_path.push(alg.to_string()); - pem_key_path.set_extension("pem"); - - create_signer::from_files(sign_cert_path.clone(), pem_key_path, alg, None) - .expect("get_temp_signer_with_alg") -} - struct TempRemoteSigner {} #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] @@ -407,8 +337,7 @@ impl crate::signer::RemoteSigner for TempRemoteSigner { async fn sign_remote(&self, claim_bytes: &[u8]) -> crate::error::Result> { #[cfg(feature = "openssl_sign")] { - let signer = - crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); // this would happen on some remote server crate::cose_sign::cose_sign_async(&signer, claim_bytes, self.reserve_size()).await @@ -558,8 +487,7 @@ impl AsyncSigner for TempAsyncRemoteSigner { async fn sign(&self, claim_bytes: Vec) -> Result> { #[cfg(feature = "openssl_sign")] { - let signer = - crate::openssl::temp_signer_async::AsyncSignerAdapter::new(SigningAlg::Ps256); + let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); // this would happen on some remote server crate::cose_sign::cose_sign_async(&signer, &claim_bytes, self.reserve_size()).await diff --git a/sdk/src/utils/test_signer.rs b/sdk/src/utils/test_signer.rs index b8693b277..4d46c88bc 100644 --- a/sdk/src/utils/test_signer.rs +++ b/sdk/src/utils/test_signer.rs @@ -11,9 +11,16 @@ // specific language governing permissions and limitations under // each license. -use c2pa_crypto::{raw_signature::signer_from_cert_chain_and_private_key, SigningAlg}; +use async_trait::async_trait; +use c2pa_crypto::{ + raw_signature::{ + async_signer_from_cert_chain_and_private_key, signer_from_cert_chain_and_private_key, + AsyncRawSigner, + }, + SigningAlg, +}; -use crate::{signer::RawSignerWrapper, Signer}; +use crate::{signer::RawSignerWrapper, AsyncSigner, Result, Signer}; /// Creates a [`Signer`] instance for testing purposes using test credentials. pub(crate) fn test_signer(alg: SigningAlg) -> Box { @@ -24,6 +31,26 @@ pub(crate) fn test_signer(alg: SigningAlg) -> Box { )) } +/// Creates an [`AsyncSigner`] instance for testing purposes using test credentials. +#[cfg(not(target_arch = "wasm32"))] +pub(crate) fn async_test_signer(alg: SigningAlg) -> Box { + let (cert_chain, private_key) = cert_chain_and_private_key_for_alg(alg); + + Box::new(AsyncRawSignerWrapper( + async_signer_from_cert_chain_and_private_key(&cert_chain, &private_key, alg, None).unwrap(), + )) +} + +/// Creates an [`AsyncSigner`] instance for testing purposes using test credentials. +#[cfg(target_arch = "wasm32")] +pub(crate) fn async_test_signer(alg: SigningAlg) -> Box { + let (cert_chain, private_key) = cert_chain_and_private_key_for_alg(alg); + + Box::new(AsyncRawSignerWrapper( + async_signer_from_cert_chain_and_private_key(&cert_chain, &private_key, alg, None).unwrap(), + )) +} + fn cert_chain_and_private_key_for_alg(alg: SigningAlg) -> (Vec, Vec) { match alg { SigningAlg::Ps256 => ( @@ -62,3 +89,56 @@ fn cert_chain_and_private_key_for_alg(alg: SigningAlg) -> (Vec, Vec) { ), } } + +#[cfg(not(target_arch = "wasm32"))] +struct AsyncRawSignerWrapper(Box); + +#[allow(dead_code)] // TEMPORARY: Not used on WASM +#[cfg(target_arch = "wasm32")] +struct AsyncRawSignerWrapper(Box); + +#[allow(dead_code)] // TEMPORARY: Not used on WASM +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl AsyncSigner for AsyncRawSignerWrapper { + async fn sign(&self, data: Vec) -> Result> { + self.0.sign(data).await.map_err(|e| e.into()) + } + + fn alg(&self) -> SigningAlg { + self.0.alg() + } + + fn certs(&self) -> Result>> { + self.0.cert_chain().map_err(|e| e.into()) + } + + fn reserve_size(&self) -> usize { + self.0.reserve_size() + } + + async fn ocsp_val(&self) -> Option> { + self.0.ocsp_response().await + } + + fn time_authority_url(&self) -> Option { + self.0.time_stamp_service_url() + } + + fn timestamp_request_headers(&self) -> Option> { + self.0.time_stamp_request_headers() + } + + fn timestamp_request_body(&self, message: &[u8]) -> Result> { + self.0 + .time_stamp_request_body(message) + .map_err(|e| e.into()) + } + + async fn send_timestamp_request(&self, message: &[u8]) -> Option>> { + self.0 + .send_time_stamp_request(message) + .await + .map(|r| r.map_err(|e| e.into())) + } +} diff --git a/sdk/src/wasm/rsa_wasm_signer.rs b/sdk/src/wasm/rsa_wasm_signer.rs index cb1cda0fa..4e3320539 100644 --- a/sdk/src/wasm/rsa_wasm_signer.rs +++ b/sdk/src/wasm/rsa_wasm_signer.rs @@ -311,10 +311,7 @@ mod tests { }; use super::*; - use crate::{ - utils::test::{fixture_path, temp_signer}, - Signer, - }; + use crate::{utils::test::fixture_path, Signer}; #[test] fn sign_ps256() { From 87c42d9064dd0d757349c00e22760e6bb91ccb34 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sun, 8 Dec 2024 13:41:12 -0800 Subject: [PATCH 26/32] Fix WASM build issues There are still some WASM test failures --- sdk/src/cose_sign.rs | 8 +++----- sdk/src/manifest.rs | 2 +- sdk/src/signer.rs | 1 + sdk/src/utils/test.rs | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 2a1e786aa..ddb4d15b5 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -416,11 +416,9 @@ mod tests { use c2pa_crypto::SigningAlg; use super::sign_claim; - use crate::{ - claim::Claim, - utils::test_signer::{async_test_signer, test_signer}, - Result, Signer, - }; + #[cfg(not(target_arch = "wasm32"))] + use crate::utils::test_signer::async_test_signer; + use crate::{claim::Claim, utils::test_signer::test_signer, Result, Signer}; #[test] fn test_sign_claim() { diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 26c956643..11c0af2ae 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1504,7 +1504,7 @@ pub(crate) mod tests { use std::io::Cursor; - #[cfg(feature = "file_io")] + #[cfg(any(feature = "file_io", target_arch = "wasm32"))] use c2pa_crypto::SigningAlg; #[cfg(feature = "file_io")] use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; diff --git a/sdk/src/signer.rs b/sdk/src/signer.rs index 38e4e93f4..72c37b436 100644 --- a/sdk/src/signer.rs +++ b/sdk/src/signer.rs @@ -440,6 +440,7 @@ impl AsyncSigner for Box { } } +#[cfg_attr(target_arch = "wasm32", allow(dead_code))] pub(crate) struct RawSignerWrapper(pub(crate) Box); impl Signer for RawSignerWrapper { diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 648d4f5dd..bfa6f9176 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -31,7 +31,7 @@ use crate::{ jumbf_io::get_assetio_handler, salt::DefaultSalt, store::Store, - AsyncSigner, RemoteSigner, Result, Signer, + AsyncSigner, RemoteSigner, Result, }; pub const TEST_SMALL_JPEG: &str = "earth_apollo17.jpg"; @@ -205,7 +205,7 @@ pub fn temp_fixture_path(temp_dir: &TempDir, file_name: &str) -> PathBuf { /// Can panic if the certs cannot be read. (This function should only /// be used as part of testing infrastructure.) #[cfg(feature = "file_io")] -pub fn temp_signer_file() -> Box { +pub fn temp_signer_file() -> Box { #![allow(clippy::expect_used)] let mut sign_cert_path = fixture_path("certs"); sign_cert_path.push("ps256"); From 278839aac0f6ea066676c9fad43c8d5802e6413f Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sun, 8 Dec 2024 13:49:15 -0800 Subject: [PATCH 27/32] Clippy --- sdk/src/utils/test_signer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/src/utils/test_signer.rs b/sdk/src/utils/test_signer.rs index 4d46c88bc..c9d32a99d 100644 --- a/sdk/src/utils/test_signer.rs +++ b/sdk/src/utils/test_signer.rs @@ -11,6 +11,8 @@ // specific language governing permissions and limitations under // each license. +#![allow(clippy::unwrap_used)] // This mod is only used in test code. + use async_trait::async_trait; use c2pa_crypto::{ raw_signature::{ From 43d31406da98702377c62bb6188525f4f98ef561 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Sun, 8 Dec 2024 16:23:53 -0800 Subject: [PATCH 28/32] mod openssl::signers doesn't need to be fully public --- internal/crypto/src/openssl/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/crypto/src/openssl/mod.rs b/internal/crypto/src/openssl/mod.rs index dc9102fa8..78fda28d4 100644 --- a/internal/crypto/src/openssl/mod.rs +++ b/internal/crypto/src/openssl/mod.rs @@ -23,5 +23,5 @@ mod cert_chain; mod ffi_mutex; pub use ffi_mutex::{OpenSslMutex, OpenSslMutexUnavailable}; -pub mod signers; +pub(crate) mod signers; pub mod validators; From 1ca94c6b6af5976b332cab603de621f277296bdb Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Mon, 9 Dec 2024 19:28:00 -0800 Subject: [PATCH 29/32] Add WASM implementation for signing via Ed25519 --- internal/crypto/Cargo.toml | 2 +- internal/crypto/src/raw_signature/signer.rs | 16 +-- internal/crypto/src/tests/webcrypto/mod.rs | 1 + .../tests/webcrypto/signers/ed25519_signer.rs | 42 ++++++++ .../crypto/src/tests/webcrypto/signers/mod.rs | 14 +++ internal/crypto/src/webcrypto/mod.rs | 1 + .../src/webcrypto/signers/ed25519_signer.rs | 101 ++++++++++++++++++ internal/crypto/src/webcrypto/signers/mod.rs | 48 +++++++++ 8 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 internal/crypto/src/tests/webcrypto/signers/ed25519_signer.rs create mode 100644 internal/crypto/src/tests/webcrypto/signers/mod.rs create mode 100644 internal/crypto/src/webcrypto/signers/ed25519_signer.rs create mode 100644 internal/crypto/src/webcrypto/signers/mod.rs diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 791ddd230..3137466a3 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -75,7 +75,7 @@ features = ["now", "wasmbind"] [target.'cfg(target_arch = "wasm32")'.dependencies] async-trait = { version = "0.1.77" } ecdsa = "0.16.9" -ed25519-dalek = "2.1.1" +ed25519-dalek = { version = "2.1.1", features = ["alloc", "digest", "pem", "pkcs8"] } p256 = "0.13.2" p384 = "0.13.0" rsa = { version = "0.9.6", features = ["sha2"] } diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index 633b19693..cca4f607d 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -176,13 +176,15 @@ pub fn signer_from_cert_chain_and_private_key( ); } - // TO DO: Do we need this for WASM or is it all async? - // #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] - // if let Some(validator) = - // crate::webcrypto::validators::validator_for_sig_and_hash_algs(sig_alg, - // hash_alg) { - // return Some(validator); - // } + #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] + { + return crate::webcrypto::signers::signer_from_cert_chain_and_private_key( + cert_chain, + private_key, + alg, + time_stamp_service_url, + ); + } Err(RawSignerError::InternalError(format!( "unsupported algorithm: {alg}" diff --git a/internal/crypto/src/tests/webcrypto/mod.rs b/internal/crypto/src/tests/webcrypto/mod.rs index c1bbf2757..a2a612f0f 100644 --- a/internal/crypto/src/tests/webcrypto/mod.rs +++ b/internal/crypto/src/tests/webcrypto/mod.rs @@ -11,5 +11,6 @@ // specific language governing permissions and limitations under // each license. +mod signers; mod validators; mod window_or_worker; diff --git a/internal/crypto/src/tests/webcrypto/signers/ed25519_signer.rs b/internal/crypto/src/tests/webcrypto/signers/ed25519_signer.rs new file mode 100644 index 000000000..9770f6673 --- /dev/null +++ b/internal/crypto/src/tests/webcrypto/signers/ed25519_signer.rs @@ -0,0 +1,42 @@ +// Copyright 2024 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 wasm_bindgen_test::wasm_bindgen_test; + +use crate::{ + raw_signature::{signer_from_cert_chain_and_private_key, RawSignatureValidator}, + webcrypto::validators::Ed25519Validator, + SigningAlg, +}; + +#[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"); + + let signer = + signer_from_cert_chain_and_private_key(cert_chain, private_key, SigningAlg::Ed25519, None) + .unwrap(); + + let data = b"some sample content to sign"; + let signature = signer.sign(data).unwrap(); + + println!("signature len = {}", signature.len()); + assert!(signature.len() <= signer.reserve_size()); + + let pub_key = include_bytes!("../../fixtures/raw_signature/ed25519.pub_key"); + + Ed25519Validator {} + .validate(&signature, data, pub_key) + .unwrap(); +} diff --git a/internal/crypto/src/tests/webcrypto/signers/mod.rs b/internal/crypto/src/tests/webcrypto/signers/mod.rs new file mode 100644 index 000000000..577cd72b2 --- /dev/null +++ b/internal/crypto/src/tests/webcrypto/signers/mod.rs @@ -0,0 +1,14 @@ +// Copyright 2024 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. + +mod ed25519_signer; diff --git a/internal/crypto/src/webcrypto/mod.rs b/internal/crypto/src/webcrypto/mod.rs index fb51e2d32..de9ae2b80 100644 --- a/internal/crypto/src/webcrypto/mod.rs +++ b/internal/crypto/src/webcrypto/mod.rs @@ -25,6 +25,7 @@ pub use async_validators::{ AsyncRawSignatureValidator, }; +pub(crate) mod signers; pub mod validators; mod window_or_worker; diff --git a/internal/crypto/src/webcrypto/signers/ed25519_signer.rs b/internal/crypto/src/webcrypto/signers/ed25519_signer.rs new file mode 100644 index 000000000..a7d3871a4 --- /dev/null +++ b/internal/crypto/src/webcrypto/signers/ed25519_signer.rs @@ -0,0 +1,101 @@ +// Copyright 2024 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 ed25519_dalek::{pkcs8::DecodePrivateKey, SigningKey}; +use x509_parser::{error::PEMError, pem::Pem}; + +use crate::{ + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, + SigningAlg, +}; + +/// Implements `RawSigner` trait using `ed25519_dalek` crate's implementation of +/// Edwards Curve encryption. +pub struct Ed25519Signer { + #[allow(dead_code)] + cert_chain: Vec>, + cert_chain_len: usize, + + signing_key: SigningKey, + + time_stamp_service_url: Option, + time_stamp_size: usize, +} + +impl Ed25519Signer { + pub(crate) fn from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + time_stamp_service_url: Option, + ) -> Result { + let cert_chain = Pem::iter_from_buffer(cert_chain) + .map(|r| match r { + Ok(pem) => Ok(pem.contents), + Err(e) => Err(e), + }) + .collect::>, PEMError>>() + .map_err(|e| RawSignerError::InvalidSigningCredentials(e.to_string()))?; + + let cert_chain_len = cert_chain.len(); + + let private_key_pem = std::str::from_utf8(private_key).map_err(|e| { + RawSignerError::InvalidSigningCredentials(format!("invalid private key: {e}")) + })?; + + let signing_key = SigningKey::from_pkcs8_pem(private_key_pem).map_err(|e| { + RawSignerError::InvalidSigningCredentials(format!("invalid private key: {e}")) + })?; + + Ok(Ed25519Signer { + cert_chain, + cert_chain_len, + + signing_key, + + time_stamp_service_url, + time_stamp_size: 10000, + // TO DO: Call out to time stamp service to get actual time stamp and use that size? + }) + } +} + +impl RawSigner for Ed25519Signer { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { + use ed25519_dalek::Signer; + + Ok(self + .signing_key + .try_sign(data) + .map_err(|e| RawSignerError::InternalError(format!("signature error: {e}")))? + .to_vec()) + } + + fn alg(&self) -> SigningAlg { + SigningAlg::Ed25519 + } + + fn reserve_size(&self) -> usize { + 1024 + self.cert_chain_len + self.time_stamp_size + } + + fn cert_chain(&self) -> Result>, RawSignerError> { + Ok(self.cert_chain.clone()) + } +} + +impl TimeStampProvider for Ed25519Signer { + fn time_stamp_service_url(&self) -> Option { + self.time_stamp_service_url.clone() + } +} diff --git a/internal/crypto/src/webcrypto/signers/mod.rs b/internal/crypto/src/webcrypto/signers/mod.rs new file mode 100644 index 000000000..134f3d664 --- /dev/null +++ b/internal/crypto/src/webcrypto/signers/mod.rs @@ -0,0 +1,48 @@ +// Copyright 2024 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 crate::{ + raw_signature::{RawSigner, RawSignerError}, + SigningAlg, +}; + +mod ed25519_signer; + +/// Return a built-in [`RawSigner`] instance using the provided signing +/// certificate and private key. +/// +/// Which signers are available may vary depending on the platform and which +/// crate features were enabled. +/// +/// Returns `None` if the signing algorithm is unsupported. May return an `Err` +/// response if the certificate chain or private key are invalid. +pub(crate) fn signer_from_cert_chain_and_private_key( + cert_chain: &[u8], + private_key: &[u8], + alg: SigningAlg, + time_stamp_service_url: Option, +) -> Result, RawSignerError> { + match alg { + SigningAlg::Ed25519 => Ok(Box::new( + ed25519_signer::Ed25519Signer::from_cert_chain_and_private_key( + cert_chain, + private_key, + time_stamp_service_url, + )?, + )), + + _ => Err(RawSignerError::InternalError(format!( + "unsupported algorithm: {alg}" + ))), + } +} From 6cc1c0f24eb49486cb6a87c8656fee985ff6072c Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Mon, 9 Dec 2024 19:35:01 -0800 Subject: [PATCH 30/32] Use Ed25519 for test signatures since it's available on WASM --- sdk/src/builder.rs | 2 +- sdk/src/manifest.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/builder.rs b/sdk/src/builder.rs index 6692b18f6..8ac8aa072 100644 --- a/sdk/src/builder.rs +++ b/sdk/src/builder.rs @@ -1570,7 +1570,7 @@ mod tests { builder.add_assertion(labels::BOX_HASH, &box_hash).unwrap(); - let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); + let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ed25519); let manifest_bytes = builder .sign_box_hashed_embeddable_async(signer.as_ref(), "image/jpeg") diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 11c0af2ae..6e844ec3d 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -2145,7 +2145,7 @@ pub(crate) mod tests { )) .unwrap(); - let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); + let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ed25519); let mut output = Cursor::new(Vec::new()); // Embed a manifest using the signer. From 577f405899bac6d68d76831f2a55e73f3b16ec20 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Mon, 9 Dec 2024 19:40:21 -0800 Subject: [PATCH 31/32] Remove TO REVIEW comment that's no longer relevant --- sdk/src/callback_signer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/src/callback_signer.rs b/sdk/src/callback_signer.rs index 096249034..52f952949 100644 --- a/sdk/src/callback_signer.rs +++ b/sdk/src/callback_signer.rs @@ -27,7 +27,6 @@ use crate::{AsyncSigner, Error, Result, Signer}; /// The callback should return an error if the data cannot be signed. pub type CallbackFunc = dyn Fn(*const (), &[u8]) -> std::result::Result, Error> + Send + Sync; -// TO REVIEW: Changing the error type to RawSignerError /// Defines a signer that uses a callback to sign data. /// From 6241275d15e3b0b9f1148ba1d2e84dd50d98a127 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Mon, 9 Dec 2024 19:44:16 -0800 Subject: [PATCH 32/32] Use `crate::utils::test_signer::async_test_signer` --- sdk/src/manifest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 6e844ec3d..ebfa67153 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1523,7 +1523,7 @@ pub(crate) mod tests { reader::Reader, store::Store, utils::test::{temp_remote_signer, TEST_VC}, - utils::test_signer::test_signer, + utils::test_signer::{async_test_signer, test_signer}, Manifest, Result, }; #[cfg(feature = "file_io")] @@ -1898,7 +1898,7 @@ pub(crate) mod tests { let temp_dir = tempdir().expect("temp dir"); let output = temp_fixture_path(&temp_dir, TEST_SMALL_JPEG); - let async_signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ps256); + let async_signer = async_test_signer(SigningAlg::Ps256); let mut manifest = test_manifest(); manifest @@ -2145,7 +2145,7 @@ pub(crate) mod tests { )) .unwrap(); - let signer = crate::utils::test_signer::async_test_signer(SigningAlg::Ed25519); + let signer = async_test_signer(SigningAlg::Ed25519); let mut output = Cursor::new(Vec::new()); // Embed a manifest using the signer.