zkPass
zkPass Developer's Guide
zkPass Developer's Guide
  • â›Šī¸Introduction
  • ZKPASS OVERVIEW
    • đŸ›ī¸Architecture
    • 🧱zkPass Components
    • 🤝Trust Models
    • 🚚Deployment
      • Public-Cloud Hosting
      • Private-Cloud Hosting
      • On-Premise Hosting
    • đŸŽ¯SDK Objectives
    • 🔑API Key
  • zkPass Modules
    • â˜ī¸DVR
      • đŸ—ģHigh Level View
      • đŸ—ī¸Key Concepts
        • User Data
        • DVR Info
        • zkPass Proof
      • đŸ‘ĨDVR Client Roles
        • Data Issuer
          • Providing User Data Retrieval API
        • Data Holder
          • 1. Retrieving the DVR
          • 2. Retrieving the User Data
          • 3. Generating the Proof
          • 4. Verifying the Proof
        • Proof Verifier
          • 1. Providing DVR Retrieval API
          • 2. Providing Proof Verification API
      • 🔎DVR Query
        • Building Query Engine
        • Processing Query
        • Query Grammar
      • đŸ—ī¸Integration Guidelines
      • 🌊DVR Workflows
  • SDK Tutorial
    • Typescript
      • Running Code
      • Code Snippet
      • Directory Structure
    • Rust
      • Running Code
      • Code Snippet
      • Directory Structure
  • API Reference
    • Typescript
      • Classes
        • Class: DvrModuleClient
      • Functions
        • Functions: ffiHelper
        • Functions: jwtHelper
        • Functions: publicKeyOptionUtils
      • Type Aliases
        • Types
        • Types: ffiType
      • Interfaces
        • Interfaces
      • Constants
        • Constants
        • Enums
      • Errors
    • Rust
      • Building Rust doc
    • RESTful API
      • Overview
      • Endpoints
        • Generate Proof
      • Utilities
        • Generate Key Pair
        • Sign User Data and DVR
        • Encrypt User Data and DVR
      • Errors
  • Glossary
    • DVR
    • User Data
    • ZKP
Powered by GitBook
On this page
Export as PDF
  1. zkPass Modules
  2. DVR
  3. DVR Client Roles

Proof Verifier

Previous4. Verifying the ProofNext1. Providing DVR Retrieval API

Last updated 1 year ago

The Proof Verifier should take these two steps for the integration with zkPass:

Sample Implementation

A sample implementation for a proof verifier is provided on the zkpass-sdk repo, as shown here:

â˜ī¸
đŸ‘Ĩ
Providing a REST API to retrieve the DVR
Providing a REST API to verify ZkPass Proof
https://github.com/gl-zkPass/zkpass-sdk/blob/main/rust/zkpass-demo/src/proof_verifier.rs
/*
 * proof_verifier.rs
 * Simulating the Proof Verifier process for the zkPass Demo
 *
 * ---
 * References:
 *   https://docs.ssi.id/zkpass/zkpass-developers-guide/privacy-apps/dvr/dvr-client-roles/proof-verifier
 * ---
 * Copyright (c) 2024 PT Darta Media Indonesia. All rights reserved.
 */
use crate::{
    helper::extract_payload_without_validation,
    lib_loader::{
        generate_query_token,
        get_dvr_id_from_proof,
        verify_zkpass_proof as verify_zkpass_proof_ffi,
    },
    sample_keys::{ issuer_pubkey, verifier_pubkey, VERIFIER_PRIVKEY },
};
use dvr_types::{
    DvrDataFfi,
    ExpectedDvrMetadataFfi,
    UserDataRequestFfi,
    PublicKeyOptionFfi,
    PublicKeyOptionTagFfi,
    PublicKeyOptionUnionFfi,
    KeysetEndpointFfi,
    PublicKeyFfi,
};
use lazy_static::lazy_static;
use serde_json::Value;
use std::{ collections::HashMap, io::prelude::*, sync::Mutex, time::Instant, ffi::CString };
use tracing::trace;
use uuid::Uuid;

