diff --git a/bindings/web5_uniffi/src/web5.udl b/bindings/web5_uniffi/src/web5.udl index b2fb23ca..b81b212d 100644 --- a/bindings/web5_uniffi/src/web5.udl +++ b/bindings/web5_uniffi/src/web5.udl @@ -1,7 +1,6 @@ namespace web5 { JwkData ed25519_generator_generate(); - [Throws=RustCoreError] ResolutionResult did_jwk_resolve([ByRef] string uri); [Async, Throws=RustCoreError] ResolutionResult did_web_resolve([ByRef] string uri); @@ -36,6 +35,7 @@ interface InMemoryKeyManager { Signer get_signer(JwkData public_jwk); [Throws=RustCoreError] JwkData import_private_jwk(JwkData private_key); + KeyManager get_as_key_manager(); }; enum Dsa { @@ -275,6 +275,7 @@ dictionary FilterData { interface PresentationDefinition { constructor(PresentationDefinitionData data); + PresentationDefinitionData get_data(); [Throws=RustCoreError] sequence select_credentials([ByRef] sequence vc_jwts); }; \ No newline at end of file diff --git a/bindings/web5_uniffi_wrapper/src/credentials/presentation_definition.rs b/bindings/web5_uniffi_wrapper/src/credentials/presentation_definition.rs index bb141b65..fdde9252 100644 --- a/bindings/web5_uniffi_wrapper/src/credentials/presentation_definition.rs +++ b/bindings/web5_uniffi_wrapper/src/credentials/presentation_definition.rs @@ -14,4 +14,8 @@ impl PresentationDefinition { .select_credentials(vc_jwts) .map_err(|e| Arc::new(e.into())) } + + pub fn get_data(&self) -> InnerPresentationDefinition { + self.0.clone() + } } diff --git a/bindings/web5_uniffi_wrapper/src/crypto/in_memory_key_manager.rs b/bindings/web5_uniffi_wrapper/src/crypto/in_memory_key_manager.rs index f2daa982..ea19e7c2 100644 --- a/bindings/web5_uniffi_wrapper/src/crypto/in_memory_key_manager.rs +++ b/bindings/web5_uniffi_wrapper/src/crypto/in_memory_key_manager.rs @@ -1,3 +1,4 @@ +use super::key_manager::KeyManager; use crate::{ dsa::{OuterSigner, Signer}, errors::Result, @@ -11,8 +12,7 @@ use web5::apid::crypto::{ }, }; -use super::key_manager::KeyManager; - +#[derive(Clone)] pub struct InMemoryKeyManager(pub InnerInMemoryKeyManager); impl InMemoryKeyManager { @@ -25,6 +25,10 @@ impl InMemoryKeyManager { .import_private_jwk(private_key) .map_err(|e| Arc::new(e.into())) } + + pub fn get_as_key_manager(&self) -> Arc { + Arc::new(self.clone()) + } } impl KeyManager for InMemoryKeyManager { diff --git a/bindings/web5_uniffi_wrapper/src/dids/methods/did_jwk.rs b/bindings/web5_uniffi_wrapper/src/dids/methods/did_jwk.rs index dea75efa..af1aaf59 100644 --- a/bindings/web5_uniffi_wrapper/src/dids/methods/did_jwk.rs +++ b/bindings/web5_uniffi_wrapper/src/dids/methods/did_jwk.rs @@ -4,9 +4,9 @@ use web5::apid::{crypto::jwk::Jwk, dids::methods::did_jwk::DidJwk as InnerDidJwk pub struct DidJwk(pub InnerDidJwk); -pub fn did_jwk_resolve(uri: &str) -> Result> { - let resolution_result = InnerDidJwk::resolve(uri).map_err(|e| Arc::new(e.into()))?; - Ok(Arc::new(ResolutionResult(resolution_result))) +pub fn did_jwk_resolve(uri: &str) -> Arc { + let resolution_result = InnerDidJwk::resolve(uri); + Arc::new(ResolutionResult(resolution_result)) } impl DidJwk { diff --git a/crates/web5/src/apid/credentials/presentation_definition.rs b/crates/web5/src/apid/credentials/presentation_definition.rs index 962561f1..253fd412 100644 --- a/crates/web5/src/apid/credentials/presentation_definition.rs +++ b/crates/web5/src/apid/credentials/presentation_definition.rs @@ -22,7 +22,7 @@ type Result = std::result::Result; /// Represents a DIF Presentation Definition defined [here](https://identity.foundation/presentation-exchange/#presentation-definition). /// Presentation Definitions are objects that articulate what proofs a Verifier requires. -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct PresentationDefinition { pub id: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -34,7 +34,7 @@ pub struct PresentationDefinition { /// Represents a DIF Input Descriptor defined [here](https://identity.foundation/presentation-exchange/#input-descriptor). /// Input Descriptors are used to describe the information a Verifier requires of a Holder. -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct InputDescriptor { pub id: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -45,14 +45,14 @@ pub struct InputDescriptor { } /// Contains the requirements for a given Input Descriptor. -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct Constraints { #[serde(skip_serializing_if = "Vec::is_empty")] pub fields: Vec, } /// Contains the requirements for a given field within a proof. -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct Field { #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, @@ -71,14 +71,14 @@ pub struct Field { } /// Type alias for the possible values of the predicate field. -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum Optionality { Required, Preferred, } /// A JSON Schema that is applied against the value of a field. -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct Filter { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub r#type: Option, diff --git a/crates/web5/src/apid/crypto/jwk.rs b/crates/web5/src/apid/crypto/jwk.rs index 51f3759d..6deb3088 100644 --- a/crates/web5/src/apid/crypto/jwk.rs +++ b/crates/web5/src/apid/crypto/jwk.rs @@ -7,8 +7,10 @@ pub struct Jwk { pub alg: String, pub kty: String, pub crv: String, + #[serde(skip_serializing_if = "Option::is_none")] pub d: Option, pub x: String, + #[serde(skip_serializing_if = "Option::is_none")] pub y: Option, } diff --git a/crates/web5/src/apid/dids/bearer_did.rs b/crates/web5/src/apid/dids/bearer_did.rs index 7600680f..2415fd3e 100644 --- a/crates/web5/src/apid/dids/bearer_did.rs +++ b/crates/web5/src/apid/dids/bearer_did.rs @@ -57,3 +57,19 @@ impl BearerDid { Ok(self.key_manager.get_signer(public_jwk)?) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::apid::crypto::key_managers::in_memory_key_manager::InMemoryKeyManager; + + #[test] + fn can_instantiate_did_jwk() { + let did_uri = "did:jwk:eyJrdHkiOiJPS1AiLCJ1c2UiOiJzaWciLCJjcnYiOiJFZDI1NTE5Iiwia2lkIjoiVnRTSFhQbEtEdzFFRW9PajVYTjNYV2hqU1BZVk52WC1lNHZqUk8weVlKQSIsIngiOiJpejcwc3ZTTHhOWmhzRHhlSlFfam5PVmJYM0tGTmtjQmNNaldqWm1YRXNBIiwiYWxnIjoiRWREU0EifQ"; + let key_manager = InMemoryKeyManager::new(); + + let bearer_did = BearerDid::new(did_uri, Arc::new(key_manager)).unwrap(); + + assert_eq!(did_uri, bearer_did.document.id); + } +} diff --git a/crates/web5/src/apid/dids/methods/did_jwk.rs b/crates/web5/src/apid/dids/methods/did_jwk.rs index 8c198bf2..ad63c877 100644 --- a/crates/web5/src/apid/dids/methods/did_jwk.rs +++ b/crates/web5/src/apid/dids/methods/did_jwk.rs @@ -5,7 +5,8 @@ use crate::apid::{ data_model::{document::Document, verification_method::VerificationMethod}, did::Did, resolution::{ - resolution_metadata::ResolutionMetadataError, resolution_result::ResolutionResult, + resolution_metadata::{ResolutionMetadata, ResolutionMetadataError}, + resolution_result::ResolutionResult, }, }, }; @@ -49,7 +50,7 @@ impl DidJwk { } pub fn from_uri(uri: &str) -> Result { - let resolution_result = DidJwk::resolve(uri)?; + let resolution_result = DidJwk::resolve(uri); match resolution_result.document { None => Err(match resolution_result.resolution_metadata.error { @@ -63,16 +64,43 @@ impl DidJwk { } } - pub fn resolve(uri: &str) -> Result { - let identifier = Did::new(uri)?; - let decoded_jwk = general_purpose::URL_SAFE_NO_PAD.decode(identifier.id)?; - let public_jwk = serde_json::from_slice(&decoded_jwk)?; + pub fn resolve(uri: &str) -> ResolutionResult { + let result: Result = (|| { + let identifier = Did::new(uri)?; + let decoded_jwk = general_purpose::URL_SAFE_NO_PAD.decode(identifier.id)?; + let public_jwk = serde_json::from_slice::(&decoded_jwk)?; - let did_jwk = DidJwk::from_public_jwk(public_jwk)?; + let kid = format!("{}#0", identifier.uri); + let document = Document { + id: identifier.uri.clone(), + verification_method: vec![VerificationMethod { + id: kid.clone(), + r#type: "JsonWebKey".to_string(), + controller: identifier.uri.clone(), + public_key_jwk: public_jwk, + }], + assertion_method: Some(vec![kid.clone()]), + authentication: Some(vec![kid.clone()]), + capability_invocation: Some(vec![kid.clone()]), + capability_delegation: Some(vec![kid.clone()]), + key_agreement: Some(vec![kid.clone()]), + ..Default::default() + }; - Ok(ResolutionResult { - document: Some(did_jwk.document), - ..Default::default() - }) + Ok(ResolutionResult { + document: Some(document), + ..Default::default() + }) + })(); + + match result { + Ok(resolution_result) => resolution_result, + Err(_) => ResolutionResult { + resolution_metadata: ResolutionMetadata { + error: Some(ResolutionMetadataError::InternalError), + }, + ..Default::default() + }, + } } } diff --git a/crates/web5/src/apid/dids/resolution/resolution_result.rs b/crates/web5/src/apid/dids/resolution/resolution_result.rs index f6a344f9..cb93ee6c 100644 --- a/crates/web5/src/apid/dids/resolution/resolution_result.rs +++ b/crates/web5/src/apid/dids/resolution/resolution_result.rs @@ -1,5 +1,8 @@ use super::{document_metadata::DocumentMetadata, resolution_metadata::ResolutionMetadata}; -use crate::apid::dids::data_model::document::Document; +use crate::apid::dids::{ + data_model::document::Document, did::Did, methods::did_jwk::DidJwk, + resolution::resolution_metadata::ResolutionMetadataError, +}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Default, Deserialize, Serialize)] @@ -11,9 +14,40 @@ pub struct ResolutionResult { impl ResolutionResult { pub fn new(uri: &str) -> Self { - println!("ResolutionResult::new() called with {}", uri); - Self { - ..Default::default() + let did = match Did::new(uri) { + Ok(did) => did, + Err(_) => { + return ResolutionResult { + resolution_metadata: ResolutionMetadata { + error: Some(ResolutionMetadataError::InvalidDid), + }, + ..Default::default() + } + } + }; + + match did.method.as_str() { + "jwk" => DidJwk::resolve(uri), + _ => ResolutionResult { + resolution_metadata: ResolutionMetadata { + error: Some(ResolutionMetadataError::MethodNotSupported), + }, + ..Default::default() + }, } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_resolve_did_jwk() { + let did_uri = "did:jwk:eyJrdHkiOiJPS1AiLCJ1c2UiOiJzaWciLCJjcnYiOiJFZDI1NTE5Iiwia2lkIjoiVnRTSFhQbEtEdzFFRW9PajVYTjNYV2hqU1BZVk52WC1lNHZqUk8weVlKQSIsIngiOiJpejcwc3ZTTHhOWmhzRHhlSlFfam5PVmJYM0tGTmtjQmNNaldqWm1YRXNBIiwiYWxnIjoiRWREU0EifQ"; + let resolution_result = ResolutionResult::new(did_uri); + + assert_eq!(None, resolution_result.resolution_metadata.error); + assert_eq!(resolution_result.document.unwrap().id, did_uri.to_string()); + } +}