Skip to content

Commit 3675195

Browse files
committed
Misc. small updates
Signed-off-by: lovesh <lovesh.bond@gmail.com>
1 parent 254697a commit 3675195

16 files changed

+382
-46
lines changed

README.md

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,41 @@
33
This repository is a Typescript interface to [Dock's Rust crypto library](https://github.com/docknetwork/crypto). It uses
44
the [WASM wrapper](https://github.com/docknetwork/crypto-wasm).
55

6+
## Contents
7+
- [crypto-wasm-ts](#crypto-wasm-ts)
8+
- [Contents](#contents)
9+
- [Getting started](#getting-started)
10+
- [Build](#build)
11+
- [Test](#test)
12+
- [Overview](#overview)
13+
- [BBS+ Signature](#bbs-signature)
14+
- [Accumulator](#accumulator)
15+
- [Composite proof](#composite-proof)
16+
- [Usage](#usage)
17+
- [BBS+ signatures](#bbs-signatures)
18+
- [Setup](#setup)
19+
- [Signing and verification](#signing-and-verification)
20+
- [Proof of knowledge of signature](#proof-of-knowledge-of-signature)
21+
- [Accumulators](#accumulators)
22+
- [Setup](#setup-1)
23+
- [Updating the accumulator](#updating-the-accumulator)
24+
- [Generating witnesses](#generating-witnesses)
25+
- [Updating witnesses](#updating-witnesses)
26+
- [Prefilled accumulator](#prefilled-accumulator)
27+
- [Composite proofs](#composite-proofs)
28+
- [Terminology](#terminology)
29+
- [Examples](#examples)
30+
- [Selective disclosure](#selective-disclosure)
31+
- [BBS+ signature over varying number of messages](#bbs-signature-over-varying-number-of-messages)
32+
- [Multiple BBS+ signatures](#multiple-bbs-signatures)
33+
- [BBS+ signature together with accumulator membership](#bbs-signature-together-with-accumulator-membership)
34+
- [Getting a blind signature](#getting-a-blind-signature)
35+
- [Verifier-local or opt-in linkability](#verifier-local-or-opt-in-linkability)
36+
- [Social KYC](#social-kyc)
37+
- [Verifiable encryption using SAVER](#verifiable-encryption-using-saver)
38+
- [Bound check using LegoGroth16](#bound-check-using-legogroth16)
39+
- [Optimization](#optimization)
40+
641
## Getting started
742

843
To use this package within your project simply run
@@ -36,7 +71,7 @@ yarn test
3671
## Overview
3772
Following is a conceptual explanation of the primitives.
3873

39-
### BBS+ Signatures
74+
### BBS+ Signature
4075
BBS+ signature allow for signing an ordered list of messages, producing a signature of constant size independent of the number
4176
of messages. The signer needs to have a public-private keypair and signature parameters which are public values whose size
4277
depends on the number of messages being signed. A verifier who needs to verify the signature needs to know the
@@ -93,7 +128,7 @@ A typical use of accumulator looks like:
93128
- Verifier can verify above proof using the current accumulator, the parameters and signer's public key and is convinced
94129
that the user knows of an element and its witness and the (non)-membership.
95130

96-
### Composite proofs
131+
### Composite proof
97132
The above primitives can be combined using the composite proof system. An example is (in zero knowledge) proving knowledge of 2
98133
different signatures and the message lists. Another example is proving knowledge of the signature and messages and certain message's presence (absence)
99134
in an accumulator. Or the knowledge of 5 signatures and proving certain message is the same in the 5 message lists.
@@ -447,17 +482,18 @@ const witnesses = new Witnesses();
447482
witnesses.add(witness1);
448483
```
449484

450-
Prover now uses the `ProofSpec` to create the proof
485+
Prover now uses the `ProofSpec` to create the proof. To ensure that the prover is not replaying, i.e. reusing a proof created by someone else, the verifier can request the prover to include its provided nonce in the proof.
451486

452487
```ts
453-
const proof = CompositeProofG1.generate(proofSpec, witnesses);
488+
const nonce = stringToBytes('a unique nonce given by verifier');
489+
const proof = CompositeProofG1.generate(proofSpec, witnesses, nonce);
454490
```
455491

456492
Verifier can now verify this proof. Note that the verifier does not and must not receive `ProofSpec` from prover, it
457493
needs to generate on its own.
458494

459495
```ts
460-
expect(proof.verify(proofSpec).verified).toEqual(true);
496+
expect(proof.verify(proofSpec, nonce).verified).toEqual(true);
461497
```
462498

463499
##### BBS+ signature over varying number of messages
@@ -530,8 +566,7 @@ const sId2 = statements.add(statement2);
530566
The prover has 2 prove that both credentials contain the same SSN which is same as saying for the 1st signature (1st `Statement`),
531567
attribute at index 0 is equal to 2nd signature's (2nd `Statement`) attribute index 5. This requires the use of a `MetaStatement` to
532568
express this condition, specifically `MetaStatement.witnessEquality` which takes the `WitnessRef` for each witness that needs to be
533-
proven equal. `WitnessRef` for SSN in 1st signature is (0, 0) and in 2nd signature is (1, 5). Create a `WitnessEqualityMetaStatement` to
534-
express that.
569+
proven equal. `WitnessRef` for SSN in 1st signature is (0, 0) and in 2nd signature is (1, 5). Create a `WitnessEqualityMetaStatement` to express that.
535570

536571
```ts
537572
// For proving equality of SSN, messages1[0] == messages2[5], specify using MetaStatement
@@ -895,11 +930,10 @@ proverStatements.add(statement2);
895930

896931
`statement1` is the for proving knowledge of BBS+ signature as seen in previous examples. `statement2` is for proving the encryption of message from a
897932
BBS+ signature. Some things to note about this statement.
898-
- The statement is created using `Statement.saverProver` because it is being created by a prover. A verifier would have
933+
934+
- The statement is created using `Statement.saverProver` because it is being created by a prover. A verifier would have
899935
used `Statement.saverVerifier` to create it and one of the arguments would be different (shown below).
900-
- The argument `saverEncGens` is the encryption generators created by decryptor. However, before they are passed to `Statement.saverProver`,
901-
the are uncompressed (ref. elliptic curve point compression) as shown in the above snippet. Uncompressing them doubles
902-
their size but makes them faster to work with. However, if you still want to use the compressed parameters use `Statement.saverProverFromCompressedParams`
936+
- The argument `saverEncGens` is the encryption generators created by decryptor. However, before they are passed to `Statement.saverProver`, the are uncompressed (ref. elliptic curve point compression) as shown in the above snippet. Uncompressing them doubles their size but makes them faster to work with. However, if you still want to use the compressed parameters use `Statement.saverProverFromCompressedParams`
903937
- `saverEk` is the encryption key created by the decryptor during `setup` but is uncompressed.
904938
- `snarkProvingKey` is the proving key created by the decryptor during `setup` but is uncompressed.
905939

@@ -956,6 +990,7 @@ metaStatements.add(MetaStatement.witnessEquality(witnessEq));
956990
```
957991

958992
The above has a few differences from the prover's statements:
993+
959994
- Instead of using `Statement.saverProver`, verifier uses `Statement.saverVerifier`.
960995
- Instead of proving key, verifier uses verifying key for the snark.
961996

@@ -999,7 +1034,8 @@ A complete example as a test is [here](./tests/composite-proofs/bound-check.spec
9991034

10001035
Allow a verifier to check that some attribute of the credential satisfies given bounds `min` and `max`, i.e. `min <= message <= max`
10011036
without learning the attribute itself. Both `min` and `max` are positive integers. This is implemented using LegoGroth16, a protocol described in the SNARK
1002-
framework [Legosnark](https://eprint.iacr.org/2019/142) in appendix H.2
1037+
framework [Legosnark](https://eprint.iacr.org/2019/142) in appendix H.2.
1038+
To work with negative integers or decimal numbers, they must be converted to positive integers first and this conversion must happen before these are signed. When working with negative integers, add the absolute value of the smallest (negative) integer to all values including bounds. Eg, if the smallest negative number a value can be is -300, the signer should sign `value + 300` to ensure that values are always positive. During the bound check, say the verifier has to check if the value is between -200 and 50, the verifier should ask the prover to the bounds as 100 (-200 + 300) and 350 (50 + 300). When working with decimal numbers, convert them to integers by multiplying with a number to make it integer, like if a decimal value can have maximum of 3 decimal places, they should be multiplied by 1000. The test mentioned above shows these scenarios.
10031039

10041040
For this, the verifier needs to first create the setup parameters which he then shares with the prover. Note that the
10051041
verifier does not have to create them each time a proof needs to be verifier, it can create them once and publish somewhere
@@ -1012,8 +1048,7 @@ from lower and upper bound respectively.
10121048
const provingKey = BoundCheckSnarkSetup();
10131049
```
10141050

1015-
For creating the proof of knowledge of the BBS+ signature and one of the signed message being in certain bounds, the prover
1016-
creates the following 2 statements.
1051+
For creating the proof of knowledge of the BBS+ signature and one of the signed message being in certain bounds, the prover creates the following 2 statements.
10171052

10181053
```ts
10191054
// Signer's parameters
@@ -1033,13 +1068,11 @@ proverStatements.add(statement1);
10331068
proverStatements.add(statement2);
10341069
```
10351070

1036-
`statement1` is the for proving knowledge of BBS+ signature as seen in previous examples. `statement2` is for proving the
1037-
bounds of message from a BBS+ signature. Some things to note about this statement.
1071+
`statement1` is the for proving knowledge of BBS+ signature as seen in previous examples. `statement2` is for proving the bounds of message from a BBS+ signature. Some things to note about this statement.
1072+
10381073
- The statement is created using `Statement.boundCheckProver` because it is being created by a prover. A verifier would have
10391074
used `Statement.boundCheckVerifier` to create it and one of the arguments would be different (shown below).
1040-
- - The argument `snarkProvingKey` is the public parameter created by the verifier. However, before they are passed to `Statement.boundCheckProver`,
1041-
the are uncompressed (ref. elliptic curve point compression) as shown in the above snippet. Uncompressing them doubles
1042-
their size but makes them faster to work with. However, if you still want to use the compressed parameters use `Statement.boundCheckProverFromCompressedParams`
1075+
- The argument `snarkProvingKey` is the public parameter created by the verifier. However, before they are passed to `Statement.boundCheckProver`, they are uncompressed (ref. elliptic curve point compression) as shown in the above snippet. Uncompressing them doubles their size but makes them faster to work with. However, if you still want to use the compressed parameters use `Statement.boundCheckProverFromCompressedParams`
10431076

10441077
The prover then establishes the equality between the message in the BBS+ signature and the bounded message by using
10451078
`WitnessEqualityMetaStatement` as below. `msgIdx` is the index of the bounded message in the array of signed messages
@@ -1094,6 +1127,7 @@ metaStatements.add(MetaStatement.witnessEquality(witnessEq));
10941127
```
10951128

10961129
The above has a few differences from the prover's statements:
1130+
10971131
- Instead of using `Statement.boundCheckProver`, verifier uses `Statement.boundCheckVerifier`.
10981132
- Instead of proving key, verifier uses verifying key for the snark.
10991133

@@ -1129,6 +1163,7 @@ const statement4 = Statement.saverProverFromSetupParamRefs(0, 1, 2, 3, chunkBitS
11291163
```
11301164

11311165
Note the use of `Statement.saverProverFromSetupParamRefs` rather than `Statement.saverProver`. The arguments:
1166+
11321167
- 0 for the encryption generators which are at index 0 in `proverSetupParams`
11331168
- 1 for the commitment generators which are at index 1 in `proverSetupParams`
11341169
- 2 for the encryption key which is at index 2 in `proverSetupParams`
@@ -1172,7 +1207,7 @@ const result = proof.verifyUsingQuasiProofSpec(verifierProofSpec);
11721207

11731208
For a complete example, see [these tests](./tests/composite-proofs/saver.spec.ts)
11741209

1175-
Similarly, for bound checks, use `Statement.boundCheckProverFromSetupParamRefs` and `Statement.boundCheckVerifierFromSetupParamRefs`.
1210+
Similarly, for bound checks, use `Statement.boundCheckProverFromSetupParamRefs` and `Statement.boundCheckVerifierFromSetupParamRefs`.
11761211
For complete example, see [these tests](./tests/composite-proofs/bound-check.spec.ts)
11771212

11781213

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@docknetwork/crypto-wasm-ts",
3-
"version": "0.13.0",
3+
"version": "0.14.0",
44
"description": "Typescript abstractions over Dock's Rust crypto library's WASM wrapper",
55
"homepage": "https://github.com/docknetwork/crypto-wasm-ts",
66
"main": "lib/crypto-wasm-ts/src/index.js",
@@ -21,7 +21,7 @@
2121
"lib": "lib"
2222
},
2323
"dependencies": {
24-
"@docknetwork/crypto-wasm": "0.9.0"
24+
"@docknetwork/crypto-wasm": "0.10.0"
2525
},
2626
"devDependencies": {
2727
"eslint-config-prettier": "^8.3.0",

src/accumulator/accumulator.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,10 @@ export abstract class Accumulator {
8989

9090
/**
9191
* To add a positive number as an accumulator member, encode it first using this.
92-
* Encodes a positive integer of at most 4 bytes
92+
* Encodes a positive safe integer, i.e. of 53 bits
9393
* @param num - should be a positive integer
9494
*/
9595
static encodePositiveNumberAsAccumulatorMember(num: number): Uint8Array {
96-
ensurePositiveIntegerOfSize(num, 32);
9796
return generateFieldElementFromNumber(num);
9897
}
9998

src/bbs-plus/signature.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,10 @@ export abstract class Signature extends BytearrayWrapper {
2323
}
2424

2525
/**
26-
* Encodes a positive integer of at most 4 bytes
26+
* Encodes a positive safe integer, i.e. of 53 bits
2727
* @param num
2828
*/
2929
static encodePositiveNumberForSigning(num: number): Uint8Array {
30-
ensurePositiveIntegerOfSize(num, 32);
3130
return generateFieldElementFromNumber(num);
3231
}
3332

src/bound-check/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { boundCheckSnarkSetup } from '@docknetwork/crypto-wasm';
22
import { LegoProvingKey } from '../legosnark';
33

44
/**
5-
* Create SNARK proving key for verifying bounds of a message, i.e. range proof
5+
* Create SNARK proving key for verifying bounds of a message, i.e. range proof.
6+
* This protocol only works with positive integers so any negative integers or decimal numbers
7+
* must be converted to positive integers
68
* @constructor
79
*/
810
export function BoundCheckSnarkSetup(): LegoProvingKey {

src/composite-proof/proof-spec.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { MetaStatements, Statements } from './statement';
22
import { SetupParam } from './setup-param';
3-
import { generateProofSpecG1 } from '@docknetwork/crypto-wasm';
3+
import { generateProofSpecG1, isProofSpecG1Valid } from '@docknetwork/crypto-wasm';
44

55
/**
66
* The specification used to construct the proof. This contains all the statements and the meta statements.
@@ -18,13 +18,21 @@ export class ProofSpecG1 {
1818
const params = (setupParams ?? new Array<SetupParam>()).map((s) => s.value);
1919
this.value = generateProofSpecG1(statements.values, metaStatements.values, params, context);
2020
}
21+
22+
/**
23+
* Check if the proof spec is valid.
24+
* @returns
25+
*/
26+
isValid(): boolean {
27+
return isProofSpecG1Valid(this.value);
28+
}
2129
}
2230

2331
/**
2432
* The specification used to construct the proof. This contains all the statements and the meta statements.
2533
* The difference between this and `ProofSpecG1` that this does not call WASM to generate a `ProofSpecG1` object that
2634
* corresponds to the `ProofSpec` struct in Rust. This WASM call be expensive due to the serialization overhead and thus
27-
* it's advised to use this when there are a lot of `Statements` or `SetupParam`s
35+
* it's advised to use this when there are a lot of `Statements` or `SetupParam`s.
2836
*/
2937
export class QuasiProofSpecG1 {
3038
statements: Statements;

src/composite-proof/statement.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,8 @@ export class Statement {
307307

308308
/**
309309
* Create statement for proving bounds of a message using LegoGroth 16, for the prover.
310-
* @param min - Inclusive lower bound on the message.
311-
* @param max - Inclusive upper bound on the message.
310+
* @param min - Inclusive lower bound on the message, must be a positive integer.
311+
* @param max - Inclusive upper bound on the message, must be a positive integer.
312312
* @param snarkPk - Proving key for LegoGroth16
313313
*/
314314
static boundCheckProver(min: number, max: number, snarkPk: LegoProvingKeyUncompressed): Uint8Array {
@@ -338,8 +338,8 @@ export class Statement {
338338

339339
/**
340340
* Create statement for proving bounds of a message using LegoGroth 16, for the verifier.
341-
* @param min - Inclusive lower bound on the message.
342-
* @param max - Inclusive upper bound on the message.
341+
* @param min - Inclusive lower bound on the message, must be a positive integer.
342+
* @param max - Inclusive upper bound on the message, must be a positive integer.
343343
* @param snarkVk - Verifying key for LegoGroth16
344344
*/
345345
static boundCheckVerifier(min: number, max: number, snarkVk: LegoVerifyingKeyUncompressed): Uint8Array {

tests/composite-proofs/bbs-signature-and-accumulator.spec.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,18 @@ describe('Proving knowledge of 1 BBS+ signature and a certain message in the acc
111111
const context = stringToBytes('some context');
112112

113113
const proofSpec = new ProofSpecG1(statements, metaStatements, [], context);
114+
expect(proofSpec.isValid()).toEqual(true);
114115

115116
const witness1 = Witness.bbsSignature(sig, unrevealedMsgs, false);
116117
const witness2 = Witness.accumulatorMembership(encodedMessages[userIdIdx], accumWitness);
117118
const witnesses = new Witnesses();
118119
witnesses.add(witness1);
119120
witnesses.add(witness2);
120121

121-
const proof = CompositeProofG1.generate(proofSpec, witnesses);
122+
const nonce = stringToBytes('some unique nonce');
122123

123-
expect(proof.verify(proofSpec).verified).toEqual(true);
124+
const proof = CompositeProofG1.generate(proofSpec, witnesses, nonce);
125+
126+
expect(proof.verify(proofSpec, nonce).verified).toEqual(true);
124127
});
125128
});

tests/composite-proofs/blind-signature.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ describe('Getting a blind signature, i.e. signature where signer is not aware of
5555
statements.add(statement1);
5656

5757
const proofSpec = new ProofSpecG1(statements, new MetaStatements());
58+
expect(proofSpec.isValid()).toEqual(true);
5859

5960
// The witness to the Pedersen commitment contains the blinding at index 0 by convention and then the hidden messages
6061
const committeds = [blinding];

0 commit comments

Comments
 (0)