use thiserror::Error;
use crate::SigningAlg;
pub trait RawSigner {
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, RawSignerError>;
fn alg(&self) -> SigningAlg;
fn max_signature_size(&self) -> usize;
}
#[derive(Debug, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum RawSignerError {
#[error("invalid signing credentials ({0})")]
InvalidSigningCredentials(String),
#[error("an error was reported by the cryptography library: {0}")]
CryptoLibraryError(String),
#[error("unsupported signing algorithm: {0}")]
UnsupportedAlgorithm(SigningAlg),
#[error("no cryptography backend was enabled at build time")]
NoCryptoBackend,
#[error("internal error ({0})")]
InternalError(String),
}
#[cfg(feature = "openssl")]
impl From<openssl::error::ErrorStack> for RawSignerError {
fn from(err: openssl::error::ErrorStack) -> Self {
Self::CryptoLibraryError(err.to_string())
}
}
#[cfg(feature = "openssl")]
impl From<crate::openssl::OpenSslMutexUnavailable> for RawSignerError {
fn from(err: crate::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_private_key(
private_key: &[u8],
alg: SigningAlg,
) -> Result<Box<dyn RawSigner + Send + Sync>, RawSignerError> {
let private_key = fix_json_pem(private_key);
#[cfg(feature = "rust_native_crypto")]
{
crate::rust_native::signers::signer_from_private_key(&private_key, alg)
}
#[cfg(all(feature = "openssl", not(feature = "rust_native_crypto")))]
{
return crate::openssl::signers::signer_from_private_key(&private_key, alg);
}
#[cfg(not(any(feature = "rust_native_crypto", feature = "openssl")))]
Err(RawSignerError::NoCryptoBackend)
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
#![allow(clippy::panic)]
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn fix_json_pem_unescapes_newlines() {
let input = b"-----BEGIN PRIVATE KEY-----\\nABCD\\n-----END PRIVATE KEY-----\\n";
let fixed = fix_json_pem(input);
assert_eq!(
String::from_utf8(fixed).unwrap(),
"-----BEGIN PRIVATE KEY-----\nABCD\n-----END PRIVATE KEY-----\n"
);
}
#[test]
fn fix_json_pem_leaves_real_newlines_alone() {
let input = b"already\nclean";
assert_eq!(fix_json_pem(input), b"already\nclean");
}
#[test]
#[cfg(feature = "openssl")]
fn mutex_unavailable_maps_to_internal_error() {
let err: RawSignerError = crate::openssl::OpenSslMutexUnavailable.into();
assert!(matches!(err, RawSignerError::InternalError(_)));
}
#[test]
fn signer_from_private_key_rejects_garbage() {
assert!(signer_from_private_key(b"not a key", SigningAlg::Es256).is_err());
}
#[test]
#[cfg(any(feature = "rust_native_crypto", feature = "openssl"))]
fn signer_from_private_key_accepts_json_escaped_pem() {
let pem = std::str::from_utf8(include_bytes!("../tests/fixtures/raw_signature/es256.priv"))
.unwrap();
let escaped = pem.replace('\n', "\\n");
let signer = signer_from_private_key(escaped.as_bytes(), SigningAlg::Es256).unwrap();
assert_eq!(signer.alg(), SigningAlg::Es256);
}
}