//
//  Global table to store the generated DVR values
//  The Verifier needs to keep track of all generated DVRs
//  so that it can verify the proof metadata
//  Note: The hash table entry should have time-expiration
//
lazy_static! {
    static ref DVR_TABLE: Mutex<HashMap<String, String>> = {
        let map = HashMap::new();
        Mutex::new(map)
    };
}

// This struct ensures that the data reference is valid
pub struct PublicKeyOptionHolder {
    key_x: CString,
    key_y: CString,
    empty_str: CString,
    pub public_key_option: PublicKeyOptionFfi,
}

// This struct ensures that the data reference is valid
#[allow(dead_code)]
struct UserDataRequestsHolder {
    tags: Vec<CString>,
    user_data_requests: Vec<UserDataRequestFfi>,
    key_x: CString,
    key_y: CString,
    empty_str: CString,
    public_key_option: PublicKeyOptionFfi,
}

//
//  Simulating the Proof Verifier
//
pub struct ProofVerifier {
    pub user_data_tags: Vec<String>,
}

impl Default for ProofVerifier {
    fn default() -> Self {
        ProofVerifier {
            user_data_tags: Vec::new(),
        }
    }
}

impl ProofVerifier {
    ///
    /// Generates the user data requests.
    ///
    fn user_data_requests(&self) -> Box<UserDataRequestsHolder> {
        let issuer_public_key_option_holder = self.generate_issuer_public_key_option();
        let user_data_tags = self.user_data_tags.clone();
        let tags: Vec<CString> = user_data_tags
            .iter()
            .map(|tag| CString::new(tag.as_str()).unwrap())
            .collect();

        let user_data_requests = tags
            .iter()
            .map(|tag| {
                UserDataRequestFfi {
                    key: tag.as_ptr(),
                    value: issuer_public_key_option_holder.public_key_option.clone(),
                }
            })
            .collect();

        Box::new(UserDataRequestsHolder {
            tags,
            user_data_requests: user_data_requests,
            key_x: issuer_public_key_option_holder.key_x,
            key_y: issuer_public_key_option_holder.key_y,
            empty_str: issuer_public_key_option_holder.empty_str,
            public_key_option: issuer_public_key_option_holder.public_key_option,
        })
    }

    ///
    /// Simulates the Proof Verifier's get_dvr_token REST API.
    ///
    pub fn get_dvr_token(
        &mut self,
        zkvm: &str,
        dvr_file: &str,
        user_data_tags: Vec<&String>
    ) -> String {
        self.user_data_tags = user_data_tags
            .iter()
            .cloned()
            .map(|s| s.clone())
            .collect();

        let mut query_content = std::fs::File::open(dvr_file).expect("Cannot find the dvr file");
        let mut query = String::new();
        query_content.read_to_string(&mut query).expect("Should not have I/O errors");
        trace!("query={}", query);

        let query: Value = serde_json::from_str(&query).unwrap();

        //
        //  Proof Verifier's integration points with the zkpass-client SDK library
        //  (for get_dvr_token REST API)
        //

        let query_string = serde_json::to_string(&query).unwrap();

        let zkvm_cstring = CString::new(zkvm).unwrap();
        let dvr_title_cstring = CString::new("My DVR").unwrap();
        let dvr_id_cstring = CString::new(Uuid::new_v4().to_string()).unwrap();
        let query_cstring = CString::new(query_string).unwrap();

        let verifier_public_key_option_holder = self.generate_verifier_public_key_option();
        let verifier_public_key_option = verifier_public_key_option_holder.public_key_option;

        let user_data_requests_holder = self.user_data_requests();
        let user_data_requests = user_data_requests_holder.user_data_requests;
        let user_data_requests_slice = user_data_requests.as_slice();

        //
        // Step 1: Create the DVR object.
        //
        let dvr_data = DvrDataFfi {
            zkvm: zkvm_cstring.as_ptr(),
            dvr_title: dvr_title_cstring.as_ptr(),
            dvr_id: dvr_id_cstring.as_ptr(),
            query: query_cstring.as_ptr(),
            user_data_requests: user_data_requests_slice.as_ptr(),
            user_data_requests_len: user_data_requests_slice.len() as u64,
            dvr_verifying_key: verifier_public_key_option,
        };

        //
        //  Step 2: Call dvr client's generate_query_token function
        //          to digitally-sign the dvr data.
        //
        let dvr_token = unsafe { generate_query_token(VERIFIER_PRIVKEY, dvr_data) };
        let payload = extract_payload_without_validation(&dvr_token).unwrap();

        // save the dvr to a global hash table
        // this will be needed by the validator to check the proof metadata
        let mut dvr_table = DVR_TABLE.lock().unwrap();
        if let Some(data) = payload.get("data") {
            let dvr_id = data["dvr_id"].as_str().unwrap();
            dvr_table.insert(dvr_id.to_string(), data.to_string());
        }

        dvr_token
    }

