vade_evan_bbs/
vade_evan_bbs.rs

1/*
2  Copyright (c) 2018-present evan GmbH.
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15*/
16
17use crate::{
18    application::{
19        datatypes::{
20            BbsCredential,
21            BbsCredentialOffer,
22            BbsCredentialRequest,
23            BbsProofRequest,
24            BbsProofVerification,
25            CredentialProposal,
26            CredentialSchema,
27            CredentialSubject,
28            ProofPresentation,
29            RevocationListCredential,
30            SchemaProperty,
31            UnfinishedBbsCredential,
32            UnsignedBbsCredential,
33        },
34        issuer::Issuer,
35        prover::Prover,
36        utils::{decode_base64, generate_uuid, get_dpk_from_string},
37        verifier::Verifier,
38    },
39    crypto::crypto_verifier::CryptoVerifier,
40};
41use async_trait::async_trait;
42use bbs::{
43    keys::{DeterministicPublicKey, SecretKey},
44    SignatureBlinding,
45    SignatureMessage,
46};
47use serde::{Deserialize, Serialize};
48use std::{collections::HashMap, error::Error};
49use vade::{VadePlugin, VadePluginResultValue};
50use vade_signer::Signer;
51
52const EVAN_METHOD: &str = "did:evan";
53const PROOF_METHOD_BBS: &str = "bbs";
54
55/// Message passed to vade containing the desired credential type.
56/// Does not perform action if type does not indicate credential type BBS+.
57/// This can be done by passing "bbs" as the value for "type".
58#[derive(Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct TypeOptions {
61    pub r#type: Option<String>,
62}
63
64/// Contains information necessary to make on-chain transactions (e.g. updating a DID Document).
65#[derive(Serialize, Deserialize)]
66#[serde(rename_all = "camelCase")]
67pub struct AuthenticationOptions {
68    /// Reference to the private key, will be forwarded to external signer if available
69    pub private_key: String,
70    /// DID of the identity
71    pub identity: String,
72}
73
74/// API payload needed to create a revocation list
75#[derive(Serialize, Deserialize)]
76#[serde(rename_all = "camelCase")]
77pub struct CreateRevocationListPayload {
78    /// DID of the issuer
79    pub issuer_did: String,
80    /// DID of the issuer's public key used to verify the credential's signature
81    pub issuer_public_key_did: String,
82    /// Private key of the issuer used to sign the credential
83    pub issuer_proving_key: String,
84    /// future did id for revocation list
85    pub credential_did: String,
86}
87
88// ####### Keep until nquads are implemented in Rust #######
89// #[derive(Serialize, Deserialize)]
90// #[serde(rename_all = "camelCase")]
91// pub struct IssueCredentialPayload {
92//     pub issuer: String,
93//     pub issuer_public_key_id: String,
94//     pub issuer_public_key: String,
95//     pub issuer_secret_key: String,
96//     pub subject: String,
97//     pub schema: String,
98//     pub credential_request: BbsCredentialRequest,
99//     pub credential_offer: BbsCredentialOffer,
100//     pub required_indices: Vec<u32>,
101//     pub nquads: Vec<String>,
102//     pub revocation_list_did: String,
103//     pub revocation_list_id: String,
104// }
105
106/// API payload for issuing a new credential
107/// Currently needs both an unsigned verifiable credential containing all the data
108/// and the nquad representation of this verifiable credential.
109#[derive(Serialize, Deserialize)]
110#[serde(rename_all = "camelCase")]
111pub struct IssueCredentialPayload {
112    /// The VC to sign, without any appended proof
113    pub unsigned_vc: UnsignedBbsCredential,
114    /// Nquads representation of the VC without any appended proof
115    pub nquads: Vec<String>,
116    /// DID url of the public key of the issuer used to later verify the signature
117    pub issuer_public_key_id: String,
118    /// The public bbs+ key of the issuer used to later verify the signature
119    pub issuer_public_key: String,
120    /// The secret bbs+ key used to create the signature
121    pub issuer_secret_key: String,
122    /// Credential request
123    pub credential_request: BbsCredentialRequest,
124    /// Credential offer linked to the credential request
125    pub credential_offer: BbsCredentialOffer,
126    /// Indices of nquads to be marked as requiredRevealStatements in the credential
127    pub required_indices: Vec<u32>,
128}
129/// API payload for creating a BbsCredentialOffer to be sent by an issuer.
130/// Contains information about how many messages the final credential will hold.
131#[derive(Serialize, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct OfferCredentialPayload {
134    /// DID of the issuer
135    pub issuer: String,
136    /// DID of the subject
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub subject: Option<String>,
139    /// Number of total nquads in the final credential
140    pub nquad_count: usize,
141}
142
143/// API payload for creating a zero-knowledge proof out of a BBS+ signature.
144#[derive(Serialize, Deserialize)]
145#[serde(rename_all = "camelCase")]
146pub struct PresentProofPayload {
147    /// The proof request sent by a verifier
148    pub proof_request: BbsProofRequest,
149    /// All relevant credentials references via the requested credential schema ID
150    pub credential_schema_map: HashMap<String, BbsCredential>,
151    /// Properties to be revealed for each credential by schema ID
152    pub revealed_properties_schema_map: HashMap<String, CredentialSubject>,
153    /// Public key per credential by schema ID
154    pub public_key_schema_map: HashMap<String, String>,
155    /// The respective nquads by respective credential's schema ID
156    pub nquads_schema_map: HashMap<String, Vec<String>>,
157    /// Prover's master secret
158    pub master_secret: String,
159    /// DID of the prover
160    pub prover_did: String,
161    /// Key DID of the prover's public key for the created assertion proof
162    pub prover_public_key_did: String,
163    /// Prover's secret key to create an assertion proof with
164    pub prover_proving_key: String,
165}
166
167/// API payload to create a credential proposal to be sent by a holder.
168#[derive(Serialize, Deserialize)]
169#[serde(rename_all = "camelCase")]
170pub struct CreateCredentialProposalPayload {
171    /// DID of the issuer
172    pub issuer: String,
173    /// DID of the subject
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub subject: Option<String>,
176    /// DID of a credential schema to propose
177    pub schema: String,
178}
179
180/// API payload to create a credential request to be sent by a holder as a response
181/// to a BbsCredentialOffer.
182#[derive(Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct RequestCredentialPayload {
185    /// Credential offering sent by an issuer
186    pub credential_offering: BbsCredentialOffer,
187    /// Master secret of the holder/receiver
188    pub master_secret: String,
189    /// Cleartext values to be signed in the credential
190    pub credential_values: HashMap<String, String>,
191    /// Public key of the issuer
192    pub issuer_pub_key: String,
193    /// Credential Schema credential
194    pub credential_schema: CredentialSchema,
195}
196
197/// API payload to create a BbsProofRequest to be sent by a verifier.
198#[derive(Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct RequestProofPayload {
201    /// DID of the verifier
202    pub verifier_did: Option<String>,
203    /// List of schema IDs to request
204    pub schemas: Vec<String>,
205    /// Attributes to reveal per schema ID
206    pub reveal_attributes: HashMap<String, Vec<usize>>,
207}
208
209/// API payload to revoke a credential as this credential's issuer.
210#[derive(Serialize, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct RevokeCredentialPayload {
213    /// DID of the issuer
214    pub issuer: String,
215    /// revocation list credential
216    pub revocation_list: RevocationListCredential,
217    /// Credential ID to revoke
218    pub revocation_id: String,
219    /// DID of the issuer's public key for verifying assertion proofs
220    pub issuer_public_key_did: String,
221    /// DID of the issuer's secret key for creating assertion proofs
222    pub issuer_proving_key: String,
223}
224
225/// API payload needed to create a credential schema needed for issuing credentials
226#[derive(Serialize, Deserialize)]
227#[serde(rename_all = "camelCase")]
228pub struct CreateCredentialSchemaPayload {
229    /// DID of the schema issuer/owner
230    pub issuer: String,
231    /// Name given to the schema
232    pub schema_name: String,
233    /// A text describing the schema's purpose
234    pub description: String,
235    /// The properties the schema holds
236    pub properties: HashMap<String, SchemaProperty>,
237    /// Names of required properties
238    pub required_properties: Vec<String>,
239    /// Tells a verifier whether properties not found in the schema are to be deemed valid
240    pub allow_additional_properties: bool,
241    /// DID of the issuer's public key to validate the schema's assertion proof
242    pub issuer_public_key_did: String,
243    /// Secret key to sign the schema with
244    pub issuer_proving_key: String,
245    /// DID of the new created schema credential
246    pub credential_did: String,
247}
248
249/// API payload for finishing a UnfinishedBbsCredential as a holder.
250#[derive(Serialize, Deserialize)]
251#[serde(rename_all = "camelCase")]
252pub struct FinishCredentialPayload {
253    /// Credential with blind signature to finish
254    pub credential: UnfinishedBbsCredential,
255    /// Holder's master secret
256    pub master_secret: String,
257    /// Signed values of the credential's signature
258    pub nquads: Vec<String>,
259    /// Issuer's BBS+ public key
260    pub issuer_public_key: String,
261    /// Blinding created during credential request creation
262    pub blinding: String,
263}
264
265/// API payload for verifying a received proof as a verifer.
266#[derive(Serialize, Deserialize)]
267#[serde(rename_all = "camelCase")]
268pub struct VerifyProofPayload {
269    /// BBS+ Presentation to verify
270    pub presentation: ProofPresentation,
271    /// Proof request sent by verifier
272    pub proof_request: BbsProofRequest,
273    /// Relevant BBS+ public keys for each credential schema occuring in this proof
274    pub keys_to_schema_map: HashMap<String, String>,
275    /// Signer address
276    pub signer_address: String,
277    pub nquads_to_schema_map: HashMap<String, Vec<String>>,
278    /// revocation list credential
279    pub revocation_list: RevocationListCredential,
280}
281
282/// API payload to create new BBS+ keys and persist them on the DID document.
283#[derive(Serialize, Deserialize)]
284#[serde(rename_all = "camelCase")]
285pub struct CreateKeysPayload {
286    pub key_owner_did: String,
287}
288
289macro_rules! parse {
290    ($data:expr, $type_name:expr) => {{
291        serde_json::from_str($data)
292            .map_err(|e| format!("{} when parsing {} {}", &e, $type_name, $data))?
293    }};
294}
295
296macro_rules! ignore_unrelated {
297    ($method:expr, $options:expr) => {{
298        if $method != EVAN_METHOD {
299            return Ok(VadePluginResultValue::Ignored);
300        }
301        let type_options: TypeOptions = parse!($options, "options");
302        match type_options.r#type.as_deref() {
303            Some(PROOF_METHOD_BBS) => (),
304            _ => return Ok(VadePluginResultValue::Ignored),
305        };
306    }};
307}
308
309pub struct VadeEvanBbs {
310    signer: Box<dyn Signer>,
311}
312
313impl VadeEvanBbs {
314    /// Creates new instance of `VadeEvanBbs`.
315    pub fn new(signer: Box<dyn Signer>) -> VadeEvanBbs {
316        VadeEvanBbs { signer }
317    }
318}
319
320impl VadeEvanBbs {
321    async fn create_new_keys(
322        &mut self,
323        payload: CreateKeysPayload,
324    ) -> Result<String, Box<dyn Error>> {
325        let keys = Issuer::create_new_keys();
326        let pub_key = base64::encode(keys.0.to_bytes_compressed_form());
327        let secret_key = base64::encode(keys.1.to_bytes_compressed_form());
328
329        let key_id = format!("bbs-key-{}", generate_uuid());
330
331        let serialised_keys = format!(
332            r###"{{
333                "didUrl": "{}#{}",
334                "publicKey": "{}",
335                "secretKey": "{}"
336            }}"###,
337            &payload.key_owner_did, key_id, pub_key, secret_key
338        );
339
340        Ok(serialised_keys)
341    }
342}
343
344#[async_trait(?Send)]
345impl VadePlugin for VadeEvanBbs {
346    /// Runs a custom function, currently supports
347    ///
348    /// - `create_master_secret` to create new master secrets
349    /// - `create_new_keys` to create a new key pair for BBS+ based signatures and persist  this in the given identity's DID document
350    ///
351    /// # Arguments
352    ///
353    /// * `method` - method to call a function for (e.g. "did:example")
354    /// * `function` - currently supports `create_master_secret` and  `create_new_keys`
355    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
356    /// * `payload` - necessary for `create_new_keys`, can be left empty for `create_master_secret`
357    async fn run_custom_function(
358        &mut self,
359        method: &str,
360        function: &str,
361        options: &str,
362        payload: &str,
363    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
364        ignore_unrelated!(method, options);
365        match function {
366            "create_master_secret" => Ok(VadePluginResultValue::Success(Some(
367                serde_json::to_string(&Prover::create_master_secret())?,
368            ))),
369            "create_new_keys" => {
370                let payload: CreateKeysPayload = parse!(&payload, "payload");
371                Ok(VadePluginResultValue::Success(Some(
372                    self.create_new_keys(payload).await?,
373                )))
374            }
375            _ => Ok(VadePluginResultValue::Ignored),
376        }
377    }
378
379    /// Creates a new zero-knowledge proof credential schema.
380    ///
381    /// Note that `options.identity` needs to be whitelisted for this function.
382    ///
383    /// # Arguments
384    ///
385    /// * `method` - method to create a credential schema for (e.g. "did:example")
386    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
387    /// * `payload` - serialized [`CreateCredentialSchemaPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.CreateCredentialSchemaPayload.html)
388    ///
389    /// # Returns
390    /// * `Option<String>` - The created schema as a JSON object
391    async fn vc_zkp_create_credential_schema(
392        &mut self,
393        method: &str,
394        options: &str,
395        payload: &str,
396    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
397        ignore_unrelated!(method, options);
398        let payload: CreateCredentialSchemaPayload = parse!(&payload, "payload");
399
400        let schema = Issuer::create_credential_schema(
401            &payload.credential_did,
402            &payload.issuer,
403            &payload.schema_name,
404            &payload.description,
405            payload.properties,
406            payload.required_properties,
407            payload.allow_additional_properties,
408            &payload.issuer_public_key_did,
409            &payload.issuer_proving_key,
410            &self.signer,
411        )
412        .await?;
413
414        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
415            &schema,
416        )?)))
417    }
418
419    /// Creates a new revocation list and stores it on-chain. The list consists of a encoded bit list which can
420    /// hold up to 131,072 revokable ids. The list is GZIP encoded and will be updated on every revocation.
421    /// The output is a W3C credential with a JWS signature by the given key.
422    ///
423    /// Note that `options.identity` needs to be whitelisted for this function.
424    ///
425    /// # Arguments
426    ///
427    /// * `method` - method to create a revocation list for (e.g. "did:example")
428    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
429    /// * `payload` - serialized [`CreateRevocationListPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.CreateRevocationListPayload.html)
430    ///
431    /// # Returns
432    /// * created revocation list as a JSON object as serialized [`RevocationList`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.RevocationList.html)
433    async fn vc_zkp_create_revocation_registry_definition(
434        &mut self,
435        method: &str,
436        options: &str,
437        payload: &str,
438    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
439        ignore_unrelated!(method, options);
440        let payload: CreateRevocationListPayload = parse!(&payload, "payload");
441
442        let revocation_list = Issuer::create_revocation_list(
443            &payload.credential_did,
444            &payload.issuer_did,
445            &payload.issuer_public_key_did,
446            &payload.issuer_proving_key,
447            &self.signer,
448        )
449        .await?;
450
451        let serialized_list = serde_json::to_string(&revocation_list)?;
452
453        Ok(VadePluginResultValue::Success(Some(serialized_list)))
454    }
455
456    /// Issues a new credential. This requires an issued schema, revocations list, an credential offer
457    /// and a credential request message. This method returns an unfinished credential which has to be post-processed
458    /// by the holder.
459    ///
460    /// # Arguments
461    ///
462    /// * `method` - method to issue a credential for (e.g. "did:example")
463    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
464    /// * `payload` - serialized [`IssueCredentialPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.IssueCredentialPayload.html)
465    ///
466    /// # Returns
467    /// * serialized [`UnfinishedBbsCredential`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.UnfinishedBbsCredential.html)
468    async fn vc_zkp_issue_credential(
469        &mut self,
470        method: &str,
471        options: &str,
472        payload: &str,
473    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
474        ignore_unrelated!(method, options);
475        let payload: IssueCredentialPayload = parse!(&payload, "payload");
476        let public_key: DeterministicPublicKey = DeterministicPublicKey::from(
477            decode_base64(
478                &payload.issuer_public_key,
479                "Issuer Deterministic Public Key",
480            )?
481            .into_boxed_slice(),
482        );
483        let sk: SecretKey = SecretKey::from(
484            decode_base64(&payload.issuer_secret_key, "Issuer Secret Key")?.into_boxed_slice(),
485        );
486
487        let unfinished_credential = Issuer::sign_nquads(
488            &payload.unsigned_vc,
489            &payload.credential_offer,
490            &payload.credential_request,
491            &payload.issuer_public_key_id,
492            &public_key,
493            &sk,
494            payload.required_indices,
495            payload.nquads,
496        )?;
497
498        // ######### Please keep this commented until we have an Rust nquad library #########
499        // let unfinished_credential = Issuer::issue_credential(
500        //     &payload.issuer,
501        //     &payload.subject,
502        //     &payload.credential_offer,
503        //     &payload.credential_request,
504        //     &payload.issuer_public_key_id,
505        //     &public_key,
506        //     &sk,
507        //     schema,
508        //     payload.required_indices,
509        //     payload.nquads,
510        //     &payload.revocation_list_did,
511        //     &payload.revocation_list_id,
512        // )?;
513
514        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
515            &unfinished_credential,
516        )?)))
517    }
518
519    /// Creates a `CredentialOffer` message. A `CredentialOffer` is sent by an issuer and is the response
520    /// to a `CredentialProposal`. The `CredentialOffer` specifies which schema the issuer
521    /// is capable and willing to use for credential issuance.
522    ///
523    /// # Arguments
524    ///
525    /// * `method` - method to create a credential offer for (e.g. "did:example")
526    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
527    /// * `payload` - serialized [`OfferCredentialPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.OfferCredentialPayload.html)
528    ///
529    /// # Returns
530    /// * `Option<String>` - The offer as a JSON object
531    async fn vc_zkp_create_credential_offer(
532        &mut self,
533        method: &str,
534        options: &str,
535        payload: &str,
536    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
537        ignore_unrelated!(method, options);
538
539        let payload: OfferCredentialPayload = parse!(&payload, "payload");
540        let result: BbsCredentialOffer = Issuer::offer_credential(
541            payload.subject.as_deref(),
542            &payload.issuer,
543            payload.nquad_count,
544        )?;
545        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
546            &result,
547        )?)))
548    }
549
550    /// Presents a proof for one or more credentials. A proof presentation is the response to a
551    /// proof request. The proof needs to incorporate all required fields from all required schemas
552    /// requested in the proof request.
553    ///
554    /// # Arguments
555    ///
556    /// * `method` - method to presents a proof for (e.g. "did:example")
557    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
558    /// * `payload` - serialized [`PresentProofPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.PresentProofPayload.html)
559    ///
560    /// # Returns
561    /// * `Option<String>` - The offer as a JSON object
562    async fn vc_zkp_present_proof(
563        &mut self,
564        method: &str,
565        options: &str,
566        payload: &str,
567    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
568        ignore_unrelated!(method, options);
569        let payload: PresentProofPayload = parse!(&payload, "payload");
570
571        let master_secret: SignatureMessage = SignatureMessage::from(
572            decode_base64(&payload.master_secret, "Master Secret")?.into_boxed_slice(),
573        );
574
575        let mut public_key_schema_map: HashMap<String, DeterministicPublicKey> = HashMap::new();
576        for (schema_did, base64_public_key) in payload.public_key_schema_map.iter() {
577            public_key_schema_map
578                .insert(schema_did.clone(), get_dpk_from_string(base64_public_key)?);
579        }
580
581        let result = Prover::present_proof(
582            &payload.proof_request,
583            &payload.credential_schema_map,
584            &payload.revealed_properties_schema_map,
585            &public_key_schema_map,
586            &payload.nquads_schema_map,
587            &master_secret,
588            &payload.prover_did,
589            &payload.prover_public_key_did,
590            &payload.prover_proving_key,
591            &self.signer,
592        )
593        .await?;
594
595        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
596            &result,
597        )?)))
598    }
599
600    /// Creates a new bbs credential proposal. This message is the first in the
601    /// credential issuance flow and is sent by the potential credential holder to the credential issuer.
602    ///
603    /// # Arguments
604    ///
605    /// * `method` - method to create a credential proposal for (e.g. "did:example")
606    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
607    /// * `payload` - serialized [`CreateCredentialProposalPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.CreateCredentialProposalPayload.html)
608    ///
609    /// # Returns
610    /// * `Option<String>` - The proposal as a JSON object
611    async fn vc_zkp_create_credential_proposal(
612        &mut self,
613        method: &str,
614        options: &str,
615        payload: &str,
616    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
617        ignore_unrelated!(method, options);
618        let payload: CreateCredentialProposalPayload = parse!(&payload, "payload");
619        let result: CredentialProposal = Prover::propose_credential(
620            &payload.issuer,
621            payload.subject.as_deref(),
622            &payload.schema,
623        );
624
625        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
626            &result,
627        )?)))
628    }
629
630    /// Requests a credential. This message is the response to a credential offering and is sent by the potential
631    /// credential holder. It incorporates the target schema offered by the issuer, and
632    /// the encoded values the holder wants to get signed. The credential is not stored on-chain and needs to be
633    /// kept private.
634    ///
635    /// # Arguments
636    ///
637    /// * `method` - method to request a credential for (e.g. "did:example")
638    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
639    /// * `payload` - serialized [`RequestCredentialPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.RequestCredentialPayload.html)
640    ///
641    /// # Returns
642    /// * `Option<String>` - A JSON object consisting of the `BbsCredentialRequest` and `SignatureBlinding` (to be stored at the holder's site in a private manner)
643    async fn vc_zkp_request_credential(
644        &mut self,
645        method: &str,
646        options: &str,
647        payload: &str,
648    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
649        ignore_unrelated!(method, options);
650        let payload: RequestCredentialPayload = serde_json::from_str(&payload)
651            .map_err(|e| format!("{} when parsing payload {}", &e, &payload))?;
652        let master_secret: SignatureMessage = SignatureMessage::from(
653            decode_base64(&payload.master_secret, "Master Secret")?.into_boxed_slice(),
654        );
655        let public_key: DeterministicPublicKey = DeterministicPublicKey::from(
656            decode_base64(&payload.issuer_pub_key, "Issuer Deterministic Public Key")?
657                .into_boxed_slice(),
658        );
659        let (credential_request, signature_blinding): (BbsCredentialRequest, SignatureBlinding) =
660            Prover::request_credential(
661                &payload.credential_offering,
662                &payload.credential_schema,
663                &master_secret,
664                payload.credential_values,
665                &public_key,
666            )?;
667        let result = serde_json::to_string(&(
668            credential_request,
669            base64::encode(signature_blinding.to_bytes_compressed_form()),
670        ))?;
671        Ok(VadePluginResultValue::Success(Some(result)))
672    }
673
674    /// Requests a proof for one or more credentials issued under one or more specific schemas and
675    /// is sent by a verifier to a prover.
676    /// The proof request consists of the fields the verifier wants to be revealed per schema.
677    ///
678    /// # Arguments
679    ///
680    /// * `method` - method to request a proof for (e.g. "did:example")
681    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
682    /// * `payload` - serialized [`RequestProofPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.RequestProofPayload.html)
683    ///
684    /// # Returns
685    /// * `Option<String>` - A `ProofRequest` as JSON
686    async fn vc_zkp_request_proof(
687        &mut self,
688        method: &str,
689        options: &str,
690        payload: &str,
691    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
692        ignore_unrelated!(method, options);
693        let payload: RequestProofPayload = parse!(&payload, "payload");
694        let result: BbsProofRequest = Verifier::create_proof_request(
695            payload.verifier_did,
696            payload.schemas,
697            payload.reveal_attributes,
698        )?;
699
700        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
701            &result,
702        )?)))
703    }
704
705    /// Revokes a credential. The information returned by this function needs to be persisted in order to update the revocation list. To revoke a credential, the revoker must be in possession of the private key associated
706    /// with the credential's revocation list. After revocation, the published revocation list is updated on-chain.
707    /// Only then is the credential truly revoked.
708    ///
709    /// Note that `options.identity` needs to be whitelisted for this function.
710    ///
711    /// # Arguments
712    ///
713    /// * `method` - method to revoke a credential for (e.g. "did:example")
714    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
715    /// * `payload` - serialized [`RevokeCredentialPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.RevokeCredentialPayload.html)
716    ///
717    /// # Returns
718    /// * `Option<String>` - The updated revocation list as a JSON object. Contains information
719    /// needed to update the respective revocation list.
720    async fn vc_zkp_revoke_credential(
721        &mut self,
722        method: &str,
723        options: &str,
724        payload: &str,
725    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
726        ignore_unrelated!(method, options);
727        let payload: RevokeCredentialPayload = parse!(&payload, "payload");
728        let updated_list = Issuer::revoke_credential(
729            &payload.issuer,
730            payload.revocation_list,
731            &payload.revocation_id,
732            &payload.issuer_public_key_did,
733            &payload.issuer_proving_key,
734            &self.signer,
735        )
736        .await?;
737
738        let serialized = serde_json::to_string(&updated_list)?;
739
740        Ok(VadePluginResultValue::Success(Some(serialized)))
741    }
742
743    /// Verifies one or multiple proofs sent in a proof presentation.
744    ///
745    /// # Arguments
746    ///
747    /// * `method` - method to verify a proof for (e.g. "did:example")
748    /// * `options` - serialized [`TypeOptions`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.TypeOptions.html)
749    /// * `payload` - serialized [`ValidateProofPayload`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/struct.ValidateProofPayload.html)
750    ///
751    /// # Returns
752    /// * `Option<String>` - A JSON object representing a `BbsProofVerification` type, specifying whether verification was successful
753    async fn vc_zkp_verify_proof(
754        &mut self,
755        method: &str,
756        options: &str,
757        payload: &str,
758    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn Error>> {
759        ignore_unrelated!(method, options);
760
761        let payload: VerifyProofPayload = parse!(&payload, "payload");
762
763        let mut public_key_schema_map: HashMap<String, DeterministicPublicKey> = HashMap::new();
764        for (schema_did, base64_public_key) in payload.keys_to_schema_map.iter() {
765            public_key_schema_map
766                .insert(schema_did.clone(), get_dpk_from_string(base64_public_key)?);
767        }
768
769        let mut verfication_result = Verifier::verify_proof(
770            &payload.presentation,
771            &payload.proof_request,
772            &public_key_schema_map,
773            &payload.signer_address,
774            &payload.nquads_to_schema_map,
775        )?;
776        if verfication_result.status != "rejected" {
777            // check revocation status
778            for cred in &payload.presentation.verifiable_credential {
779                let revoked =
780                    CryptoVerifier::is_revoked(&cred.credential_status, &payload.revocation_list)?;
781                if revoked {
782                    verfication_result = BbsProofVerification {
783                        presented_proof: payload.presentation.id.to_string(),
784                        status: "rejected".to_string(),
785                        reason: Some(format!("Credential id {} is revoked", cred.id)),
786                    };
787                }
788            }
789        }
790
791        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
792            &verfication_result,
793        )?)))
794    }
795
796    /// Finishes a credential, e.g. by incorporating the prover's master secret into the credential signature after issuance.
797    ///
798    /// # Arguments
799    ///
800    /// * `method` - method to update a finish credential for (e.g. "did:example")
801    /// * `options` - JSON string with additional information supporting the request (e.g. authentication data)
802    /// * `payload` - JSON string with information for the request (e.g. actual data to write)
803    ///
804    /// # Returns
805    /// * serialized [`Credential`](https://docs.rs/vade_evan_bbs/*/vade_evan_bbs/application/datatypes/struct.Credential.html) consisting of the credential
806    async fn vc_zkp_finish_credential(
807        &mut self,
808        method: &str,
809        options: &str,
810        payload: &str,
811    ) -> Result<VadePluginResultValue<Option<String>>, Box<dyn std::error::Error>> {
812        ignore_unrelated!(method, options);
813
814        let payload: FinishCredentialPayload = parse!(&payload, "payload");
815
816        let blinding: SignatureBlinding = SignatureBlinding::from(
817            decode_base64(&payload.blinding, "Signature Blinding")?.into_boxed_slice(),
818        );
819        let master_secret: SignatureMessage = SignatureMessage::from(
820            decode_base64(&payload.master_secret, "Master Secret")?.into_boxed_slice(),
821        );
822
823        let public_key: DeterministicPublicKey = get_dpk_from_string(&payload.issuer_public_key)?;
824
825        let credential = Prover::finish_credential(
826            &payload.credential,
827            &master_secret,
828            &payload.nquads,
829            &public_key,
830            &blinding,
831        )?;
832
833        Ok(VadePluginResultValue::Success(Some(serde_json::to_string(
834            &credential,
835        )?)))
836    }
837}