diff --git a/VERSIONS.md b/VERSIONS.md index 737e512..be7f3de 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -11,6 +11,7 @@ - these properties in payload are now optional: - `issuer_public_key_did` - `issuer_proving_key` +- add `helper_convert_credential_to_nquads` helper function ### Fixes diff --git a/src/api/vade_evan_api.rs b/src/api/vade_evan_api.rs index fcad730..8ec6290 100644 --- a/src/api/vade_evan_api.rs +++ b/src/api/vade_evan_api.rs @@ -496,6 +496,84 @@ impl VadeEvan { .map_err(|err| err.into()) } + /// Converts a Credential to canonized nquads + /// + /// # Arguments + /// + /// * `credential` - credential to be converted to nquads + /// + /// # Example + /// + /// ``` + /// cfg_if::cfg_if! { + /// if #[cfg(not(all(feature = "c-lib", feature = "target-c-sdk")))] { + /// use anyhow::Result; + /// use vade_evan::{VadeEvan, VadeEvanConfig, DEFAULT_TARGET, DEFAULT_SIGNER}; + /// + /// async fn example() -> Result<()> { + /// let mut vade_evan = VadeEvan::new(VadeEvanConfig { target: DEFAULT_TARGET, signer: DEFAULT_SIGNER })?; + /// let credential = r###"{ + /// "id": "uuid:70b7ec4e-f035-493e-93d3-2cf5be4c7f88", + /// "type": [ + /// "VerifiableCredential" + /// ], + /// "proof": { + /// "type": "BbsBlsSignature2020", + /// "created": "2023-02-01T14:08:17.000Z", + /// "signature": "kvSyi40dnZ5S3/mSxbSUQGKLpyMXDQNLCPtwDGM9GsnNNKF7MtaFHXIbvXaVXku0EY/n2uNMQ2bmK2P0KEmzgbjRHtzUOWVdfAnXnVRy8/UHHIyJR471X6benfZk8KG0qVqy+w67z9g628xRkFGA5Q==", + /// "proofPurpose": "assertionMethod", + /// "verificationMethod": "did:evan:EiAee4ixDnSP0eWyp0YFV7Wt9yrZ3w841FNuv9NSLFSCVA#bbs-key-1", + /// "credentialMessageCount": 13, + /// "requiredRevealStatements": [] + /// }, + /// "issuer": "did:evan:EiAee4ixDnSP0eWyp0YFV7Wt9yrZ3w841FNuv9NSLFSCVA", + /// "@context": [ + /// "https://www.w3.org/2018/credentials/v1", + /// "https://schema.org/", + /// "https://w3id.org/vc-revocation-list-2020/v1" + /// ], + /// "issuanceDate": "2023-02-01T14:08:09.849Z", + /// "credentialSchema": { + /// "id": "did:evan:EiCimsy3uWJ7PivWK0QUYSCkImQnjrx6fGr6nK8XIg26Kg", + /// "type": "EvanVCSchema" + /// }, + /// "credentialStatus": { + /// "id": "did:evan:EiA0Ns-jiPwu2Pl4GQZpkTKBjvFeRXxwGgXRTfG1Lyi8aA#4", + /// "type": "RevocationList2020Status", + /// "revocationListIndex": "4", + /// "revocationListCredential": "did:evan:EiA0Ns-jiPwu2Pl4GQZpkTKBjvFeRXxwGgXRTfG1Lyi8aA" + /// }, + /// "credentialSubject": { + /// "id": "did:evan:EiAee4ixDnSP0eWyp0YFV7Wt9yrZ3w841FNuv9NSLFSCVA", + /// "data": { + /// "bio": "biography" + /// } + /// } + /// }"###; + /// + /// // convert the credential to nquads + /// vade_evan + /// .helper_convert_credential_to_nquads(credential) + /// .await?; + /// + /// Ok(()) + /// } + /// } else { + /// // currently no example for target-c-sdk and c-lib/target-java-lib + /// } + /// } + #[cfg(all(feature = "vc-zkp-bbs"))] + pub async fn helper_convert_credential_to_nquads( + &mut self, + credential: &str, + ) -> Result { + let credential_helper = Credential::new(self)?; + credential_helper + .convert_credential_to_nquads(credential) + .await + .map_err(|err| err.into()) + } + /// Proposes to share a proof for a credential. /// The proof proposal consists of the fields the prover wants to reveal per schema. /// diff --git a/src/c_lib.rs b/src/c_lib.rs index fb620a1..b6ecded 100644 --- a/src/c_lib.rs +++ b/src/c_lib.rs @@ -792,6 +792,26 @@ pub extern "C" fn execute_vade( .map_err(stringify_vade_evan_error) } }), + #[cfg(all(feature = "vc-zkp-bbs"))] + "helper_convert_credential_to_nquads" => runtime.block_on({ + async { + let mut vade_evan = get_vade_evan( + Some(&str_config), + #[cfg(all(feature = "c-lib", feature = "target-c-sdk"))] + ptr_request_list, + #[cfg(all(feature = "c-lib", feature = "target-c-sdk"))] + request_function_callback, + ) + .map_err(stringify_generic_error)?; + vade_evan + .helper_convert_credential_to_nquads( + arguments_vec.get(0).unwrap_or_else(|| &no_args), + ) + .await + .map_err(stringify_vade_evan_error)?; + Ok("".to_string()) + } + }), #[cfg(any(feature = "vc-zkp-bbs"))] "run_custom_function" => runtime.block_on({ execute_vade_function!( diff --git a/src/helpers/credential.rs b/src/helpers/credential.rs index 317e3e7..9481c93 100644 --- a/src/helpers/credential.rs +++ b/src/helpers/credential.rs @@ -487,13 +487,35 @@ impl<'a> Credential<'a> { )), } } + + /// Converts a Credential to canonized nquads + /// + /// # Arguments + /// * `credential_str` - Credential to be converted + /// + /// # Returns + /// * `nquads` - Vec of canonized nquads + pub async fn convert_credential_to_nquads( + &self, + credential_str: &str, + ) -> Result { + // get nquads + let mut parsed_credential: Map = serde_json::from_str(credential_str)?; + // remove proof if exists + if parsed_credential.contains_key("proof") { + parsed_credential.remove("proof"); + } + let credential_without_proof = serde_json::to_string(&parsed_credential)?; + let did_doc_nquads = convert_to_nquads(&credential_without_proof).await?; + Ok(serde_json::to_string(&did_doc_nquads)?) + } } #[cfg(test)] #[cfg(not(all(feature = "c-lib", feature = "target-c-sdk")))] mod tests { use crate::helpers::credential::is_revoked; - + const ADDITIONAL_HIDDEN_MESSAGES_COUNT: usize = 1; cfg_if::cfg_if! { if #[cfg(feature = "did-sidetree")] { use anyhow::Result; @@ -978,4 +1000,27 @@ mod tests { Ok(()) } + + #[tokio::test] + #[cfg(feature = "did-sidetree")] + async fn helper_can_convert_to_nquads() -> Result<()> { + let mut vade_evan = VadeEvan::new(crate::VadeEvanConfig { + target: DEFAULT_TARGET, + signer: DEFAULT_SIGNER, + })?; + + let credential_helper = Credential::new(&mut vade_evan)?; + let credential: BbsCredential = serde_json::from_str(CREDENTIAL_ACTIVE)?; + // verify the credential issuer + let nquads_result = credential_helper + .convert_credential_to_nquads(CREDENTIAL_ACTIVE) + .await; + assert!(nquads_result.is_ok()); + let nquads: Vec = serde_json::from_str(&nquads_result?)?; + assert_eq!( + nquads.len() + ADDITIONAL_HIDDEN_MESSAGES_COUNT, + credential.proof.credential_message_count + ); + Ok(()) + } } diff --git a/src/main.rs b/src/main.rs index b0665ec..14c50ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -253,6 +253,17 @@ async fn main() -> Result<()> { ) .await? } + #[cfg(all(feature = "vc-zkp-bbs"))] + ("convert_credential_to_nquads", Some(sub_m)) => { + get_vade_evan(sub_m)? + .helper_convert_credential_to_nquads(get_argument_value( + sub_m, + "credential", + None, + )) + .await?; + "".to_string() + } #[cfg(all(feature = "vc-zkp-bbs", feature = "did-sidetree"))] ("create_self_issued_credential", Some(sub_m)) => { get_vade_evan(sub_m)? @@ -422,6 +433,16 @@ fn add_subcommand_helper<'a>(app: App<'a, 'a>) -> Result> { } else {} } + cfg_if::cfg_if! { + if #[cfg(all(feature = "vc-zkp-bbs"))] { + subcommand = subcommand.subcommand( + SubCommand::with_name("convert_credential_to_nquads") + .about("Converts a given credential to nquads vector.") + .arg(get_clap_argument("credential")?) + ); + } else {} + } + cfg_if::cfg_if! { if #[cfg(all(feature = "vc-zkp-bbs", feature = "did-sidetree"))] { subcommand = subcommand.subcommand( diff --git a/src/wasm_lib.rs b/src/wasm_lib.rs index 68fba64..a928757 100644 --- a/src/wasm_lib.rs +++ b/src/wasm_lib.rs @@ -190,6 +190,12 @@ struct HelperVerifyPresentationPayload { pub presentation_str: String, pub proof_request_str: String, } +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct HelperConvertCredentialToNquads { + pub credential_str: String, +} + #[wasm_bindgen] pub fn set_panic_hook() { @@ -441,6 +447,20 @@ cfg_if::cfg_if! { .map_err(jsify_vade_evan_error)?) } + #[cfg(all(feature = "vc-zkp-bbs"))] + #[wasm_bindgen] + pub async fn helper_convert_credential_to_nquads( + credential: String, + ) -> Result { + let mut vade_evan = get_vade_evan(None).map_err(jsify_generic_error)?; + vade_evan + .helper_convert_credential_to_nquads( + &credential, + ).await + .map_err(jsify_vade_evan_error)?; + Ok("".to_string()) + } + #[cfg(all(feature = "vc-zkp-bbs", feature = "did-sidetree"))] #[wasm_bindgen] pub async fn helper_create_proof_proposal( @@ -790,6 +810,16 @@ pub async fn execute_vade( Err(error) => Err(get_parsing_error_message(&error, &payload)), } } + #[cfg(all(feature = "vc-zkp-bbs"))] + "helper_convert_credential_to_nquads" => { + let payload_result = parse::(&payload); + match payload_result { + Ok(payload) => { + helper_convert_credential_to_nquads(payload.credential_str).await + } + Err(error) => Err(get_parsing_error_message(&error, &payload)), + } + } #[cfg(all(feature = "vc-zkp-bbs", feature = "did-sidetree"))] "helper_create_proof_proposal" => { let payload_result = parse::(&payload);