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}