    ///
    /// Generates the public key option.
    ///
    pub fn generate_public_key_option(&self, is_verifier: bool) -> Box<PublicKeyOptionHolder> {
        let (key_x, key_y) = if is_verifier { verifier_pubkey() } else { issuer_pubkey() };
        let key_x = CString::new(key_x).unwrap();
        let key_y = CString::new(key_y).unwrap();
        let empty_str = CString::new("").unwrap();

        let public_key_option = PublicKeyOptionFfi {
            tag: PublicKeyOptionTagFfi::PublicKey,
            value: PublicKeyOptionUnionFfi {
                keyset_endpoint: KeysetEndpointFfi {
                    jku: empty_str.as_ptr(),
                    kid: empty_str.as_ptr(),
                },
                public_key: PublicKeyFfi {
                    x: key_x.as_ptr(),
                    y: key_y.as_ptr(),
                },
            },
        };

        Box::new(PublicKeyOptionHolder {
            key_x,
            key_y,
            empty_str,
            public_key_option,
        })
    }

    ///
    /// Generates the verifier public key option.
    ///
    fn generate_verifier_public_key_option(&self) -> Box<PublicKeyOptionHolder> {
        self.generate_public_key_option(true)
    }

    ///
    /// Generates the issuer public key option.
    ///
    fn generate_issuer_public_key_option(&self) -> Box<PublicKeyOptionHolder> {
        self.generate_public_key_option(false)
    }

    //
    /// Simulates the Proof Verifier's verify_zkpass_proof REST API.
    ///
    pub async fn verify_zkpass_proof(&self, zkvm: &str, zkpass_proof_token: &str) -> String {
        println!("\n#### starting zkpass proof verification...");
        let start = Instant::now();

        let url = std::env
            ::var("ZKPASS_URL")
            .unwrap_or("https://staging-zkpass.ssi.id".to_string());

        let some_ttl: u64 = 3600;
        let dvr_id = unsafe { get_dvr_id_from_proof(zkpass_proof_token) };
        let expected_dvr = DVR_TABLE.lock().unwrap().get(&dvr_id).unwrap().clone();
        let expected_dvr_cstring = CString::new(expected_dvr).unwrap();

        let user_data_requests_holder = self.user_data_requests();
        let user_data_requests = user_data_requests_holder.user_data_requests;
        let user_data_requests_slice = user_data_requests.as_slice();

        //
        // Step 1: Create the expected metadata object.
        //
        let expected_metadata = ExpectedDvrMetadataFfi {
            ttl: some_ttl,
            dvr: expected_dvr_cstring.as_ptr(),
            user_data_verifying_keys: user_data_requests_slice.as_ptr(),
            user_data_verifying_keys_len: user_data_requests_slice.len() as u64,
        };

        //
        // Step 2: Call zkpass_client.verify_zkpass_proof to verify the proof.
        //
        let result = unsafe {
            verify_zkpass_proof_ffi(&url, zkvm, zkpass_proof_token, expected_metadata)
        };

        let duration = start.elapsed();
        println!("#### verification completed [time={:?}]", duration);

        result
    }
}