use core::fmt;
use super::{KeyId, KeyStore, KeyStoreError};
use crate::jose::{Jwk, JwsAlgorithm, SignError, Signer};
pub struct KeySigner<'a> {
store: &'a dyn KeyStore,
key: KeyId,
alg: JwsAlgorithm,
public: Jwk,
kid: Option<String>,
}
impl<'a> KeySigner<'a> {
pub async fn new(
store: &'a dyn KeyStore,
key: KeyId,
alg: JwsAlgorithm,
) -> Result<Self, KeyStoreError> {
if !store.capabilities().supports(alg) {
return Err(KeyStoreError::UnsupportedAlgorithm(alg));
}
let public = store.public_jwk(&key).await?;
if let Some(key_alg) = public.alg() {
let key_alg: JwsAlgorithm = key_alg.parse().map_err(|_| KeyStoreError::InvalidKey {
key: key.clone(),
reason: format!("unknown algorithm `{key_alg}` in its public JWK"),
})?;
if key_alg != alg {
return Err(KeyStoreError::AlgorithmMismatch {
key,
key_alg,
requested: alg,
});
}
}
Ok(Self {
store,
key,
alg,
public,
kid: None,
})
}
#[must_use]
pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
self.kid = Some(kid.into());
self
}
pub fn store(&self) -> &'a dyn KeyStore {
self.store
}
pub fn key_id(&self) -> &KeyId {
&self.key
}
pub fn public_jwk(&self) -> &Jwk {
&self.public
}
}
impl fmt::Debug for KeySigner<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KeySigner")
.field("key", &self.key)
.field("alg", &self.alg)
.field("public", &self.public)
.field("kid", &self.kid)
.finish_non_exhaustive()
}
}
#[async_trait::async_trait]
impl Signer for KeySigner<'_> {
fn alg(&self) -> JwsAlgorithm {
self.alg
}
fn kid(&self) -> Option<&str> {
Some(self.kid.as_deref().unwrap_or(self.key.as_str()))
}
async fn sign(&self, signing_input: &[u8]) -> Result<Vec<u8>, SignError> {
self.store
.sign(&self.key, self.alg, signing_input)
.await
.map_err(SignError::new)
}
}