diff --git a/src/verifier/client.rs b/src/verifier/client.rs index 1488561..94fcb11 100644 --- a/src/verifier/client.rs +++ b/src/verifier/client.rs @@ -37,13 +37,13 @@ pub trait Client: Debug { pub struct DIDClient { id: ClientId, vm: String, - signer: Arc, + signer: Arc + Send + Sync>, } impl DIDClient { pub async fn new( vm: String, - signer: Arc, + signer: Arc + Send + Sync>, resolver: impl JWKResolver, ) -> Result { let (id, _f) = vm.rsplit_once('#').context(format!( @@ -55,7 +55,7 @@ impl DIDClient { .await .context("unable to resolve key from verification method")?; - if &*jwk != signer.jwk() { + if *jwk != signer.jwk().context("signer did not have a JWK")? { bail!( "verification method resolved from DID document did not match public key of signer" ) @@ -74,14 +74,14 @@ impl DIDClient { pub struct X509SanClient { id: ClientId, x5c: Vec, - signer: Arc, + signer: Arc + Send + Sync>, variant: X509SanVariant, } impl X509SanClient { pub fn new( x5c: Vec, - signer: Arc, + signer: Arc + Send + Sync>, variant: X509SanVariant, ) -> Result { let leaf = &x5c[0]; @@ -145,7 +145,10 @@ impl Client for DIDClient { &self, body: &AuthorizationRequestObject, ) -> Result { - let algorithm = self.signer.alg(); + let algorithm = self + .signer + .alg() + .context("failed to retrieve signing algorithm")?; let header = json!({ "alg": algorithm, "kid": self.vm, @@ -172,7 +175,10 @@ impl Client for X509SanClient { &self, body: &AuthorizationRequestObject, ) -> Result { - let algorithm = self.signer.alg(); + let algorithm = self + .signer + .alg() + .context("failed to retrieve signing algorithm")?; let x5c: Vec = self .x5c .iter() diff --git a/src/verifier/request_signer.rs b/src/verifier/request_signer.rs index 3b0550d..a02d8ba 100644 --- a/src/verifier/request_signer.rs +++ b/src/verifier/request_signer.rs @@ -3,17 +3,34 @@ use anyhow::Result; use async_trait::async_trait; #[cfg(feature = "p256")] use p256::ecdsa::{signature::Signer, Signature, SigningKey}; +#[cfg(feature = "p256")] +use ssi_claims::jws::{JWSSigner, JWSSignerInfo}; +#[cfg(feature = "p256")] +use ssi_jwk::Algorithm; + use ssi_jwk::JWK; use std::fmt::Debug; #[async_trait] pub trait RequestSigner: Debug { + type Error: std::fmt::Display; + /// The algorithm that will be used to sign. - fn alg(&self) -> &str; + fn alg(&self) -> Result; + /// The public JWK of the signer. - fn jwk(&self) -> &JWK; + fn jwk(&self) -> Result; + + /// Sign the payload and return the signature. async fn sign(&self, payload: &[u8]) -> Vec; + + /// Attempt to sign the payload and return the signature. + async fn try_sign(&self, payload: &[u8]) -> Result, Self::Error> { + // default implementation will call sign. + // Override for custom error handling. + Ok(self.sign(payload).await) + } } #[cfg(feature = "p256")] @@ -30,17 +47,28 @@ impl P256Signer { let jwk = serde_json::from_str(&pk.to_jwk_string())?; Ok(Self { key, jwk }) } + + pub fn jwk(&self) -> &JWK { + &self.jwk + } } #[cfg(feature = "p256")] #[async_trait] impl RequestSigner for P256Signer { - fn alg(&self) -> &str { - "ES256" + type Error = anyhow::Error; + + fn alg(&self) -> Result { + Ok(self + .jwk + .algorithm + .map(|alg| alg) + .unwrap_or(Algorithm::ES256) + .to_string()) } - fn jwk(&self) -> &JWK { - &self.jwk + fn jwk(&self) -> Result { + Ok(self.jwk.clone()) } async fn sign(&self, payload: &[u8]) -> Vec { @@ -48,3 +76,23 @@ impl RequestSigner for P256Signer { sig.to_vec() } } + +#[cfg(feature = "p256")] +impl JWSSigner for P256Signer { + async fn fetch_info(&self) -> std::result::Result { + let algorithm = self.jwk.algorithm.unwrap_or(Algorithm::ES256); + + let key_id = self.jwk.key_id.clone(); + + Ok(JWSSignerInfo { algorithm, key_id }) + } + + async fn sign_bytes( + &self, + signing_bytes: &[u8], + ) -> std::result::Result, ssi_claims::SignatureError> { + self.try_sign(signing_bytes).await.map_err(|e| { + ssi_claims::SignatureError::Other(format!("Failed to sign bytes: {}", e).into()) + }) + } +}