use core::fmt;
use super::{KeyId, KeyStore, KeyStoreError};
use crate::jose::{Jwk, JwsAlgorithm, SignError, Signer};
#[derive(Clone)]
pub struct KeySigner<S> {
store: S,
key: KeyId,
alg: JwsAlgorithm,
public: Jwk,
kid: Option<String>,
}
impl<S: KeyStore> KeySigner<S> {
pub async fn new(store: S, key: KeyId, alg: JwsAlgorithm) -> Result<Self, KeyStoreError> {
if !store.capabilities().supports(alg) {
return Err(KeyStoreError::UnsupportedAlgorithm(alg));
}
let public = store.public_jwk(&key).await?;
check_alg(&key, &public, alg)?;
Ok(Self {
store,
key,
alg,
public,
kid: None,
})
}
pub fn from_public_jwk(
store: S,
key: KeyId,
alg: JwsAlgorithm,
public: Jwk,
) -> Result<Self, KeyStoreError> {
if !store.capabilities().supports(alg) {
return Err(KeyStoreError::UnsupportedAlgorithm(alg));
}
if public.is_private() {
return Err(KeyStoreError::invalid_key(
key,
"the given public JWK carries private parameters",
));
}
if !fits(&public, alg) {
return Err(KeyStoreError::invalid_key(
key,
format!(
"the given public JWK (`kty` {}, `crv` {}) is not a key for {alg}",
public.kty(),
public.crv().unwrap_or("absent"),
),
));
}
check_alg(&key, &public, 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) -> &S {
&self.store
}
pub fn key_id(&self) -> &KeyId {
&self.key
}
pub fn public_jwk(&self) -> &Jwk {
&self.public
}
}
fn check_alg(key: &KeyId, public: &Jwk, alg: JwsAlgorithm) -> Result<(), KeyStoreError> {
let Some(key_alg) = public.alg() else {
return Ok(());
};
let key_alg: JwsAlgorithm = key_alg.parse().map_err(|_| {
KeyStoreError::invalid_key(
key.clone(),
format!("unknown algorithm `{key_alg}` in its public JWK"),
)
})?;
if key_alg == alg {
Ok(())
} else {
Err(KeyStoreError::AlgorithmMismatch {
key: key.clone(),
key_alg,
requested: alg,
})
}
}
fn fits(public: &Jwk, alg: JwsAlgorithm) -> bool {
match alg {
JwsAlgorithm::EdDSA => public.kty() == "OKP" && public.crv() == Some("Ed25519"),
JwsAlgorithm::ES256 => public.kty() == "EC" && public.crv() == Some("P-256"),
JwsAlgorithm::ES256K => public.kty() == "EC" && public.crv() == Some("secp256k1"),
JwsAlgorithm::ES384 => public.kty() == "EC" && public.crv() == Some("P-384"),
JwsAlgorithm::RS256 | JwsAlgorithm::PS256 => public.kty() == "RSA",
}
}
impl<S> fmt::Debug for KeySigner<S> {
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<S: KeyStore> Signer for KeySigner<S> {
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)
}
}