From 024dfeaaef240c1281a5fa835f85590a02260931 Mon Sep 17 00:00:00 2001 From: Curtish Date: Fri, 8 Dec 2023 14:57:26 +0000 Subject: [PATCH] feat(wasm): adding wasm compatibility --- .gitignore | 1 + Cargo.toml | 18 +++++- src/lib.rs | 3 + src/services/mod.rs | 1 + src/wasm/error.rs | 16 +++++ src/wasm/helpers.rs | 145 +++++++++++++++++++++++++++++++++++++++++++ src/wasm/issuer.rs | 71 +++++++++++++++++++++ src/wasm/mod.rs | 8 +++ src/wasm/prover.rs | 134 +++++++++++++++++++++++++++++++++++++++ src/wasm/verifier.rs | 40 ++++++++++++ 10 files changed, 434 insertions(+), 3 deletions(-) create mode 100644 src/wasm/error.rs create mode 100644 src/wasm/helpers.rs create mode 100644 src/wasm/issuer.rs create mode 100644 src/wasm/mod.rs create mode 100644 src/wasm/prover.rs create mode 100644 src/wasm/verifier.rs diff --git a/.gitignore b/.gitignore index 2246e24e..bce69bb4 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ local.properties anoncred-kmm/anoncred-wrapper-rust/src/* **/.DS_Store anoncred-kmm/anoncred-kmm/anoncred-wrapper-rust/src/* +pkg/ diff --git a/Cargo.toml b/Cargo.toml index f7e17f61..2ad7418e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,11 @@ crate-type = ["staticlib", "rlib", "cdylib"] [features] default = ["ffi", "logger", "zeroize"] -ffi = ["ffi-support"] +ffi = ["ffi-support", "non_wasm"] logger = ["env_logger"] vendored = ["openssl", "openssl/vendored"] +non_wasm = ["ursa/cl_native", "ursa/serde"] +wasm = ["ursa/portable_wasm", "zeroize", "wasm-bindgen", "wasm-logger", "console_error_panic_hook", "serde-wasm-bindgen"] [dependencies] bs58 = "0.4.0" @@ -36,14 +38,24 @@ serde_json = { version = "1.0.94", features = ["raw_value"]} sha2 = "0.10.6" tempfile = "3.4.0" thiserror = "1.0.39" -ursa = { version = "0.3.7", default-features = false, features = ["cl_native", "serde"] } -zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] } +zeroize = { version = "=1.4", optional = true, features = ["zeroize_derive"] } +wasm-bindgen = { version = "0.2.84", optional = true } +wasm-logger = { version = "0.2.0", optional = true } +console_error_panic_hook = { version="0.1.7", optional = true } +serde-wasm-bindgen = { version = "0.4", optional = true} # We add the openssl dependency here because ursa does not expose a vendored openssl feature # Since we use "cl_native" as a feature, which uses openssl, we can add a vendored build with # the new exposed "vendored" feature openssl = { version = "0.10.45", optional = true } +[dependencies.ursa] +git = "https://github.com/hyperledger/ursa.git" +rev = "390f004e3e91f1257da4bbcd6bc2d72f67b62c0f" +version = "0.3.7" +default-features = false + + [profile.release] lto = true codegen-units = 1 diff --git a/src/lib.rs b/src/lib.rs index e24b1e56..a75faa51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,4 +28,7 @@ mod utils; #[cfg(feature = "ffi")] mod ffi; +#[cfg(feature = "wasm")] +mod wasm; + pub mod data_types; diff --git a/src/services/mod.rs b/src/services/mod.rs index 8261db6e..72b678da 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -8,4 +8,5 @@ pub mod verifier; pub mod utils { pub use super::helpers::encode_credential_attribute; + pub use super::helpers::new_nonce; } diff --git a/src/wasm/error.rs b/src/wasm/error.rs new file mode 100644 index 00000000..edcc433a --- /dev/null +++ b/src/wasm/error.rs @@ -0,0 +1,16 @@ +use wasm_bindgen::prelude::*; + +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize)] +#[repr(usize)] +#[wasm_bindgen] +pub enum ErrorCode { + Success = 0, + Input = 1, + IOError = 2, + InvalidState = 3, + Unexpected = 4, + CredentialRevoked = 5, + InvalidUserRevocId = 6, + ProofRejected = 7, + RevocationRegistryFull = 8, +} diff --git a/src/wasm/helpers.rs b/src/wasm/helpers.rs new file mode 100644 index 00000000..0e1e9f4d --- /dev/null +++ b/src/wasm/helpers.rs @@ -0,0 +1,145 @@ +// --- HELPERS --- +use wasm_bindgen::prelude::*; + +use std::str::FromStr; + +use crate::data_types::credential::Credential; +use crate::data_types::cred_def::SignatureType; +use crate::data_types::cred_def::CredentialDefinition; +use crate::data_types::cred_offer::CredentialOffer; +use crate::data_types::cred_request::CredentialRequestMetadata; +use crate::data_types::link_secret::LinkSecret; +use crate::data_types::pres_request::PresentationRequest; +use crate::data_types::schema::Schema; + +use crate::services::issuer; + +use crate::types::CredentialDefinitionConfig; + +use crate::utils::validation::Validatable; + +use super::error::ErrorCode; + +#[wasm_bindgen(js_name = anoncredsSetDefaultLogger)] +pub fn anoncreds_set_default_logger() -> ErrorCode { + console_error_panic_hook::set_once(); + wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); + debug!("Initialized default logger"); + + ErrorCode::Success +} + +#[wasm_bindgen(js_name = anoncredsCreateSchema)] +pub fn anoncreds_create_schema( + name: &str, + version: &str, + issuer_id: &str, + attribute_names: Vec, +) -> JsValue { + let mut attribute_names_vec: Vec = vec![]; + + for name in &attribute_names { + let name = name.as_string(); + if let Some(name) = name { + attribute_names_vec.push(name.to_owned()); + } + } + + let schema = + issuer::create_schema(name, version, issuer_id, attribute_names_vec.into()).unwrap(); + + serde_wasm_bindgen::to_value(&schema).unwrap() +} + +#[wasm_bindgen(js_name = anoncredsCreateCredentialDefinition)] +pub fn anoncreds_create_credential_definition( + schema_id: &str, + schema: JsValue, + tag: &str, + issuer_id: &str, + signature_type: &str, + support_revocation: bool, +) -> Vec { + let schema: Schema = serde_wasm_bindgen::from_value(schema).unwrap(); + let signature_type = SignatureType::from_str(signature_type) + .map_err(err_map!(Input)) + .unwrap(); + let (cred_def, cred_def_pvt, key_proof) = issuer::create_credential_definition( + schema_id, + &schema, + issuer_id, + tag, + signature_type, + CredentialDefinitionConfig { support_revocation }, + ) + .unwrap(); + + let cred_def = serde_wasm_bindgen::to_value(&cred_def).unwrap(); + let cred_def_pvt = serde_wasm_bindgen::to_value(&cred_def_pvt).unwrap(); + let key_proof = serde_wasm_bindgen::to_value(&key_proof).unwrap(); + + vec![cred_def, cred_def_pvt, key_proof] +} + +#[wasm_bindgen(js_name = anoncredsCreateCredentialDefinitionCustom)] +pub fn anoncreds_create_credential_definition_custom( + schema_id: &str, + schema: JsValue, + tag: &str, + issuer_id: &str, +) -> Vec { + let schema: Schema = serde_wasm_bindgen::from_value(schema).unwrap(); + + let (cred_def, cred_def_pvt, key_proof) = issuer::create_credential_definition( + schema_id, + &schema, + issuer_id, + tag, + SignatureType::CL, + CredentialDefinitionConfig { support_revocation: false }, + ) + .unwrap(); + + let cred_def = serde_wasm_bindgen::to_value(&cred_def).unwrap(); + let cred_def_pvt = serde_wasm_bindgen::to_value(&cred_def_pvt).unwrap(); + let key_proof = serde_wasm_bindgen::to_value(&key_proof).unwrap(); + + vec![cred_def, cred_def_pvt, key_proof] +} + + +#[wasm_bindgen(js_name = anoncredsValidateCredentialDefinitionFromJson)] +pub fn anoncreds_validate_credential_definition_from_json( + json: JsValue +) -> Result { + let cred_def: CredentialDefinition = serde_wasm_bindgen::from_value(json).map_err(|e| >::into(e))?; + cred_def.validate().map(|_| true).map_err(|e| JsValue::from_str(&e.to_string())) +} + + +// --- SERDE --- + +pub fn deserialise_credential(credential: JsValue) -> Credential { + serde_wasm_bindgen::from_value(credential).expect("Unable to deserialise Credential") +} + +pub fn deserialise_credential_offer(cred_offer: JsValue) -> CredentialOffer { + serde_wasm_bindgen::from_value(cred_offer).expect("Unable to deserialise Credential Offer") +} + +pub fn deserialise_credential_definition(cred_def: JsValue) -> CredentialDefinition { + serde_wasm_bindgen::from_value(cred_def).expect("Unable to deserialise Credential Definition") +} + +pub fn deserialise_credential_request_metadata(cred_req_meta: JsValue) -> CredentialRequestMetadata { + serde_wasm_bindgen::from_value(cred_req_meta).expect("Unable to deserialise Credential Request Metadata") +} + +pub fn deserialise_link_secret(link_secret: &str) -> LinkSecret { + LinkSecret::try_from(link_secret).expect("Unable to deserialise Link Secret") +} + +pub fn deserialise_presentation_request(pres_req: JsValue) -> PresentationRequest { + let json = serde_wasm_bindgen::from_value(pres_req).expect("Unable to deserialise Presentation Request"); + serde_json::from_value(json).expect("Unable to parse Presentation Request") +} diff --git a/src/wasm/issuer.rs b/src/wasm/issuer.rs new file mode 100644 index 00000000..dabec0a6 --- /dev/null +++ b/src/wasm/issuer.rs @@ -0,0 +1,71 @@ +// --- ISSUER --- +use wasm_bindgen::prelude::*; + +use crate::data_types::cred_def::CredentialDefinition; +use crate::data_types::cred_def::CredentialDefinitionPrivate; +use crate::data_types::cred_def::SignatureType; +use crate::data_types::cred_offer::CredentialOffer; +use crate::data_types::cred_request::CredentialRequest; +use crate::data_types::schema::Schema; + +use crate::services::issuer; + +use crate::types::CredentialDefinitionConfig; +use crate::types::MakeCredentialValues; + +#[wasm_bindgen(js_name = issuerCreateCredentialOffer)] +pub fn issuer_create_credential_offer(js_schema: JsValue) -> Vec { + let schema: Schema = serde_wasm_bindgen::from_value(js_schema).unwrap(); + + let (cred_def, cred_def_priv, key_correctness_proof) = + issuer::create_credential_definition("did:web:xyz/resource/schema", + &schema, + "did:web:xyz", + "default-tag", + SignatureType::CL, + CredentialDefinitionConfig::default() + ).expect("Unable to create Credential Definition"); + + let credential_offer = + issuer::create_credential_offer("did:web:xyz/resource/schema", + "did:web:xyz/resource/cred-def", + &key_correctness_proof, + ).expect("Unable to create Credential Offer"); + + let js_cred_offer = serde_wasm_bindgen::to_value(&credential_offer).unwrap(); + let js_cred_def = serde_wasm_bindgen::to_value(&cred_def).unwrap(); + let js_cred_def_priv = serde_wasm_bindgen::to_value(&cred_def_priv).unwrap(); + + vec![js_cred_offer, js_cred_def, js_cred_def_priv] +} + +#[wasm_bindgen(js_name = issuerCreateCredential)] +pub fn issuer_create_credential( + js_cred_offer: JsValue, + js_cred_def: JsValue, + js_cred_def_priv: JsValue, + js_cred_request: JsValue +) -> JsValue { + let cred_def: CredentialDefinition = serde_wasm_bindgen::from_value(js_cred_def).unwrap(); + let cred_def_priv: CredentialDefinitionPrivate = serde_wasm_bindgen::from_value(js_cred_def_priv).unwrap(); + let credential_offer: CredentialOffer = serde_wasm_bindgen::from_value(js_cred_offer).unwrap(); + let credential_request: CredentialRequest = serde_wasm_bindgen::from_value(js_cred_request).unwrap(); + + let mut credential_values = MakeCredentialValues::default(); + credential_values.add_raw("name", "john").expect("Unable to add credential value"); + credential_values.add_raw("age", "28").expect("Unable to add credential value"); + + let credential = issuer::create_credential( + &cred_def, + &cred_def_priv, + &credential_offer, + &credential_request, + credential_values.into(), + None, + None, + None + ) + .expect("Unable to create credential"); + + serde_wasm_bindgen::to_value(&credential).unwrap() +} diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs new file mode 100644 index 00000000..f8214363 --- /dev/null +++ b/src/wasm/mod.rs @@ -0,0 +1,8 @@ +mod error; +mod helpers; + +pub mod prover; + +// not currently required for SDKs +// pub mod issuer; +// pub mod verifier; diff --git a/src/wasm/prover.rs b/src/wasm/prover.rs new file mode 100644 index 00000000..2e60f2a5 --- /dev/null +++ b/src/wasm/prover.rs @@ -0,0 +1,134 @@ +// --- PROVER --- +extern crate console_error_panic_hook; + +use std::collections::HashMap; + +use wasm_bindgen::prelude::*; + +use crate::data_types::cred_def::CredentialDefinition; +use crate::data_types::cred_def::CredentialDefinitionId; +use crate::data_types::schema::Schema; +use crate::data_types::schema::SchemaId; + +use crate::services::prover; +use crate::services::utils::new_nonce; + +use crate::types::PresentCredentials; + +use super::helpers; + + +#[wasm_bindgen(js_name = proverCreateLinkSecret)] +pub fn prover_create_link_secret() -> String { + let secret = prover::create_link_secret().expect("Unable to create link secret"); + let secret_str = secret.try_into().expect("Unable to convert link secret"); + + return secret_str; +} + +#[wasm_bindgen(js_name = proverCreateCredentialRequest)] +pub fn prover_create_credential_request( + cred_offer: JsValue, + cred_def: JsValue, + link_secret: &str, + link_secret_id: &str +) -> Vec { + let credential_offer = helpers::deserialise_credential_offer(cred_offer); + let cred_def = helpers::deserialise_credential_definition(cred_def); + let link_secret = helpers::deserialise_link_secret(link_secret); + let entropy = new_nonce().unwrap().to_string(); + + let (credential_request, credential_request_metadata) = + prover::create_credential_request( + Some(&entropy), + None, + &cred_def, + &link_secret, + &link_secret_id, + &credential_offer, + ) + .expect("Unable to create Credential Request"); + + let js_cred_req = serde_wasm_bindgen::to_value(&credential_request).expect("Unable to serialise Credential Request"); + let js_cred_meta = serde_wasm_bindgen::to_value(&credential_request_metadata).expect("Unable to serialise Credential Request Metadata"); + let js_entropy = serde_wasm_bindgen::to_value(&entropy).expect("Unable to serialise Entropy"); + + vec![js_cred_req, js_cred_meta, js_entropy] +} + +#[wasm_bindgen(js_name = proverProcessCredential)] +pub fn prover_process_credential( + cred_def: JsValue, + credential: JsValue, + cred_req_meta: JsValue, + link_secret: &str, +) -> JsValue { + let mut credential = helpers::deserialise_credential(credential); + let cred_def = helpers::deserialise_credential_definition(cred_def); + let cred_req_meta = helpers::deserialise_credential_request_metadata(cred_req_meta); + let link_secret = helpers::deserialise_link_secret(link_secret); + + prover::process_credential( + &mut credential, + &cred_req_meta, + &link_secret, + &cred_def, + None + ) + .expect("Unable to process the Credential"); + + serde_wasm_bindgen::to_value(&credential).expect("Unable to serialise Credential") +} + +#[wasm_bindgen(js_name = proverCreatePresentation)] +pub fn prover_create_presentation( + presentation_request: JsValue, + schema_dict: JsValue, + cred_def_dict: JsValue, + credential: JsValue, + link_secret: &str, +) -> JsValue { + let pres_request = helpers::deserialise_presentation_request(presentation_request); + let credential = helpers::deserialise_credential(credential); + let link_secret = helpers::deserialise_link_secret(link_secret); + + let mut schemas = HashMap::new(); + let schema_list: HashMap = serde_wasm_bindgen::from_value(schema_dict) + .expect("Unable to deserialise Schemas"); + + for (key, value) in schema_list.iter() { + schemas.insert(key, value); + } + + let mut cred_defs = HashMap::new(); + let cred_def_list: HashMap = serde_wasm_bindgen::from_value(cred_def_dict) + .expect("Unable to deserialise Credential Definitions"); + + for (key, value) in cred_def_list.iter() { + cred_defs.insert(key, value); + } + + let mut present = PresentCredentials::default(); + let mut cred1 = present.add_credential(&credential, None, None); + let pres_req_val = pres_request.value(); + + for key in pres_req_val.requested_attributes.keys() { + cred1.add_requested_attribute(key, true); + } + + for key in pres_req_val.requested_predicates.keys() { + cred1.add_requested_predicate(key); + } + + let presentation = prover::create_presentation( + &pres_request, + present, + None, + &link_secret, + &schemas, + &cred_defs + ) + .expect("Unable to create Presentation"); + + serde_wasm_bindgen::to_value(&presentation).expect("Unable to serialise Presentation") +} diff --git a/src/wasm/verifier.rs b/src/wasm/verifier.rs new file mode 100644 index 00000000..e3232402 --- /dev/null +++ b/src/wasm/verifier.rs @@ -0,0 +1,40 @@ +// --- VERIFIER --- +use wasm_bindgen::prelude::*; + +use std::collections::HashMap; + +use crate::data_types::cred_def::CredentialDefinition; +use crate::data_types::cred_def::CredentialDefinitionId; +use crate::data_types::presentation::Presentation; +use crate::data_types::pres_request::PresentationRequest; +use crate::data_types::schema::Schema; +use crate::data_types::schema::SchemaId; + +use crate::services::verifier; + +use super::helpers; + +#[wasm_bindgen(js_name = verifierVerifyPresentation)] +pub fn verifier_verify_presentation( + js_presentation: JsValue, + js_presentation_request: JsValue, + js_schema: JsValue, + js_cred_def: JsValue +) -> bool { + let presentation: Presentation = serde_wasm_bindgen::from_value(js_presentation).unwrap(); + let pres_request: PresentationRequest = helpers::deserialise_presentation_request(js_presentation_request); + + let schema: Schema = serde_wasm_bindgen::from_value(js_schema).unwrap(); + let mut schemas = HashMap::new(); + let schema_id = SchemaId::new_unchecked("did:web:xyz/resource/schema"); + schemas.insert(&schema_id, &schema); + + let cred_def: CredentialDefinition = serde_wasm_bindgen::from_value(js_cred_def).unwrap(); + let mut cred_defs = HashMap::new(); + let cred_def_id = CredentialDefinitionId::new_unchecked("did:web:xyz/resource/cred-def"); + cred_defs.insert(&cred_def_id, &cred_def); + + let verified = verifier::verify_presentation(&presentation, &pres_request, &schemas, &cred_defs, None, None, None).expect("Error"); + + return verified; +}