zkverifier_kit

package module
v1.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2024 License: MIT Imports: 12 Imported by: 0

README

ZKVerifier Kit

Introduction

This repository is an SDK for connecting external services with Rarimo passport verification system that may be used in back-end services. Its main purpose is providing a convenient methods that can be used in any system without deep introduction in the Rarimo system structure.

Usage

General usage looks like this:

package main

import (
	kit "github.com/rarimo/zkverifier-kit"
	"github.com/rarimo/zkverifier-kit/root"
)

func main() {
	rv := root.NewVerifier(contractCaller, reqTimeout)
	
	v, err := kit.NewVerifier(
		kit.PassportVerification,
		nil,
		kit.WithVerificationKeyFile("key.json"),
		kit.WithEventID("304358862882731539112827930982999386691702727710421481944329166126417129570"),
		kit.WithAgeAbove(18),
		kit.WithCitizenships("UKR"),
		kit.WithIdentityVerifier(rv),
		kit.WithIdentitiesCounter(0),
		kit.WithIdentitiesCreationTimestampLimit(1847321000),
	)
	if err != nil {
		// ...
	}
	// data is an abstract event data that you expect to be in proof
	err = v.VerifyProof(proof, kit.WithEventData(data))
	if err != nil {
		// ...
	}
}

Let's break this down.

Configurable root verifier

Firstly, you instantiate identity root verifier, which will verify the IdStateRoot public signal with contract call. You can refer to our generated contract bindings in poseidonsmt package. However, maybe, you would like to create the verifier from config map.

Here is configuration sample that you should have in config.yaml of your app:

root_verifier:
  rpc: https://your-rpc
  contract: 0x...
  request_timeout: 10s

You can get values with gitlab.com/distributed_lab/kit/kv package. Then just create the verifier from config:

    getter := kv.MustFromEnv()
    config := root.NewVerifierProvider(getter, root.PoseidonSMT)
	rv := config.ProvideVerifier()
Custom verification key

If you specify WithVerificationKeyPath, the app will try to open the file and convert its contents to bytes. Check out example_verification_key.json. You don't have to get the key this way: just convert your key to bytes and pass it directly without the mentioned option:

v, err := kit.NewVerifier(
	kit.PassportVerification,
	keyBytes,
	options...
)
Notes about options

Each option adds new validation rule to the proof, except WithVerificaitonKeyFile. Most of the options can be combined, but here is what you should consider:

  • If you pass non-nil verification key, don't use WithVerificationKeyFile
  • Don't use WithEventData together with WithRarimoAddress, because the address check is basically the data check with extra validation
  • It is recommended to use WithIdentitiesCounter and WithIdentitiesCreationTimestampLimit together, because they imply a shared business logic of protection against double-eligibility.

You have two ways of providing options: globally (NewVerifier, NewPassportVerifier) and locally (VerifyProof). The latter override the former.

More usage examples can be found in verifier tests.

Proof format

Proof can be gained from the front-end apps or related Rarimo mobile applications. In general, it has such format:

type ZKProof struct {
    Proof struct {
        A        []string   `json:"pi_a"`
        B        [][]string `json:"pi_b"`
        C        []string   `json:"pi_c"`
        Protocol string     `json:"protocol"`
    } `json:"proof"`
    PubSignals []string `json:"pub_signals"`
}

In our systems mostly used ZKProof type is the one from iden3 package.

Documentation

Index

Constants

View Source
const (
	GlobalPassport proofType = iota
	GeorgianPassport
	PollParticipation
)
View Source
const (
	Nullifier pubSignalID = iota
	BirthDate
	ExpirationDate
	Citizenship
	EventID
	EventData
	IdStateRoot
	Selector
	TimestampUpperBound
	IdentityCounterUpperBound
	BirthdateUpperBound
	ExpirationDateLowerBound

	PersonalNumberHash
	DocumentType
	CurrentDate

	ParticipationEventID
	NullifiersTreeRoot
)

Variables

View Source
var ErrVerificationKeyRequired = errors.New("verification key is required")

Functions

func Indexes

func Indexes(t proofType) map[pubSignalID]int

Indexes returns public signals indexes based on proof type provided. Use it when accessing public signals values in provided ZKP. Proof type must be supported by this package.

func ORError

func ORError(one, another error, fieldNames [2]string) val.Errors

func PubSignalsCount

func PubSignalsCount(t proofType) int

PubSignalsCount returns the exact count of pub signals in proof. Use for validation on need to access specific fields, as Verifier already validates length.

Types

type PubSignalGetter

type PubSignalGetter struct {
	ProofType proofType
	Signals   []string
}

PubSignalGetter is a structure to extract public signals from abstract ZKP in a convenient way. It is an alternative for Indexes to initialize once and reuse for the same proof type and signals.

func (*PubSignalGetter) Get

