Skip to content

Commit

Permalink
feat: make oid4vc-core trait methods async
Browse files Browse the repository at this point in the history
  • Loading branch information
nanderstabel committed May 7, 2024
1 parent 35b4f8f commit 048f2fb
Show file tree
Hide file tree
Showing 20 changed files with 102 additions and 58 deletions.
6 changes: 4 additions & 2 deletions oid4vc-core/src/authentication/sign.rs
Original file line number Diff line number Diff line change
@@ -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<String>;
fn sign(&self, message: &str, subject_syntax_type: &str) -> Result<Vec<u8>>;
async fn key_id(&self, subject_syntax_type: &str) -> Option<String>;
async fn sign(&self, message: &str, subject_syntax_type: &str) -> Result<Vec<u8>>;
fn external_signer(&self) -> Option<Arc<dyn ExternalSign>>;
}

Expand Down
4 changes: 3 additions & 1 deletion oid4vc-core/src/authentication/subject.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::{Sign, Verify};
use anyhow::Result;
use async_trait::async_trait;
use std::sync::Arc;

pub type SigningSubject = Arc<dyn Subject>;

// 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<String>;
async fn identifier(&self, subject_syntax_type: &str) -> Result<String>;
}
4 changes: 2 additions & 2 deletions oid4vc-core/src/openid4vc_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ pub trait Extension: Serialize + PartialEq + Sized + std::fmt::Debug + Clone + S
_extension_parameters: &<Self::RequestHandle as RequestHandle>::Parameters,
_user_input: &<Self::ResponseHandle as ResponseHandle>::Input,
_subject_syntax_type: impl TryInto<SubjectSyntaxType>,
) -> anyhow::Result<Vec<String>> {
) -> impl Future<Output = anyhow::Result<Vec<String>>> {
// Will be overwritten by the extension.
Err(anyhow::anyhow!("Not implemented."))
async { Err(anyhow::anyhow!("Not implemented.")) }
}

fn build_authorization_response(
Expand Down
8 changes: 5 additions & 3 deletions oid4vc-core/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ impl TestSubject {
}
}

