use async_trait::async_trait;
use bcder::Oid;
use thiserror::Error;
use crate::raw_signature::{oids::*, SigningAlg};
pub trait RawSignatureValidator {
fn validate(
&self,
sig: &[u8],
data: &[u8],
public_key: &[u8],
) -> Result<(), RawSignatureValidationError>;
}
#[async_trait(?Send)]
pub trait AsyncRawSignatureValidator {
async fn validate_async(
&self,
sig: &[u8],
data: &[u8],
public_key: &[u8],
) -> Result<(), RawSignatureValidationError>;
}
pub fn validator_for_signing_alg(alg: SigningAlg) -> Option<Box<dyn RawSignatureValidator>> {
#[cfg(any(target_arch = "wasm32", feature = "rust_native_crypto"))]
{
if let Some(validator) =
crate::raw_signature::rust_native::validators::validator_for_signing_alg(alg)
{
return Some(validator);
}
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(validator) =
crate::raw_signature::openssl::validators::validator_for_signing_alg(alg)
{
return Some(validator);
}
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
if let Some(validator) =
crate::raw_signature::webcrypto::validators::validator_for_signing_alg(alg)
{
return Some(validator);
}
let _ = alg; None
}
pub(crate) fn validator_for_sig_and_hash_algs(
sig_alg: &Oid,
hash_alg: &Oid,
) -> Option<Box<dyn RawSignatureValidator>> {
if sig_alg.as_ref() == RSA_OID.as_bytes()
|| sig_alg.as_ref() == SHA256_WITH_RSAENCRYPTION_OID.as_bytes()
|| sig_alg.as_ref() == SHA384_WITH_RSAENCRYPTION_OID.as_bytes()
|| sig_alg.as_ref() == SHA512_WITH_RSAENCRYPTION_OID.as_bytes()
{
#[cfg(any(target_arch = "wasm32", feature = "rust_native_crypto"))]
{
if let Some(validator) =
crate::raw_signature::rust_native::validators::validator_for_sig_and_hash_algs(
sig_alg, hash_alg,
)
{
return Some(validator);
}
}
#[cfg(not(target_arch = "wasm32"))]
if let Some(validator) =
crate::raw_signature::openssl::validators::validator_for_sig_and_hash_algs(
sig_alg, hash_alg,
)
{
return Some(validator);
}
} else if sig_alg.as_ref() == EC_PUBLICKEY_OID.as_bytes()
|| sig_alg.as_ref() == ECDSA_WITH_SHA256_OID.as_bytes()
|| sig_alg.as_ref() == ECDSA_WITH_SHA384_OID.as_bytes()
|| sig_alg.as_ref() == ECDSA_WITH_SHA512_OID.as_bytes()
{
if hash_alg.as_ref() == SHA256_OID.as_bytes() {
return validator_for_signing_alg(SigningAlg::Es256);
} else if hash_alg.as_ref() == SHA384_OID.as_bytes() {
return validator_for_signing_alg(SigningAlg::Es384);
} else if hash_alg.as_ref() == SHA512_OID.as_bytes() {
return validator_for_signing_alg(SigningAlg::Es512);
}
} else if sig_alg.as_ref() == ED25519_OID.as_bytes() {
return validator_for_signing_alg(SigningAlg::Ed25519);
}
None
}
#[cfg(target_arch = "wasm32")]
pub fn async_validator_for_signing_alg(
alg: SigningAlg,
) -> Option<Box<dyn AsyncRawSignatureValidator>> {
crate::raw_signature::webcrypto::async_validator_for_signing_alg(alg)
}
#[derive(Debug, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum RawSignatureValidationError {
#[error("the signature does not match the provided data or public key")]
SignatureMismatch,
#[error("an error was reported by the cryptography library: {0}")]
CryptoLibraryError(String),
#[error("invalid public key")]
InvalidPublicKey,
#[error("invalid signature value")]
InvalidSignature,
#[error("signature uses an unsupported algorithm")]
UnsupportedAlgorithm,
#[error("internal error ({0})")]
InternalError(String),
}
#[cfg(not(target_arch = "wasm32"))]
impl From<openssl::error::ErrorStack> for RawSignatureValidationError {
fn from(err: openssl::error::ErrorStack) -> Self {
Self::CryptoLibraryError(err.to_string())
}
}
#[cfg(not(target_arch = "wasm32"))]
impl From<crate::raw_signature::openssl::OpenSslMutexUnavailable> for RawSignatureValidationError {
fn from(err: crate::raw_signature::openssl::OpenSslMutexUnavailable) -> Self {
Self::InternalError(err.to_string())
}
}
#[cfg(target_arch = "wasm32")]
impl From<crate::raw_signature::webcrypto::WasmCryptoError> for RawSignatureValidationError {
fn from(err: crate::raw_signature::webcrypto::WasmCryptoError) -> Self {
match err {
crate::raw_signature::webcrypto::WasmCryptoError::UnknownContext => {
Self::InternalError("unknown WASM context".to_string())
}
crate::raw_signature::webcrypto::WasmCryptoError::NoCryptoAvailable => {
Self::InternalError("WASM crypto unavailable".to_string())
}
}
}
}