use async_trait::async_trait;
use thiserror::Error;
use crate::{
crypto::{
raw_signature::SigningAlg,
time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider},
},
maybe_send_sync::{MaybeSend, MaybeSync},
};
pub trait RawSigner: TimeStampProvider {
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, RawSignerError>;
fn alg(&self) -> SigningAlg;
fn cert_chain(&self) -> Result<Vec<Vec<u8>>, RawSignerError>;
fn reserve_size(&self) -> usize;
fn ocsp_response(&self) -> Option<Vec<u8>> {
None
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait AsyncRawSigner: AsyncTimeStampProvider + MaybeSync + MaybeSend {
async fn sign(&self, data: Vec<u8>) -> Result<Vec<u8>, RawSignerError>;
fn alg(&self) -> SigningAlg;
fn cert_chain(&self) -> Result<Vec<Vec<u8>>, RawSignerError>;
fn reserve_size(&self) -> usize;
async fn ocsp_response(&self) -> Option<Vec<u8>> {
None
}
}
#[derive(Debug, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum RawSignerError {
#[error("invalid signing credentials ({0})")]
InvalidSigningCredentials(String),
#[error("I/O error ({0})")]
IoError(String),
#[error("an error was reported by the cryptography library: {0}")]
CryptoLibraryError(String),
#[error("internal error ({0})")]
InternalError(String),
}
impl From<std::io::Error> for RawSignerError {
fn from(err: std::io::Error) -> Self {
Self::IoError(err.to_string())
}
}
#[cfg(all(
feature = "openssl",
not(all(feature = "rust_native_crypto", target_arch = "wasm32"))
))]
impl From<openssl::error::ErrorStack> for RawSignerError {
fn from(err: openssl::error::ErrorStack) -> Self {
Self::CryptoLibraryError(err.to_string())
}
}
#[cfg(all(
feature = "openssl",
not(all(feature = "rust_native_crypto", target_arch = "wasm32"))
))]
impl From<crate::crypto::raw_signature::openssl::OpenSslMutexUnavailable> for RawSignerError {
fn from(err: crate::crypto::raw_signature::openssl::OpenSslMutexUnavailable) -> Self {
Self::InternalError(err.to_string())
}
}
fn fix_json_pem(data: &[u8]) -> Vec<u8> {
String::from_utf8_lossy(data)
.replace("\\n", "\n")
.into_bytes()
}
#[allow(unused)] pub fn signer_from_cert_chain_and_private_key(
cert_chain: &[u8],
private_key: &[u8],
alg: SigningAlg,
time_stamp_service_url: Option<String>,
) -> Result<Box<dyn RawSigner + Send + Sync>, RawSignerError> {
let cert_chain = fix_json_pem(cert_chain);
let private_key = fix_json_pem(private_key);
#[cfg(any(feature = "rust_native_crypto", target_arch = "wasm32"))]
{
match crate::crypto::raw_signature::rust_native::signers::signer_from_cert_chain_and_private_key(
&cert_chain,
&private_key,
alg,
time_stamp_service_url.clone(),
) {
Ok(signer) => return Ok(signer),
Err(RawSignerError::InternalError(_)) => (),
Err(err) => return Err(err),
}
}
#[cfg(all(
feature = "openssl",
not(all(feature = "rust_native_crypto", target_arch = "wasm32"))
))]
{
return crate::crypto::raw_signature::openssl::signers::signer_from_cert_chain_and_private_key(
&cert_chain,
&private_key,
alg,
time_stamp_service_url,
);
}
Err(RawSignerError::InternalError(format!(
"unsupported algorithm: {alg}"
)))
}
pub fn async_signer_from_cert_chain_and_private_key(
cert_chain: &[u8],
private_key: &[u8],
alg: SigningAlg,
time_stamp_service_url: Option<String>,
) -> Result<Box<dyn AsyncRawSigner + Send + Sync>, RawSignerError> {
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<dyn RawSigner + Send + Sync>);
#[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<u8>) -> Result<Vec<u8>, RawSignerError> {
self.0.sign(&data)
}
fn alg(&self) -> SigningAlg {
self.0.alg()
}
fn cert_chain(&self) -> Result<Vec<Vec<u8>>, RawSignerError> {
self.0.cert_chain()
}
fn reserve_size(&self) -> usize {
self.0.reserve_size()
}
async fn ocsp_response(&self) -> Option<Vec<u8>> {
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<String> {
self.0.time_stamp_service_url()
}
fn time_stamp_request_headers(&self) -> Option<Vec<(String, String)>> {
self.0.time_stamp_request_headers()
}
fn time_stamp_request_body(&self, message: &[u8]) -> Result<Vec<u8>, TimeStampError> {
self.0.time_stamp_request_body(message)
}
async fn send_time_stamp_request(
&self,
message: &[u8],
) -> Option<Result<Vec<u8>, TimeStampError>> {
self.0.send_time_stamp_request(message)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
pub(crate) fn test_signer(alg: SigningAlg) -> Box<dyn RawSigner> {
let (cert_chain, private_key) = match alg {
SigningAlg::Ed25519 => (
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ed25519.pub").as_slice(),
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ed25519.priv").as_slice(),
),
SigningAlg::Es256 => (
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es256.pub").as_slice(),
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es256.priv").as_slice(),
),
SigningAlg::Es384 => (
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es384.pub").as_slice(),
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es384.priv").as_slice(),
),
SigningAlg::Es512 => (
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es512.pub").as_slice(),
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es512.priv").as_slice(),
),
SigningAlg::Ps256 => (
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps256.pub").as_slice(),
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps256.priv").as_slice(),
),
SigningAlg::Ps384 => (
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps384.pub").as_slice(),
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps384.priv").as_slice(),
),
SigningAlg::Ps512 => (
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps512.pub").as_slice(),
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps512.priv").as_slice(),
),
};
signer_from_cert_chain_and_private_key(cert_chain, private_key, alg, None).unwrap()
}