func (p *PubSignalGetter) Get(id pubSignalID) string

Get extracts public signal by its identifier. Returns empty string on invalid id, proof type or pub signals.

type Verifier

type Verifier struct {
	// contains filtered or unexported fields
}

Verifier is a structure representing some instance for validation and verification zero knowledge proof generated by Rarimo system.

func NewVerifier

func NewVerifier(verificationKey []byte, options ...VerifyOption) (*Verifier, error)

NewVerifier creates a new Verifier instance. VerificationKey is required to VerifyGroth16, usually you should just read it from file. Optional parameters will take part in proof verification on Verifier.VerifyProof call.

If you provided WithVerificationKeyFile option, you can pass nil as the first arg.

func (*Verifier) VerifyProof

func (v *Verifier) VerifyProof(proof zkptypes.ZKProof, options ...VerifyOption) error

VerifyProof method verifies ZK proof and checks public signals. The public signals to validate are defined in the VerifyOption list. Firstly, you pass initial values to verify in NewVerifier. In case when custom values are required for different proofs, the options can be passed to VerifyProof, which override the initial ones.

Filtered validation.Errors are always returned, unless this is internal error. You may use errors.As to assert whether it's validation or internal error.

type VerifyOption

type VerifyOption func(*VerifyOptions)

VerifyOption type alias for function that may add new values to VerifyOptions structure. It allows to create convenient methods With... that will add new value to the fields for that structure.

func WithAgeAbove

func WithAgeAbove(age int) VerifyOption

WithAgeAbove adds new age check. It is an integer (e.g. 10, 18, 21) above which the person's age must be in proof.

func WithCitizenships

func WithCitizenships(citizenships ...string) VerifyOption

WithCitizenships adds new available citizenship/s to prove that user is a resident of specified country. Function takes an arbitrary number of strings that consists from Alpha-3 county codes, described in the ISO 3166 international standard (e.g. "USA", "UKR", "TUR").

func WithDocumentType

func WithDocumentType(docType string) VerifyOption

WithDocumentType is used for DocumentType verification in GeorgianPassport proof

func WithEventData

func WithEventData(raw []byte) VerifyOption

WithEventData takes raw data for which the proof should be generated. This value format has to be validated before the signals validation because the kit checks ONLY the correspondence of these values. The raw value should be exactly in the same format as it was passed in the proof generation process and the length of the byte array is up to 31 bytes.

Example: service takes Ethereum address, validates address format on their side, then converts address to bytes array and passes that bytes to the event data input in proof generation. After this precisely the same value has to be passed in the WithEventData function.

func WithEventID

func WithEventID(id string) VerifyOption

WithEventID takes event identifier as a string that represents big number in a decimal format, i.e. it is compared with value from proof without conversions. This is used for PollParticipation flow too as verifier's (challenged) event ID.

func WithIdentitiesCounter

func WithIdentitiesCounter(count int64) VerifyOption

WithIdentitiesCounter takes maximum amount of identities that user can have during proof verification.

On proof verification either this or WithIdentitiesCreationTimestampLimit pass.

func WithIdentitiesCreationTimestampLimit

func WithIdentitiesCreationTimestampLimit(unixTime int64) VerifyOption

WithIdentitiesCreationTimestampLimit takes the upper bound for timestamp when user might create identities.

On proof verification either this or WithIdentitiesCounter should pass.

func WithPassportRootVerifier

func WithPassportRootVerifier(v root.Verifier) VerifyOption

WithPassportRootVerifier takes an abstract verifier that should verify IdStateRoot against the identity tree.

func WithPollParticipationEventID

func WithPollParticipationEventID(id string) VerifyOption

WithPollParticipationEventID takes decimal eventID

func WithPollRootVerifier

func WithPollRootVerifier(v root.Verifier) VerifyOption

WithPollRootVerifier takes an abstract verifier that should verify NullifiersTreeRoot against the state

func WithProofSelectorValue

func WithProofSelectorValue(selector string) VerifyOption

WithProofSelectorValue takes selector as a string that represents bit mask in a decimal format.

func WithProofType

func WithProofType(t proofType) VerifyOption

WithProofType select your proof type to use specific pub signals indexes. Default is GlobalPassport.

func WithVerificationKeyFile

func WithVerificationKeyFile(name string) VerifyOption

WithVerificationKeyFile takes a string that represents the name of the file with verification key. The file is read on NewVerifier call. If you are providing this option along with the key argument, the latter will be overwritten by the read from file.

type VerifyOptions

type VerifyOptions struct {
	// contains filtered or unexported fields
}

VerifyOptions structure that stores all fields that may be validated before proof verification. All elements must be able to have "zero" value in order to skip it during validation. For structure validation `github.com/go-ozzo/ozzo-validation/v4` is used, so IsEmpty method has to work correct with each field in order to have supposed logic.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL