Skip to content

Commit

Permalink
Add APID VC implementations for sign & verify, add JSON serialization…
Browse files Browse the repository at this point in the history
… for VC at UniFFI layer (#251)
  • Loading branch information
KendallWeihe authored Jun 26, 2024
1 parent 1155fed commit cfe6f16
Show file tree
Hide file tree
Showing 10 changed files with 619 additions and 72 deletions.
18 changes: 7 additions & 11 deletions bindings/web5_uniffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use web5_uniffi_wrapper::{
credentials::{
presentation_definition::PresentationDefinition,
verifiable_credential_1_1::VerifiableCredential,
verifiable_credential_1_1::{
data::VerifiableCredential as VerifiableCredentialData, VerifiableCredential,
},
},
crypto::{in_memory_key_manager::InMemoryKeyManager, key_manager::KeyManager},
dids::{
Expand All @@ -23,16 +25,10 @@ use web5_uniffi_wrapper::{
};

use web5::apid::{
credentials::{
presentation_definition::{
Constraints as ConstraintsData, Field as FieldData, Filter as FilterData,
InputDescriptor as InputDescriptorData, Optionality,
PresentationDefinition as PresentationDefinitionData,
},
verifiable_credential_1_1::{
CredentialSubject as CredentialSubjectData,
VerifiableCredential as VerifiableCredentialData,
},
credentials::presentation_definition::{
Constraints as ConstraintsData, Field as FieldData, Filter as FilterData,
InputDescriptor as InputDescriptorData, Optionality,
PresentationDefinition as PresentationDefinitionData,
},
crypto::jwk::Jwk as JwkData,
dids::{
Expand Down
19 changes: 9 additions & 10 deletions bindings/web5_uniffi/src/web5.udl
Original file line number Diff line number Diff line change
Expand Up @@ -208,29 +208,28 @@ interface BearerDid {
Signer get_signer(string key_id);
};

dictionary CredentialSubjectData {
string id;
record<string, string>? params;
};

dictionary VerifiableCredentialData {
sequence<string> context;
string id;
sequence<string> type;
string issuer;
string issuance_date;
string? expiration_date;
CredentialSubjectData credential_subject;
string json_serialized_issuer;
timestamp issuance_date;
timestamp? expiration_date;
string json_serialized_credential_subject;
};

interface VerifiableCredential {
[Throws=RustCoreError]
constructor(VerifiableCredentialData data);
[Name=verify, Throws=RustCoreError]
constructor([ByRef] string vcjwt);
[Name=verify_with_verifier, Throws=RustCoreError]
constructor([ByRef] string vcjwt, Verifier verifier);
[Throws=RustCoreError]
string sign(Signer signer);
string sign(BearerDid bearer_did);
[Throws=RustCoreError]
string sign_with_signer([ByRef] string key_id, Signer signer);
[Throws=RustCoreError]
VerifiableCredentialData get_data();
};

Expand Down
1 change: 1 addition & 0 deletions bindings/web5_uniffi_wrapper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ repository.workspace = true
license-file.workspace = true

[dependencies]
serde_json = { workspace = true }
thiserror = { workspace = true }
web5 = { path = "../../crates/web5" }
Original file line number Diff line number Diff line change
@@ -1,35 +1,111 @@
use crate::{
dids::bearer_did::BearerDid,
dsa::{Signer, Verifier},
errors::Result,
errors::{Result, RustCoreError},
};
use std::sync::Arc;
use std::sync::{Arc, RwLock};
use web5::apid::credentials::verifiable_credential_1_1::VerifiableCredential as InnerVerifiableCredential;

pub struct VerifiableCredential(pub InnerVerifiableCredential);
pub struct VerifiableCredential(pub Arc<RwLock<InnerVerifiableCredential>>);

impl VerifiableCredential {
pub fn new(verifiable_credential: InnerVerifiableCredential) -> Self {
Self(verifiable_credential)
pub fn new(verifiable_credential: data::VerifiableCredential) -> Result<Self> {
let inner_verifiable_credential = verifiable_credential.to_inner()?;

Ok(Self(Arc::new(RwLock::new(inner_verifiable_credential))))
}

pub fn verify(vcjwt: &str) -> Result<Self> {
let vc = InnerVerifiableCredential::verify(vcjwt).map_err(|e| Arc::new(e.into()))?;
Ok(Self(vc))
let inner_verifiable_credential =
InnerVerifiableCredential::verify(vcjwt).map_err(|e| Arc::new(e.into()))?;

Ok(Self(Arc::new(RwLock::new(inner_verifiable_credential))))
}

pub fn verify_with_verifier(vcjwt: &str, verifier: Arc<dyn Verifier>) -> Result<Self> {
let vc = InnerVerifiableCredential::verify_with_verifier(vcjwt, verifier.to_inner())
.map_err(|e| Arc::new(e.into()))?;
Ok(Self(vc))
let inner_verifiable_credential =
InnerVerifiableCredential::verify_with_verifier(vcjwt, verifier.to_inner())
.map_err(|e| Arc::new(e.into()))?;

Ok(Self(Arc::new(RwLock::new(inner_verifiable_credential))))
}

pub fn sign(&self, bearer_did: Arc<BearerDid>) -> Result<String> {
let inner_verifiable_credential = self
.0
.read()
.map_err(|e| RustCoreError::from_poison_error(e, "RwLockReadError"))?;

inner_verifiable_credential
.sign(&bearer_did.0)
.map_err(|e| Arc::new(e.into()))
}

pub fn sign(&self, signer: Arc<dyn Signer>) -> Result<String> {
self.0
.sign(signer.to_inner())
pub fn sign_with_signer(&self, key_id: &str, signer: Arc<dyn Signer>) -> Result<String> {
let inner_verifiable_credential = self
.0
.read()
.map_err(|e| RustCoreError::from_poison_error(e, "RwLockReadError"))?;

inner_verifiable_credential
.sign_with_signer(key_id, signer.to_inner())
.map_err(|e| Arc::new(e.into()))
}

pub fn get_data(&self) -> InnerVerifiableCredential {
self.0.clone()
pub fn get_data(&self) -> Result<data::VerifiableCredential> {
let inner_verifiable_credential = self
.0
.read()
.map_err(|e| RustCoreError::from_poison_error(e, "RwLockReadError"))?;

data::VerifiableCredential::from_inner(inner_verifiable_credential.clone())
}
}

pub mod data {
use super::*;
use std::time::SystemTime;

#[derive(Clone)]
pub struct VerifiableCredential {
pub context: Vec<String>,
pub id: String,
pub r#type: Vec<String>,
pub json_serialized_issuer: String, // JSON serialized
pub issuance_date: SystemTime,
pub expiration_date: Option<SystemTime>,
pub json_serialized_credential_subject: String, // JSON serialized
}

impl VerifiableCredential {
pub fn from_inner(inner_verifiable_credential: InnerVerifiableCredential) -> Result<Self> {
Ok(Self {
context: inner_verifiable_credential.context.clone(),
id: inner_verifiable_credential.id.clone(),
r#type: inner_verifiable_credential.r#type.clone(),
json_serialized_issuer: serde_json::to_string(&inner_verifiable_credential.issuer)
.map_err(|e| Arc::new(e.into()))?,
issuance_date: inner_verifiable_credential.issuance_date,
expiration_date: inner_verifiable_credential.expiration_date,
json_serialized_credential_subject: serde_json::to_string(
&inner_verifiable_credential.credential_subject,
)
.map_err(|e| Arc::new(e.into()))?,
})
}

pub fn to_inner(&self) -> Result<InnerVerifiableCredential> {
Ok(InnerVerifiableCredential {
context: self.context.clone(),
id: self.id.clone(),
r#type: self.r#type.clone(),
issuer: serde_json::from_str(&self.json_serialized_issuer)
.map_err(|e| Arc::new(e.into()))?,
issuance_date: self.issuance_date,
expiration_date: self.expiration_date,
credential_subject: serde_json::from_str(&self.json_serialized_credential_subject)
.map_err(|e| Arc::new(e.into()))?,
})
}
}
}
17 changes: 16 additions & 1 deletion bindings/web5_uniffi_wrapper/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::sync::Arc;
use serde_json::Error as SerdeJsonError;
use std::sync::{Arc, PoisonError};
use std::{any::type_name, fmt::Debug};
use thiserror::Error;
use web5::apid::credentials::presentation_definition::PexError;
Expand All @@ -21,6 +22,14 @@ pub enum RustCoreError {
}

impl RustCoreError {
pub fn from_poison_error<T>(error: PoisonError<T>, error_type: &str) -> Arc<Self> {
Arc::new(RustCoreError::Error {
r#type: error_type.to_string(),
variant: "PoisonError".to_string(),
message: error.to_string(),
})
}

fn new<T>(error: T) -> Self
where
T: std::error::Error + 'static,
Expand Down Expand Up @@ -123,4 +132,10 @@ impl From<BearerDidError> for RustCoreError {
}
}

impl From<SerdeJsonError> for RustCoreError {
fn from(error: SerdeJsonError) -> Self {
RustCoreError::new(error)
}
}

pub type Result<T> = std::result::Result<T, Arc<RustCoreError>>;
1 change: 1 addition & 0 deletions crates/web5/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ did-web = "0.2.2"
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
getrandom = { version = "0.2.12", features = ["js"] }
hex = "0.4"
josekit = "0.8.6"
jsonpath-rust = "0.5.1"
jsonschema = { version = "0.18.0", default-features = false }
k256 = { version = "0.13.3", features = ["ecdsa", "jwk"] }
Expand Down
32 changes: 32 additions & 0 deletions crates/web5/src/apid/credentials/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
use std::time::SystemTimeError;

use josekit::JoseError as JosekitError;
use serde_json::Error as SerdeJsonError;

use super::dids::{
bearer_did::BearerDidError, data_model::DataModelError, did::DidError,
resolution::resolution_metadata::ResolutionMetadataError,
};

pub mod presentation_definition;
pub mod verifiable_credential_1_1;

Expand All @@ -15,6 +25,28 @@ pub enum CredentialError {
VcDataModelValidationError(String),
#[error("invalid timestamp: {0}")]
InvalidTimestamp(String),
#[error("serde json error {0}")]
SerdeJsonError(String),
#[error(transparent)]
Jose(#[from] JosekitError),
#[error(transparent)]
BearerDid(#[from] BearerDidError),
#[error("missing kid jose header")]
MissingKid,
#[error(transparent)]
Resolution(#[from] ResolutionMetadataError),
#[error(transparent)]
DidDataModel(#[from] DataModelError),
#[error(transparent)]
Did(#[from] DidError),
#[error(transparent)]
SystemTime(#[from] SystemTimeError),
}

impl From<SerdeJsonError> for CredentialError {
fn from(err: SerdeJsonError) -> Self {
CredentialError::SerdeJsonError(err.to_string())
}
}

type Result<T> = std::result::Result<T, CredentialError>;
Loading

0 comments on commit cfe6f16

Please sign in to comment.