Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add APID VC implementations for sign & verify, add JSON serialization for VC at UniFFI layer #251

Merged
merged 9 commits into from
Jun 26, 2024
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
Loading