use std::sync::Arc;
use bergshamra_dsig::{sign::sign, DsigContext};
use bergshamra_keys::{KeyUsage, KeysManager};
use kryptering::{SignatureAlgorithm, Signer};
use crate::crypto::error::CryptoError;
const DEFAULT_SIGNATURE_METHOD_URI: &str = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
pub struct SamlSigner {
keys_manager: KeysManager,
hsm_signer: Option<Arc<dyn Signer>>,
}
struct SharedSigner(Arc<dyn Signer>);
impl Signer for SharedSigner {
fn algorithm(&self) -> SignatureAlgorithm {
self.0.algorithm()
}
fn sign(&self, data: &[u8]) -> kryptering::Result<Vec<u8>> {
self.0.sign(data)
}
}
impl SamlSigner {
pub fn new(keys_manager: KeysManager) -> Self {
Self {
keys_manager,
hsm_signer: None,
}
}
pub fn with_hsm_signer(keys_manager: KeysManager, signer: Arc<dyn Signer>) -> Self {
Self {
keys_manager,
hsm_signer: Some(signer),
}
}
pub fn is_hsm_backed(&self) -> bool {
self.hsm_signer.is_some()
}
pub fn signature_method_uri(&self) -> Result<&'static str, CryptoError> {
if let Some(signer) = &self.hsm_signer {
return bergshamra_crypto::sign::kryptering_algorithm_uri(signer.algorithm())
.ok_or_else(|| {
CryptoError::UnsupportedAlgorithm(
"No XML-DSig SignatureMethod URI mapping exists for the configured HSM signer algorithm".to_string(),
)
});
}
Ok(DEFAULT_SIGNATURE_METHOD_URI)
}
pub fn sign_enveloped(&self, xml_with_template: &str) -> Result<String, CryptoError> {
let mut ctx = DsigContext::new(self.keys_manager.clone());
if let Some(signer) = &self.hsm_signer {
ctx = ctx.with_hsm_signer(Box::new(SharedSigner(Arc::clone(signer))));
}
let signed = sign(&ctx, xml_with_template)?;
Ok(signed)
}
pub fn sign_redirect_query(
&self,
query_string: &[u8],
algorithm_uri: &str,
) -> Result<Vec<u8>, CryptoError> {
if let Some(signer) = &self.hsm_signer {
let declared = bergshamra_crypto::sign::kryptering_algorithm_uri(signer.algorithm());
if declared != Some(algorithm_uri) {
return Err(CryptoError::UnsupportedAlgorithm(format!(
"HSM signer algorithm (SigAlg {}) does not match requested SigAlg {}",
declared.unwrap_or("<unmapped>"),
algorithm_uri,
)));
}
return signer
.sign(query_string)
.map_err(|e| CryptoError::HsmError(e.to_string()));
}
let sig_alg = bergshamra_crypto::sign::from_uri(algorithm_uri)
.map_err(CryptoError::BergshamraError)?;
let key = self
.keys_manager
.find_by_usage(KeyUsage::Sign)
.ok_or_else(|| CryptoError::KeyNotFound("No signing key found".to_string()))?;
let signing_key = key
.to_signing_key()
.map_err(CryptoError::BergshamraError)?
.ok_or_else(|| {
CryptoError::KeyNotFound("Key cannot be used for signing".to_string())
})?;
let signature = sig_alg
.sign(&signing_key, query_string)
.map_err(CryptoError::BergshamraError)?;
Ok(signature)
}
pub fn keys_manager(&self) -> &KeysManager {
&self.keys_manager
}
}