#[async_trait]
impl Sign for TestSubject {
fn key_id(&self, _subject_syntax_type: &str) -> Option<String> {
async fn key_id(&self, _subject_syntax_type: &str) -> Option<String> {
Some(self.key_id.clone())
}

fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result<Vec<u8>> {
async fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result<Vec<u8>> {
let signature: Signature = TEST_KEYPAIR.sign(message.as_bytes());
Ok(signature.to_bytes().to_vec())
}
Expand All @@ -52,8 +53,9 @@ impl Verify for TestSubject {
}
}

#[async_trait]
impl Subject for TestSubject {
fn identifier(&self, _subject_syntax_type: &str) -> Result<String> {
async fn identifier(&self, _subject_syntax_type: &str) -> Result<String> {
Ok(self.did.to_string())
}
}
Expand Down
4 changes: 2 additions & 2 deletions oid4vc-manager/src/managers/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ impl ProviderManager {
self.provider.validate_request(authorization_request).await
}

pub fn generate_response<E: Extension + OpenID4VC>(
pub async fn generate_response<E: Extension + OpenID4VC>(
&self,
authorization_request: &AuthorizationRequest<Object<E>>,
input: <E::ResponseHandle as ResponseHandle>::Input,
) -> Result<AuthorizationResponse<E>> {
self.provider.generate_response(authorization_request, input)
self.provider.generate_response(authorization_request, input).await
}

pub async fn send_response<E: Extension>(
Expand Down
7 changes: 5 additions & 2 deletions oid4vc-manager/src/managers/relying_party.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ impl RelyingPartyManager {
})
}

pub fn encode<E: Extension>(&self, authorization_request: &AuthorizationRequest<Object<E>>) -> Result<String> {
self.relying_party.encode(authorization_request)
pub async fn encode<E: Extension>(
&self,
authorization_request: &AuthorizationRequest<Object<E>>,
) -> Result<String> {
self.relying_party.encode(authorization_request).await
}

pub async fn validate_response<E: Extension>(
Expand Down
8 changes: 5 additions & 3 deletions oid4vc-manager/src/methods/key_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ impl Default for KeySubject {

#[async_trait]
impl Sign for KeySubject {
fn key_id(&self, _subject_syntax_type: &str) -> Option<String> {
async fn key_id(&self, _subject_syntax_type: &str) -> Option<String> {
self.document
.authentication
.as_ref()
.and_then(|authentication_methods| authentication_methods.first().cloned())
}

fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result<Vec<u8>> {
async fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result<Vec<u8>> {
match self.external_signer() {
Some(external_signer) => external_signer.sign(message),
None => Ok(self.keypair.sign(message.as_bytes())),
Expand All @@ -69,8 +69,9 @@ impl Verify for KeySubject {
}
}

#[async_trait]
impl Subject for KeySubject {
fn identifier(&self, _subject_syntax_type: &str) -> Result<String> {
async fn identifier(&self, _subject_syntax_type: &str) -> Result<String> {
Ok(self.document.id.clone())
}
}
Expand Down Expand Up @@ -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.
Expand Down
8 changes: 5 additions & 3 deletions oid4vc-manager/tests/common/memory_storage.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -122,7 +123,7 @@ impl<CFC: CredentialFormatCollection + DeserializeOwned> Storage<CFC> 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),
Expand All @@ -136,8 +137,9 @@ impl<CFC: CredentialFormatCollection + DeserializeOwned> Storage<CFC> for Memory
.ok(),
"did:key",
)
.ok(),
)
.await
.ok()
}))
.unwrap(),
notification_id: None,
},
Expand Down
7 changes: 4 additions & 3 deletions oid4vc-manager/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ impl TestSubject {

#[async_trait]
impl Sign for TestSubject {
fn key_id(&self, _subject_syntax_type: &str) -> Option<String> {
async fn key_id(&self, _subject_syntax_type: &str) -> Option<String> {
Some(self.key_id.clone())
}

fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result<Vec<u8>> {
async fn sign(&self, message: &str, _subject_syntax_type: &str) -> Result<Vec<u8>> {
let signature: Signature = TEST_KEYPAIR.sign(message.as_bytes());
Ok(signature.to_bytes().to_vec())
}
Expand All @@ -58,8 +58,9 @@ impl Verify for TestSubject {
}
}

#[async_trait]
impl Subject for TestSubject {
fn identifier(&self, _subject_syntax_type: &str) -> Result<String> {
async fn identifier(&self, _subject_syntax_type: &str) -> Result<String> {
Ok(self.did.to_string())
}
}
Expand Down
2 changes: 1 addition & 1 deletion oid4vc-manager/tests/oid4vci/authorization_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion oid4vc-manager/tests/oid4vci/pre_authorized_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
8 changes: 5 additions & 3 deletions oid4vc-manager/tests/oid4vp/implicit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Ed25519KeyPair>(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`
Expand Down Expand Up @@ -148,6 +148,7 @@ async fn test_implicit_flow() {
&verifiable_credential,
"did:key",
)
.await
.unwrap();

// Create a verifiable presentation using the JWT.
Expand All @@ -166,6 +167,7 @@ async fn test_implicit_flow() {
presentation_submission,
},
)
.await
.unwrap();

// Validate the authorization_response.
Expand Down
26 changes: 15 additions & 11 deletions oid4vc-manager/tests/siopv2/implicit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
async fn key_id(&self, subject_syntax_type: &str) -> Option<String> {
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<Vec<u8>> {
async fn sign(&self, message: &str, subject_syntax_type: &str) -> anyhow::Result<Vec<u8>> {
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.")),
}
}
Expand All @@ -63,11 +64,12 @@ impl Verify for MultiDidMethodSubject {
}
}

#[async_trait]
impl Subject for MultiDidMethodSubject {
fn identifier(&self, subject_syntax_type: &str) -> anyhow::Result<String> {
async fn identifier(&self, subject_syntax_type: &str) -> anyhow::Result<String> {
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.")),
}
}
Expand Down Expand Up @@ -119,7 +121,7 @@ async fn test_implicit_flow(#[case] did_method: &str) {
key_subject: KeySubject::from_keypair(generate::<Ed25519KeyPair>(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();
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -228,6 +231,7 @@ async fn test_implicit_flow(#[case] did_method: &str) {
// encoded as a JWT.
let authorization_response: AuthorizationResponse<SIOPv2> = 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.
Expand Down
5 changes: 3 additions & 2 deletions oid4vci/src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub struct ProofOfPossession {
}

impl ProofBuilder {
pub fn build(self) -> anyhow::Result<KeyProofType> {
pub async fn build(self) -> anyhow::Result<KeyProofType> {
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");
Expand All @@ -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")),
Expand Down
23 changes: 18 additions & 5 deletions oid4vci/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ impl<CFC: CredentialFormatCollection + DeserializeOwned> Wallet<CFC> {
// 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,
Expand Down Expand Up @@ -149,7 +152,11 @@ impl<CFC: CredentialFormatCollection + DeserializeOwned> Wallet<CFC> {
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)
Expand All @@ -162,7 +169,8 @@ impl<CFC: CredentialFormatCollection + DeserializeOwned> Wallet<CFC> {
.clone(),
)
.subject_syntax_type(self.default_subject_syntax_type.to_string())
.build()?,
.build()
.await?,
),
};

Expand All @@ -187,7 +195,11 @@ impl<CFC: CredentialFormatCollection + DeserializeOwned> Wallet<CFC> {
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)
Expand All @@ -200,7 +212,8 @@ impl<CFC: CredentialFormatCollection + DeserializeOwned> Wallet<CFC> {
.clone(),
)
.subject_syntax_type(self.default_subject_syntax_type.to_string())
.build()?,
.build()
.await?,
);

let batch_credential_request = BatchCredentialRequest {
Expand Down
Loading

0 comments on commit 048f2fb

Please sign in to comment.