diff --git a/oid4vc-core/src/authentication/sign.rs b/oid4vc-core/src/authentication/sign.rs index 6284a6dd..7ba7b29a 100644 --- a/oid4vc-core/src/authentication/sign.rs +++ b/oid4vc-core/src/authentication/sign.rs @@ -1,11 +1,13 @@ use anyhow::Result; +use async_trait::async_trait; use std::sync::Arc; +#[async_trait] pub trait Sign: Send + Sync { // TODO: add this? // fn jwt_alg_name() -> &'static str; - fn key_id(&self, subject_syntax_type: &str) -> Option; - fn sign(&self, message: &str, subject_syntax_type: &str) -> Result>; + async fn key_id(&self, subject_syntax_type: &str) -> Option; + async fn sign(&self, message: &str, subject_syntax_type: &str) -> Result>; fn external_signer(&self) -> Option>; } diff --git a/oid4vc-core/src/authentication/subject.rs b/oid4vc-core/src/authentication/subject.rs index 54ddc2fc..475020b4 100644 --- a/oid4vc-core/src/authentication/subject.rs +++ b/oid4vc-core/src/authentication/subject.rs @@ -1,11 +1,13 @@ use crate::{Sign, Verify}; use anyhow::Result; +use async_trait::async_trait; use std::sync::Arc; pub type SigningSubject = Arc; // TODO: Use a URI of some sort. /// This [`Subject`] trait is used to sign and verify JWTs. +#[async_trait] pub trait Subject: Sign + Verify + Send + Sync { - fn identifier(&self, subject_syntax_type: &str) -> Result; + async fn identifier(&self, subject_syntax_type: &str) -> Result; } diff --git a/oid4vc-core/src/openid4vc_extension.rs b/oid4vc-core/src/openid4vc_extension.rs index 3c46c947..76061f0a 100644 --- a/oid4vc-core/src/openid4vc_extension.rs +++ b/oid4vc-core/src/openid4vc_extension.rs @@ -29,9 +29,9 @@ pub trait Extension: Serialize + PartialEq + Sized + std::fmt::Debug + Clone + S _extension_parameters: &::Parameters, _user_input: &::Input, _subject_syntax_type: impl TryInto, - ) -> anyhow::Result> { + ) -> impl Future>> { // Will be overwritten by the extension. - Err(anyhow::anyhow!("Not implemented.")) + async { Err(anyhow::anyhow!("Not implemented.")) } } fn build_authorization_response( diff --git a/oid4vc-core/src/test_utils.rs b/oid4vc-core/src/test_utils.rs index 09adbf64..12a6f6e2 100644 --- a/oid4vc-core/src/test_utils.rs +++ b/oid4vc-core/src/test_utils.rs @@ -30,12 +30,13 @@ impl TestSubject { } } +#[async_trait] impl Sign for TestSubject { - fn key_id(&self, _subject_syntax_type: &str) -> Option { + async fn key_id(&self, _subject_syntax_type: &str) -> Option { Some(self.key_id.clone()) } - fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { + async fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { let signature: Signature = TEST_KEYPAIR.sign(message.as_bytes()); Ok(signature.to_bytes().to_vec()) } @@ -52,8 +53,9 @@ impl Verify for TestSubject { } } +#[async_trait] impl Subject for TestSubject { - fn identifier(&self, _subject_syntax_type: &str) -> Result { + async fn identifier(&self, _subject_syntax_type: &str) -> Result { Ok(self.did.to_string()) } } diff --git a/oid4vc-manager/src/managers/provider.rs b/oid4vc-manager/src/managers/provider.rs index e462581b..ff93abdf 100644 --- a/oid4vc-manager/src/managers/provider.rs +++ b/oid4vc-manager/src/managers/provider.rs @@ -28,12 +28,12 @@ impl ProviderManager { self.provider.validate_request(authorization_request).await } - pub fn generate_response( + pub async fn generate_response( &self, authorization_request: &AuthorizationRequest>, input: ::Input, ) -> Result> { - self.provider.generate_response(authorization_request, input) + self.provider.generate_response(authorization_request, input).await } pub async fn send_response( diff --git a/oid4vc-manager/src/managers/relying_party.rs b/oid4vc-manager/src/managers/relying_party.rs index 54a4945d..ab759d44 100644 --- a/oid4vc-manager/src/managers/relying_party.rs +++ b/oid4vc-manager/src/managers/relying_party.rs @@ -23,8 +23,11 @@ impl RelyingPartyManager { }) } - pub fn encode(&self, authorization_request: &AuthorizationRequest>) -> Result { - self.relying_party.encode(authorization_request) + pub async fn encode( + &self, + authorization_request: &AuthorizationRequest>, + ) -> Result { + self.relying_party.encode(authorization_request).await } pub async fn validate_response( diff --git a/oid4vc-manager/src/methods/key_method.rs b/oid4vc-manager/src/methods/key_method.rs index d693421f..f23f9c32 100644 --- a/oid4vc-manager/src/methods/key_method.rs +++ b/oid4vc-manager/src/methods/key_method.rs @@ -43,14 +43,14 @@ impl Default for KeySubject { #[async_trait] impl Sign for KeySubject { - fn key_id(&self, _subject_syntax_type: &str) -> Option { + async fn key_id(&self, _subject_syntax_type: &str) -> Option { self.document .authentication .as_ref() .and_then(|authentication_methods| authentication_methods.first().cloned()) } - fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { + async fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { match self.external_signer() { Some(external_signer) => external_signer.sign(message), None => Ok(self.keypair.sign(message.as_bytes())), @@ -69,8 +69,9 @@ impl Verify for KeySubject { } } +#[async_trait] impl Subject for KeySubject { - fn identifier(&self, _subject_syntax_type: &str) -> Result { + async fn identifier(&self, _subject_syntax_type: &str) -> Result { Ok(self.document.id.clone()) } } @@ -148,6 +149,7 @@ mod tests { // Test whether the provider manager can generate a authorization_response for the authorization_request succesfully. let authorization_response = provider_manager .generate_response(&authorization_request, Default::default()) + .await .unwrap(); // Let the relying party validate the authorization_response. diff --git a/oid4vc-manager/tests/common/memory_storage.rs b/oid4vc-manager/tests/common/memory_storage.rs index 41ea08d3..4dfb1c90 100644 --- a/oid4vc-manager/tests/common/memory_storage.rs +++ b/oid4vc-manager/tests/common/memory_storage.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, fs::File}; +use futures::executor::block_on; use jsonwebtoken::{Algorithm, Header}; use lazy_static::lazy_static; use oid4vc_core::{authentication::subject::SigningSubject, generate_authorization_code, jwt}; @@ -122,7 +123,7 @@ impl Storage for Memory (access_token == ACCESS_TOKEN.clone()).then_some(CredentialResponse { credential: CredentialResponseType::Immediate { - credential: serde_json::to_value( + credential: serde_json::to_value(block_on(async { jwt::encode( signer.clone(), Header::new(Algorithm::EdDSA), @@ -136,8 +137,9 @@ impl Storage for Memory .ok(), "did:key", ) - .ok(), - ) + .await + .ok() + })) .unwrap(), notification_id: None, }, diff --git a/oid4vc-manager/tests/common/mod.rs b/oid4vc-manager/tests/common/mod.rs index cdab8196..7e6a2390 100644 --- a/oid4vc-manager/tests/common/mod.rs +++ b/oid4vc-manager/tests/common/mod.rs @@ -37,11 +37,11 @@ impl TestSubject { #[async_trait] impl Sign for TestSubject { - fn key_id(&self, _subject_syntax_type: &str) -> Option { + async fn key_id(&self, _subject_syntax_type: &str) -> Option { Some(self.key_id.clone()) } - fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { + async fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { let signature: Signature = TEST_KEYPAIR.sign(message.as_bytes()); Ok(signature.to_bytes().to_vec()) } @@ -58,8 +58,9 @@ impl Verify for TestSubject { } } +#[async_trait] impl Subject for TestSubject { - fn identifier(&self, _subject_syntax_type: &str) -> Result { + async fn identifier(&self, _subject_syntax_type: &str) -> Result { Ok(self.did.to_string()) } } diff --git a/oid4vc-manager/tests/oid4vci/authorization_code.rs b/oid4vc-manager/tests/oid4vci/authorization_code.rs index 94d8fadc..b9b8c408 100644 --- a/oid4vc-manager/tests/oid4vci/authorization_code.rs +++ b/oid4vc-manager/tests/oid4vci/authorization_code.rs @@ -38,7 +38,7 @@ async fn test_authorization_code_flow() { // Create a new subject. let subject = KeySubject::new(); - let subject_did = subject.identifier("did:key").unwrap(); + let subject_did = subject.identifier("did:key").await.unwrap(); // Create a new wallet. let wallet = Wallet::new(Arc::new(subject), "did:key").unwrap(); diff --git a/oid4vc-manager/tests/oid4vci/pre_authorized_code.rs b/oid4vc-manager/tests/oid4vci/pre_authorized_code.rs index 6aa6d3ac..0105eece 100644 --- a/oid4vc-manager/tests/oid4vci/pre_authorized_code.rs +++ b/oid4vc-manager/tests/oid4vci/pre_authorized_code.rs @@ -42,7 +42,7 @@ async fn test_pre_authorized_code_flow(#[case] batch: bool, #[case] by_reference // Create a new subject. let subject = KeySubject::new(); - let subject_did = subject.identifier("did:key").unwrap(); + let subject_did = subject.identifier("did:key").await.unwrap(); // Create a new wallet. let wallet: Wallet = Wallet::new(Arc::new(subject), "did:key").unwrap(); diff --git a/oid4vc-manager/tests/oid4vp/implicit.rs b/oid4vc-manager/tests/oid4vp/implicit.rs index 308e0898..d8de19cf 100644 --- a/oid4vc-manager/tests/oid4vp/implicit.rs +++ b/oid4vc-manager/tests/oid4vp/implicit.rs @@ -77,18 +77,18 @@ async fn test_implicit_flow() { )), None, ); - let issuer_did = issuer.identifier("did:key").unwrap(); + let issuer_did = issuer.identifier("did:key").await.unwrap(); // Create a new subject. let subject = Arc::new(KeySubject::from_keypair( generate::(Some("this-is-a-very-UNSAFE-secret-key".as_bytes().try_into().unwrap())), None, )); - let subject_did = subject.identifier("did:key").unwrap(); + let subject_did = subject.identifier("did:key").await.unwrap(); // Create a new relying party. let relying_party = Arc::new(KeySubject::new()); - let relying_party_did = relying_party.identifier("did:key").unwrap(); + let relying_party_did = relying_party.identifier("did:key").await.unwrap(); let relying_party_manager = RelyingPartyManager::new(relying_party, "did:key").unwrap(); // Create authorization request with response_type `id_token vp_token` @@ -148,6 +148,7 @@ async fn test_implicit_flow() { &verifiable_credential, "did:key", ) + .await .unwrap(); // Create a verifiable presentation using the JWT. @@ -166,6 +167,7 @@ async fn test_implicit_flow() { presentation_submission, }, ) + .await .unwrap(); // Validate the authorization_response. diff --git a/oid4vc-manager/tests/siopv2/implicit.rs b/oid4vc-manager/tests/siopv2/implicit.rs index c1962177..48688c50 100644 --- a/oid4vc-manager/tests/siopv2/implicit.rs +++ b/oid4vc-manager/tests/siopv2/implicit.rs @@ -30,19 +30,20 @@ pub struct MultiDidMethodSubject { pub key_subject: KeySubject, } +#[async_trait] impl Sign for MultiDidMethodSubject { - fn key_id(&self, subject_syntax_type: &str) -> Option { + async fn key_id(&self, subject_syntax_type: &str) -> Option { match subject_syntax_type { - "did:test" => self.test_subject.key_id(subject_syntax_type), - "did:key" => self.key_subject.key_id(subject_syntax_type), + "did:test" => self.test_subject.key_id(subject_syntax_type).await, + "did:key" => self.key_subject.key_id(subject_syntax_type).await, _ => None, } } - fn sign(&self, message: &str, subject_syntax_type: &str) -> anyhow::Result> { + async fn sign(&self, message: &str, subject_syntax_type: &str) -> anyhow::Result> { match subject_syntax_type { - "did:test" => self.test_subject.sign(message, subject_syntax_type), - "did:key" => self.key_subject.sign(message, subject_syntax_type), + "did:test" => self.test_subject.sign(message, subject_syntax_type).await, + "did:key" => self.key_subject.sign(message, subject_syntax_type).await, _ => Err(anyhow::anyhow!("Unsupported DID method.")), } } @@ -63,11 +64,12 @@ impl Verify for MultiDidMethodSubject { } } +#[async_trait] impl Subject for MultiDidMethodSubject { - fn identifier(&self, subject_syntax_type: &str) -> anyhow::Result { + async fn identifier(&self, subject_syntax_type: &str) -> anyhow::Result { match subject_syntax_type { - "did:test" => self.test_subject.identifier(subject_syntax_type), - "did:key" => self.key_subject.identifier(subject_syntax_type), + "did:test" => self.test_subject.identifier(subject_syntax_type).await, + "did:key" => self.key_subject.identifier(subject_syntax_type).await, _ => Err(anyhow::anyhow!("Unsupported DID method.")), } } @@ -119,7 +121,7 @@ async fn test_implicit_flow(#[case] did_method: &str) { key_subject: KeySubject::from_keypair(generate::(None), None), }; - let client_id = subject.identifier(did_method).unwrap(); + let client_id = subject.identifier(did_method).await.unwrap(); // Create a new relying party manager. let relying_party_manager = RelyingPartyManager::new(Arc::new(subject), did_method).unwrap(); @@ -157,7 +159,8 @@ async fn test_implicit_flow(#[case] did_method: &str) { Mock::given(method("GET")) .and(path("/request_uri")) .respond_with( - ResponseTemplate::new(200).set_body_string(relying_party_manager.encode(&authorization_request).unwrap()), + ResponseTemplate::new(200) + .set_body_string(relying_party_manager.encode(&authorization_request).await.unwrap()), ) .mount(&mock_server) .await; @@ -228,6 +231,7 @@ async fn test_implicit_flow(#[case] did_method: &str) { // encoded as a JWT. let authorization_response: AuthorizationResponse = provider_manager .generate_response(&authorization_request, response_claims) + .await .unwrap(); // The provider manager sends it's authorization_response to the mock server's `redirect_uri` endpoint. diff --git a/oid4vci/src/proof.rs b/oid4vci/src/proof.rs index c3f7e7b0..acbe8002 100644 --- a/oid4vci/src/proof.rs +++ b/oid4vci/src/proof.rs @@ -49,7 +49,7 @@ pub struct ProofOfPossession { } impl ProofBuilder { - pub fn build(self) -> anyhow::Result { + pub async fn build(self) -> anyhow::Result { anyhow::ensure!(self.rfc7519_claims.aud.is_some(), "aud claim is required"); anyhow::ensure!(self.rfc7519_claims.iat.is_some(), "iat claim is required"); anyhow::ensure!(self.nonce.is_some(), "nonce claim is required"); @@ -72,7 +72,8 @@ impl ProofBuilder { nonce: self.nonce.ok_or(anyhow::anyhow!("No nonce found"))?, }, &subject_syntax_type, - )?, + ) + .await?, }), Some(ProofType::Cwt) => todo!(), None => Err(anyhow::anyhow!("proof_type is required")), diff --git a/oid4vci/src/wallet/mod.rs b/oid4vci/src/wallet/mod.rs index 128b6cc6..c706b903 100644 --- a/oid4vci/src/wallet/mod.rs +++ b/oid4vci/src/wallet/mod.rs @@ -113,7 +113,10 @@ impl Wallet { // TODO: must be `form`, but `AuthorizationRequest needs to be able to serilalize properly. .json(&AuthorizationRequest { response_type: "code".to_string(), - client_id: self.subject.identifier(&self.default_subject_syntax_type.to_string())?, + client_id: self + .subject + .identifier(&self.default_subject_syntax_type.to_string()) + .await?, redirect_uri: None, scope: None, state: None, @@ -149,7 +152,11 @@ impl Wallet { KeyProofType::builder() .proof_type(ProofType::Jwt) .signer(self.subject.clone()) - .iss(self.subject.identifier(&self.default_subject_syntax_type.to_string())?) + .iss( + self.subject + .identifier(&self.default_subject_syntax_type.to_string()) + .await?, + ) .aud(credential_issuer_metadata.credential_issuer) .iat(1571324800) .exp(9999999999i64) @@ -162,7 +169,8 @@ impl Wallet { .clone(), ) .subject_syntax_type(self.default_subject_syntax_type.to_string()) - .build()?, + .build() + .await?, ), }; @@ -187,7 +195,11 @@ impl Wallet { KeyProofType::builder() .proof_type(ProofType::Jwt) .signer(self.subject.clone()) - .iss(self.subject.identifier(&self.default_subject_syntax_type.to_string())?) + .iss( + self.subject + .identifier(&self.default_subject_syntax_type.to_string()) + .await?, + ) .aud(credential_issuer_metadata.credential_issuer) .iat(1571324800) .exp(9999999999i64) @@ -200,7 +212,8 @@ impl Wallet { .clone(), ) .subject_syntax_type(self.default_subject_syntax_type.to_string()) - .build()?, + .build() + .await?, ); let batch_credential_request = BatchCredentialRequest { diff --git a/oid4vp/src/oid4vp.rs b/oid4vp/src/oid4vp.rs index 57bc668a..698ac516 100644 --- a/oid4vp/src/oid4vp.rs +++ b/oid4vp/src/oid4vp.rs @@ -6,7 +6,7 @@ pub use dif_presentation_exchange::{ evaluate_input, ClaimFormatDesignation, InputDescriptor, InputDescriptorMappingObject, PathNested, PresentationDefinition, PresentationSubmission, }; -use futures::{executor::block_on, future::join_all}; +use futures::future::join_all; use identity_credential::{credential::Jwt, presentation::Presentation}; use jsonwebtoken::{Algorithm, Header}; use oid4vc_core::openid4vc_extension::{OpenID4VC, RequestHandle, ResponseHandle}; @@ -41,7 +41,7 @@ impl Extension for OID4VP { type RequestHandle = RequestHandler; type ResponseHandle = ResponseHandler; - fn generate_token( + async fn generate_token( subject: Arc, client_id: &str, extension_parameters: &::Parameters, @@ -52,7 +52,7 @@ impl Extension for OID4VP { .try_into() .map_err(|_| anyhow::anyhow!("Failed to convert the subject syntax type"))? .to_string(); - let subject_identifier = subject.identifier(&subject_syntax_type_string)?; + let subject_identifier = subject.identifier(&subject_syntax_type_string).await?; let vp_token = VpToken::builder() .iss(subject_identifier.clone()) @@ -70,7 +70,8 @@ impl Extension for OID4VP { Header::new(Algorithm::EdDSA), vp_token, &subject_syntax_type_string, - )?; + ) + .await?; Ok(vec![jwt]) } @@ -98,7 +99,7 @@ impl Extension for OID4VP { ) -> anyhow::Result<::ResponseItem> { let vp_token: VpToken = match &response.extension.oid4vp_parameters { Oid4vpParams::Jwt { .. } => todo!(), - Oid4vpParams::Params { vp_token, .. } => block_on(validator.decode(vp_token.to_owned()))?, + Oid4vpParams::Params { vp_token, .. } => validator.decode(vp_token.to_owned()).await?, }; join_all( diff --git a/siopv2/src/provider.rs b/siopv2/src/provider.rs index b9482d62..68675f3f 100644 --- a/siopv2/src/provider.rs +++ b/siopv2/src/provider.rs @@ -82,7 +82,7 @@ impl Provider { /// Generates an [`AuthorizationResponse`] in response to an [`AuthorizationRequest`] and the user's claims. The [`AuthorizationResponse`] /// contains an [`IdToken`], which is signed by the [`Subject`] of the [`Provider`]. - pub fn generate_response( + pub async fn generate_response( &self, authorization_request: &AuthorizationRequest>, input: ::Input, @@ -96,7 +96,8 @@ impl Provider { &authorization_request.body.extension, &input, self.default_subject_syntax_type.clone(), - )?; + ) + .await?; E::build_authorization_response(jwts, input, redirect_uri, state) } @@ -153,6 +154,7 @@ mod tests { // Test whether the provider can generate a authorization_response for the authorization_request succesfully. assert!(provider .generate_response(&authorization_request, Default::default()) + .await .is_ok()); } } diff --git a/siopv2/src/relying_party.rs b/siopv2/src/relying_party.rs index 3f30304c..6fea0036 100644 --- a/siopv2/src/relying_party.rs +++ b/siopv2/src/relying_party.rs @@ -31,13 +31,17 @@ impl RelyingParty { }) } - pub fn encode(&self, authorization_request: &AuthorizationRequest>) -> Result { + pub async fn encode( + &self, + authorization_request: &AuthorizationRequest>, + ) -> Result { jwt::encode( self.subject.clone(), Header::new(Algorithm::EdDSA), authorization_request, &self.default_subject_syntax_type.to_string(), ) + .await } /// Validates a [`AuthorizationResponse`] by decoding the header of the id_token, fetching the public key corresponding to diff --git a/siopv2/src/siopv2.rs b/siopv2/src/siopv2.rs index fe875035..72544c7d 100644 --- a/siopv2/src/siopv2.rs +++ b/siopv2/src/siopv2.rs @@ -34,7 +34,7 @@ impl Extension for SIOPv2 { type RequestHandle = RequestHandler; type ResponseHandle = ResponseHandler; - fn generate_token( + async fn generate_token( subject: Arc, client_id: &str, extension_parameters: &::Parameters, @@ -45,7 +45,7 @@ impl Extension for SIOPv2 { .try_into() .map_err(|_| anyhow::anyhow!("Failed to convert the subject syntax type"))? .to_string(); - let subject_identifier = subject.identifier(&subject_syntax_type_string)?; + let subject_identifier = subject.identifier(&subject_syntax_type_string).await?; let id_token = IdToken::builder() .iss(subject_identifier.clone()) @@ -63,7 +63,8 @@ impl Extension for SIOPv2 { Header::new(Algorithm::EdDSA), id_token, &subject_syntax_type_string, - )?; + ) + .await?; Ok(vec![jwt]) } diff --git a/siopv2/src/test_utils.rs b/siopv2/src/test_utils.rs index d3c93608..e449e892 100644 --- a/siopv2/src/test_utils.rs +++ b/siopv2/src/test_utils.rs @@ -30,12 +30,13 @@ impl TestSubject { } } +#[async_trait] impl Sign for TestSubject { - fn key_id(&self, _subject_syntax_type: &str) -> Option { + async fn key_id(&self, _subject_syntax_type: &str) -> Option { Some(self.key_id.clone()) } - fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { + async fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result> { let signature: Signature = TEST_KEYPAIR.sign(message.as_bytes()); Ok(signature.to_bytes().to_vec()) } @@ -52,8 +53,9 @@ impl Verify for TestSubject { } } +#[async_trait] impl Subject for TestSubject { - fn identifier(&self, _subject_syntax_type: &str) -> Result { + async fn identifier(&self, _subject_syntax_type: &str) -> Result { Ok(self.did.to_string()) } }