Lovesh Harchandani from Dock presents their approach to anonymous credentials and dives in on the various predicates that can be proven in zero knowledge. In over 90 minutes of discussion, we cover what these cryptographic techniques are, how they enable several important use cases for digital identity credentials, and we stretch James Monaghan's ability to keep up as interviewer by taking a look at the source code which makes it all possible! We show how various zero knowledge primitives we've built can be used in a modular fashion to solve real-world use cases. We cover privacy-preserving signature schemes, zero knowledge attribute equalities, range proofs, and verifiable encryption based on ZK-SNARKs, expressing arbitrary predicates as Circom programs and creating ZK proofs for them and blinded credentials (issuer is unaware of all attributes). For anyone interested in the technical underpinnings of this new frontier of digital identity, this episode is a must!
Anonymous credentials with range proofs, verifiable encryption, ZKSNARKs, Circom support, and blinded issuance - Lovesh Harchandani
1. Our approach to anonymous credentials
Anonymous credentials with range proofs,
verifiable encryption, ZKSNARKs, Circom support,
and blinded issuance
www.dock.io
https://creativecommons.org/licenses/by-sa/4.0/ SSIMeetup.org
Lovesh Harchandani
2. 1. Empower global SSI
communities
2. Open to everyone interested
in SSI
3. All content is shared with CC
BY SA
Alex Preukschat
@AlexPreukschat
James Monaghan
@james_monaghan
Coordinating Nodes
SSIMeetup.org | @SSIMeetup
SSIMeetup objectives
3. Agenda
I assume the audience is familiar with verifiable credentials and anonymous credentials.
● Some use cases that you can build with our codebase
● The codebase - various repos and their structure
● Supported primitives and signature schemes
● Composite proof system
● Using schemas and encodings for credentials
● Accumulators for revocation
● Attribute equality
● Pseudonyms
● ZK-SNARKs to satisfy predicates
● Enforcing bounds on attributes (range proofs)
● Expressing arbitrary predicates as Circom
● Verifiable encryption
● Requesting blinded credentials
4. Use-cases
● Proving certain attributes are equal across credentials without revealing them, eg. proving the SSN is
same in all credentials without revealing it. Or the first and last names are same in all credentials. Useful
when verifying that all the credentials are about the same subject.
● User registration with user ids based on credentials. Achieved with pseudonyms.
● Proving bounds (min, max) on credential attributes. Eg. checking salary is greater/less than certain
amount, checking if credential is expired or not.
● Expressing complex predicates in a programming language (Circom) and creating a zero knowledge
proof. Eg. checking the yearly income calculated from several pay slip credentials is greater/less than
certain amount, checking if total assets are greater than liabilities from a balance sheet credential,
checking if the student’s grade is appropriate and marks in certain subject satisfy the criteria
5. Use-cases (continued)
● Making credential usage inspectable/traceable with verifiable encryption of credential attributes. Eg. a
credential usage where the SSN attribute is encrypted for a 3rd party like a regulator.
● Blinded credentials where some attributes are kept private from the issuer. Eg.
○ Credential with a secret key as an attribute which only the holder knows
○ Requesting a credential containing his SSN as an attribute without revealing it to the issuer but
proving to him that it is the same as in another credential (a more trusted source)
○ Requesting credential with some attributes hidden from the issuer but encrypted for a 3rd party
6. Codebase
● The core crypto logic is written in Rust and is no_std compatible.
● Relies on arkworks for math which is written primarily by folks at UC Berkeley.
● Rust code implements various primitives like signature schemes (BBS+, etc), accumulators, ZK-SNARK
integration, verifiable encryption, etc.
● Rust code is oblivious to credentials, blockchain or any application specific logic like how to encode
attributes as numbers.
● Rust code is compiled to a WASM package to be used with Javascript/Typescript.
● The Typescript repo uses above WASM package to implement anonymous credentials. Even this code is
oblivious to blockchain.
● Most of this presentation will focus on using the Typescript repo.
● The blockchain SDK uses above Typescript repo and interacts with the blockchain to fetch public keys,
accumulator info, etc.
7. Cryptographic primitives
●The Rust code implements the following (relevant to this session):
○ Signature schemes
■ BBS+
■ BBS (from 2023)
■ Pointcheval-Sanders (from this Coconut paper)
○ Schnorr’s Proof of Knowledge (PoK) for Pedersen commitments
○ Pairing based accumulator
○ Verifiable encryption using SAVER protocol
○ ZK-SNARK protocol LegoGroth16 from the LegoSNARK paper.
8. Cryptographic primitives (continued)
○ Composite proof system
■ Invokes above mentioned primitives as sub-protocols, and gets their corresponding proofs as
sub-proofs, and combines them to create a composite proof.
■ Eg, to prove that a BBS+ signed credential is non-revoked and certain attribute is verifiably
encrypted, composite proof system will invoke 3 sub-protocols, for BBS+, accumulator and
SAVER, get 3 sub-proofs and bind them using Schnorr’s PoK.
■ Enforce attribute equality using Schnorr’s PoK
■ Has optimizations like reusing sub-proofs (when applicable), making proof generation faster
9. Building anonymous credentials
Schemas and Encodings
● Credential are JSON objects and schema defines the structure of the credential - attributes names, their
position in the credential, any nesting and their encoding.
● Encoding defines how each credential attribute must be converted to a finite field element (a positive
integer).
● Different attribute types can different encodings. Eg.
○ a string used in selective disclosure can be hashed to give a finite field element
○ negative numbers need an offset to be made positive
○ decimal numbers need a multiple to make them integer
○ …
● We use JSON-schema syntax for schemas
● Each credential has the schema embedded as one of the attribute and this attribute is always
disclosed.
Signing credentials
● Signature schemes like BBS+ need list of finite field elements to sign
● Because credentials are JSON object, we flatten them deterministically to a list before signing or
verifying
10. Example schema
{
'$schema': 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
credentialSubject: {
type: 'object',
properties: {
fname: { type: 'string' },
lname: { type: 'string' },
sensitive: {
type: 'object',
properties: {
email: { type: 'string' },
SSN: { '$ref': '#/definitions/encryptableString' },
}
},
timeOfBirth: { type: 'integer', minimum: 0 },
score: { type: 'number', multipleOf: 0.1, minimum: -100 }
}
},
},
definitions: {
encryptableString: { type: 'string' },
encryptableCompString: { type: 'string' }
}
}
Attribute that will be used in
selective disclosure
Attribute can be used in verifiable
encryption
Attribute will be a positive integer
and can be use in range proofs
Attribute can be a negative decimal
and can be use in range proofs
11. Accumulator
● Support pairing based accumulator for revocation.
● Revocation check can be modelled as either accumulator membership or non-membership.
● During issuance, holder gets a witness from the issuer that it needs to keep updated as per the
accumulator.
● Two options to keep the witness updated
○ As credentials are issued/revoked, issuer publishes the required data on a public database like
blockchain
○ Issuer sends the required data using the direct communication channel with holder
● Accumulator operations can be batched
○ Issuer can publish data for multiple issuances/revocations
○ Holders can combine data from many updates to update their witness
● Using a pre-filled accumulator is recommended as it only changes during revocation and not issuance.
● Can be used for zero-knowledge (non)membership checks in general.
● Blockchain storage
○ Only the latest accumulator is stored in the state
○ Updates are stored in the transaction data (like calldata in Ethereum)
○ Past accumulator values are indexed using events and thus can be efficiently retrieved.
12. Zero knowledge aribute equality
● Useful when multiple credentials are being used and certain attributes are required to be same in
all/some of them but can’t be disclosed to the verifier.
● Achieved by using the same randomness in Schnorr’s Proof of Knowledge (PoK) protocol for attributes
that need to be proven equal.
● Consider the following case of 2 credentials with attributes a1, a2, a3, a4 and b1, b2, b3, b4.
○ Proving possession of a credentials involves proving knowledge of the signature and above
attributes when (roughly) expressed as
■ <......> + G1 * a1 + G2 * a2 + G3 * a3 + G4 * a4
■ <......> + G1 * b1 + G2 * b2 + G3 * b3 + G4 * b4
○ Generally holder will chooses random values r1, r2, …, r8, one for each attribute but when proving
some attribute equal, say a1 = b3, holder will choose the same random value r for both.
○ In above 2 expressions, G1, G2, .. are public parameters and technically could be different among
different credentials.
○ Each of the above expression is called a Pedersen commitment to the attributes.
13. Pseudonyms
● Anonymous credentials keep the holder unlinkable but pseudonyms allow the holder to be linkable (by
choice). Eg. registering at a verifier and using the same registration id in subsequent interactions.
● Holders can create different pseudonyms with different verifiers.
● Each pseudonym is created using secret(s) where the holder keeps the secret with himself and shares
the pseudonym with the verifier like private-public key pairs.
● The secret(s) could be
○ random - unbounded pseudonyms
○ credential attributes - bounded pseudonyms
● Pseudonyms are Pedersen commitments and rely on Schnorr’s PoK.
○ A pseudonym P = G1 * a1 + G2 * a2 + …
○ a1, a2 are the pseudonym secrets and G1, G2 are public parameters which should be generated
trustlessly.
○ When using the above pseudonym, holder will send P to the verifier and prove the knowledge of
a1, a2.
○ If a1, a2 were from the credential, then their equality will be proved using the technique from
previous slide
14. Using ZK-SNARKs for predicates
● For predicates like range proofs, ZK-SNARKs based on LegoGroth16 are used.
● LegoGroth16 can be considered an extension to Groth16, proof contains a Pedersen commitment to the
witnesses .
○ LegoGroth16 proof = (<Groth16 proof>, G1 * w1 + G2 * w2 + ..)
○ Above LegoGroth16 proof is composed of 2 parts, a Groth16 proof and a Pedersen commitment to
the witnesses.
○ Schnorr’s PoK is used to prove that the witnesses in above Pedersen commitment are same as
credential attributes as described earlier, w1 = a1 and w2 = a2 for attributes a1, a2.
● Similar to Groth16, it requires a trusted setup which is acceptable as the setup is done by the verifier.
● Groth16 requires the predicate to be written as an arithmetic circuit.
15. Using ZK-SNARKs for range proofs
● For commonly used predicate like range proof (bound check), the circuit is hardcoded in the Rust code.
● For range proof, attributes should be converted to positive integers while issuing. Eg,
○ An attribute whose value is -50 and its minimum is -60 should be encoded as 10 (60-50).
○ An attribute whose value is 2.35 and can have 2 decimal points should be encoded as 235
(2.35*100)
○ Values which could be negative and decimal will have an offset and multiple, like -30.55 will be
2945 ((60-30.55)*100)
● Range proofs are relatively expensive to create. We allow them to be reused at cost of some storage
overhead for the holder.
16. Expressing predicates as Circom
● It's not possible to imagine all possible predicates verifiers can need!
● Verifiers can express the predicate in a program language called Circom and create the proof.
● Circom is built for writing programs for which ZK-SNARKs need to be created.
● A workflow using Circom looks like this:
a. Verifier expresses the predicates as a Circom program.
b. Verifier compiles the program to get some artifacts (R1CS file, WASM file)
c. Verifier using artifacts from step 2, creates proving and verification keys and shares the proving key
with the holder (holder will create proof).
d. Holder will use R1CS, WASM files and proving key to create the proof.
e. Verifier will use the verification key to verify the proof.
● Steps a, b, and c from above are part of a one time setup.
● The proof output in step d above is a LegoGroth16 proof and contains a Pedersen commitment to the
witnesses.
● We use Schnorr’s PoK to prove that the witnesses in the commitment are the credential attributes.
17. Encrypting aributes using verifiable encryption
● Anonymity offered by credentials could be considered too high in some cases. Eg. A regulator wants to
know the identity of each holder interacting with the verifier.
● But we don’t want to share the identity with the verifier, we only want to assure the verifier that the
regulator can learn our identity.
● For this we encrypt the holder’s identity (some unique attribute(s)) for the regulator.
● The verifier needs to be convinced that the holder has encrypted the desired attribute and encrypted for
the regulator. We get this with verifiable encryption.
● We use a Groth16 based ZK-SNARK called SAVER for this.
● Like LegoGroth16, proofs from SAVER also contain a commitment to the witness (not exactly a
commitment to the witness but to the witness in a decomposed form).
18. Verifiable encryption (continued)
● We use similar techniques to bind these proofs to the credential attributes.
● The flow is like this:
○ Regulator does the SNARK setup and publishes it including it encryption key
○ Holder while creating the proof uses the SNARK setup to verifiably encrypt its attribute(s) and
sends the proof along with the ciphertext.
○ Verifier can check that the ciphertext satisfies the requirements.
○ The verifier can share the ciphertext with the regulator who can decrypt it.
● Similar to range proofs, these are relatively expensive to create so we allow them to be reused at cost of
some storage overhead for the holder.
19. Blinded credentials
● In cases when holders want to embed attributes in the credential but want to keep them secret from
the issuer, they use blinded credentials.
● The secret attribute could be a secret key later used in pseudonyms.
● Or the holder wants to embed some attributes in a credential that are the same in some other
credential without revealing to the issuer.
● The holder can also prove predicates about the secret attributes like before - the issuer acts as the
verifier.
● We use the same technique as before to achieve this - create a Pedersen commitment to the secret
attributes, prove any predicates about them using techniques mentioned before, use Schnorr’s PoK to
bind them and send the proof to the issuer.
20. Blinded credentials
● Assuming a credential can contain attributes a1, a2, a3, a4, a5 and holder wants to hide a1 and a2
while getting a credential containing all 5, holder’s request will contain a commitment of this form
○ C = G1 * a1 + G2 * a2 + H * r
● Holder creates proofs for any predicate on a1, a2
● Using Schnorr’s PoK, holder proves that a1, a2 in predicates are same as in the commitment C.
● Holder “unblinds” the blinded credential making it a regular credential.