{"id":26233684,"url":"https://github.com/docknetwork/crypto-wasm-ts","last_synced_at":"2025-10-16T21:22:35.457Z","repository":{"id":57108967,"uuid":"424344613","full_name":"docknetwork/crypto-wasm-ts","owner":"docknetwork","description":"Typescript abstractions over Rust crypto library's WASM wrapper ","archived":false,"fork":false,"pushed_at":"2025-04-09T04:44:45.000Z","size":14736,"stargazers_count":34,"open_issues_count":4,"forks_count":10,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-09-28T02:27:59.753Z","etag":null,"topics":["accumulator","anonymous-credentials","circom","cryptography","privacy-enhancing-technologies","signature-scheme","zero-knowledge-proofs","zk-snarks"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/docknetwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-11-03T18:52:39.000Z","updated_at":"2025-09-21T22:16:53.000Z","dependencies_parsed_at":"2023-02-08T10:31:55.863Z","dependency_job_id":"13fdce9a-c30a-4f9b-81e5-eda176f53f68","html_url":"https://github.com/docknetwork/crypto-wasm-ts","commit_stats":{"total_commits":119,"total_committers":4,"mean_commits":29.75,"dds":0.3445378151260504,"last_synced_commit":"bc11e0f94a79372f34c79a29af77bca318dc5f54"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/docknetwork/crypto-wasm-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docknetwork%2Fcrypto-wasm-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docknetwork%2Fcrypto-wasm-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docknetwork%2Fcrypto-wasm-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docknetwork%2Fcrypto-wasm-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/docknetwork","download_url":"https://codeload.github.com/docknetwork/crypto-wasm-ts/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/docknetwork%2Fcrypto-wasm-ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278703578,"owners_count":26031204,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accumulator","anonymous-credentials","circom","cryptography","privacy-enhancing-technologies","signature-scheme","zero-knowledge-proofs","zk-snarks"],"created_at":"2025-03-13T01:17:05.220Z","updated_at":"2025-10-07T00:43:07.966Z","avatar_url":"https://github.com/docknetwork.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# crypto-wasm-ts\n\nThis repository is a Typescript interface to [Dock Labs' Rust crypto library](https://github.com/docknetwork/crypto). It uses \nthe [WASM wrapper](https://github.com/docknetwork/crypto-wasm).\n\n## Contents\n- [crypto-wasm-ts](#crypto-wasm-ts)\n  - [Contents](#contents)\n  - [Getting started](#getting-started)\n    - [Build](#build)\n    - [Test](#test)\n  - [Overview](#overview)\n    - [BBS Signature](#bbs-signature)\n    - [Accumulator](#accumulator)\n    - [Composite proof](#composite-proof)\n  - [Usage](#usage)\n    - [BBS signatures](#bbs-signatures)\n      - [Setup](#setup)\n      - [Signing and verification](#signing-and-verification)\n      - [Proof of knowledge of signature](#proof-of-knowledge-of-signature)\n    - [Accumulators](#accumulators)\n      - [Setup](#setup-1)\n      - [Updating the accumulator](#updating-the-accumulator)\n      - [Generating witnesses](#generating-witnesses)\n      - [Updating witnesses](#updating-witnesses)\n      - [Prefilled accumulator](#prefilled-accumulator)\n    - [Composite proofs](#composite-proofs)\n      - [Terminology](#terminology)\n      - [Examples](#examples)\n        - [Selective disclosure](#selective-disclosure)\n        - [BBS signature over null-valued messages](#bbs-signatures-over-null-valued-messages)\n        - [Multiple BBS signatures](#multiple-bbs-signatures)\n        - [BBS signature together with accumulator membership](#bbs-signature-together-with-accumulator-membership)\n        - [Getting a blind signature](#getting-a-blind-signature)\n        - [Pseudonyms](#pseudonyms)\n        - [Social KYC](#social-kyc)\n    - [Verifiable encryption using SAVER](#verifiable-encryption-using-saver)\n      - [Encoding for verifiable encryption](#encoding-for-verifiable-encryption)\n    - [Bound check (range proof)](#bound-check-range-proof)\n      - [Encoding for negative or decimal numbers](#encoding-for-negative-or-decimal-numbers)\n    - [Optimization](#optimization)\n    - [Working with messages as JS objects](#working-with-messages-as-js-objects)\n    - [Writing predicates in Circom](#writing-predicates-in-circom)\n    - [Anonymous credentials](#anonymous-credentials)\n\n## Getting started\n\nTo use this package within your project simply run\n\n```\nnpm install @docknetwork/crypto-wasm-ts\n```\n\nOr with [Yarn](https://yarnpkg.com/)\n\n```\nyarn add @docknetwork/crypto-wasm-ts\n```\n\n### Build\n\nTo build the project run:\n\n```\nyarn build\n```\n\n### Test\n\nTo run the all tests in the project run:\n\n```\nyarn test\n```\n\n## Overview\nFollowing is a conceptual explanation of the primitives.\n\n### BBS Signatures\nDisclaimer: There's multiple variations of the BBS scheme available out there, of which BBS+ was thought to be the version that is proven to be secure. However, in a recent [paper](https://eprint.iacr.org/2023/275), it was proven that in the initial BBS design was secure, with even a smaller signature size. This version is also the one used in the [IRTF standardization effort](https://identity.foundation/bbs-signature/draft-irtf-cfrg-bbs-signatures.html).\n\nThe BBS signature Scheme (henceforth referred to as just BBS) allows for signing an ordered list of messages, producing a signature of constant size independent of the number\nof messages. The signer needs to have a public-private keypair and signature parameters which are public values whose size\ndepends on the number of messages being signed. A verifier who needs to verify the signature needs to know the\nsignature parameters used to sign the messages and the public key of the signer. In the context of anonymous credentials, \nmessages are called attributes.  \nBBS also allows a user to request a blind signature from a signer where the signer does not know 1 or more messages\nfrom the list. The user can then unblind the blind signature to get a regular signature which can be verified by a verifier in\nthe usual way. Such blind signatures can be used to hide a user specific secret like a private key or some unique identifier\nas a message in the message list and the signer does not become aware of the hidden message.     \nWith a BBS signature, a user in possession of the signature and messages and create a [zero-knowledge proof of knowledge](https://en.wikipedia.org/wiki/Proof_of_knowledge)\nof the signature and the corresponding signed messages such that he can prove to a verifier that he knows a signature and the\nmessages and optionally reveal one or more of the messages.  \nA typical use of BBS signatures looks like:\n- Signature parameters of the required size are assumed to exist and published at a public location. The signer can create\n  his own or reuse parameters created by another party.\n- Signer creates a public-private keypair and publishes the public key. \n  The keypair can be reused for signing other messages as well.\n- User requests a signature from the signer.\n- Signer signs the message list using the signature parameters and his private key.\n- User verifies the signature on the  message list using the signature parameters and signer's public key\n- User creates a proof of knowledge of the signature and message list and optionally reveals 1 or more messages to the verifier.\n- The verifier uses the signature parameters and signer's public key to verify this proof.\n  If successful, the verifier is convinced that the user does have a signature from the\n  signer and any messages revealed were part of the message list signed by the signer.\n\n### Accumulator\nAn accumulator is a set-like data structure in which elements can be added or removed but the size of the accumulator remains constant.\nHowever, an accumulator cannot be directly checked for presence of an element, an element needs to have accompanying data called\nthe witness (its the manager's signature on the element), the element and the witness and these together can be used to check the presence or absence of the element.\nAn accumulator can be considered similar to the root of the merkle tree where the inclusion proof is the witness of the element \n(non-membership proofs aren't possible with simple merkle trees). As with merkle trees, as elements are added or removed from the accumulator,\nthe witness (inclusion proof) needs to be updated for the current accumulated value (root).\n\n2 kinds of accumulators are provided, **positive** and **universal**. \nPositive support only membership witnesses while universal support both membership and non-membership witnesses. \nCreating non-membership witnesses is expensive however, and the cost depends on the number of members present in the accumulator.\nBoth accumulators are owned by an accumulator manager who has the private key to the accumulator\nand only the owner can add or remove elements or create witnesses using the accumulator.  \nAn accumulator allows proving membership of a member (or non-member) and the corresponding witness in zero knowledge, meaning\na user in possession of an accumulator member (or non-member) and the witness can convince a verifier that he knows of an\nelement present (or absent) in the accumulator without revealing the element or the witness. Note, the like merkle trees,\nwitnesses (inclusion proof) are tied to the accumulated value (root) and need to be updated as accumulator changes.  \nWitnesses can be updated either by the accumulator manager using his private key or the manager can publish witness update\ninformation and the updates (additions and removals) and users can update their witnesses.\nA typical use of accumulator looks like:\n- Accumulator parameters are assumed to exist and published at a public location. The manager can create his own params or\n  reuse existing ones.\n- Accumulator manager creates a keypair and publishes the public key.\n- Accumulator manager initializes the accumulator and publishes the accumulator.\n- User requests an element to be added to the accumulator, then requests the membership witness from the manager.\n  The user could have also requested a non-membership witness for an absent element.\n- Signer checks whether requested element is not already present (in his database) and adds the element to the\n  accumulator if not already present. He publishes the new accumulator and creates a (non)membership witness and sends to the user.\n- User verifies the (non)membership using the element, the witness, the new accumulated value and the accumulator params and signer's public key.\n- To prove knowledge of (non)membership in zero knowledge, user and verifier agree on a proving key. Anyone can generate this.\n- User can create a proof of knowledge of the element and the witness corresponding to the accumulator.\n- Verifier can verify above proof using the current accumulator, the parameters and signer's public key and is convinced\n  that the user knows of an element and its witness and the (non)-membership.\n\n### Composite proof\nThe above primitives can be combined using the composite proof system. An example is (in zero knowledge) proving knowledge of 2\ndifferent signatures and the message lists.\nAnother example is proving knowledge of the signature and messages and certain message's presence (absence) in an accumulator.\nOr the knowledge of 5 signatures and proving certain message is the same in the 5 message lists.\n\n## Usage\n\nBefore calling any function that calls the underlying WASM, use `initializeWasm` to load the WASM module.\nThis function returns a promise which is resolved once the WASM module is successfully loaded.  \n\n```ts\nimport { initializeWasm } from '@docknetwork/crypto-wasm-ts'\n// Load the WASM module\nawait initializeWasm();\n```\n\n### Supported Signature Schemes\nThe library has support for BBS, BBS+, PS, and BBDT16 schemes.  \nAlthough they're similar in many aspects, specially for BBS and BBS, you're advised to consult the tests for changes\nbetween the different schemes. \nHowever, you can make a good guess just by looking at the schema [here](./tests/scheme.ts#80).\n\nFor all the following examples, BBS will be used, but the concepts should be transferable to other schemes.\n\n### BBS signatures\n\nBBS signatures sign an ordered list of messages and thus it is important to serialize your signing payload in this format. \nEg, in case of a credential with attributes in JSON format where each attribute is a key, convert the JSON to a list of \nattributes and this conversion should be deterministic, meaning attributes should always end up in the same order. Following \nis a conversion of a JSON credential with 4 attributes to a list where the values are placed in the alphabetical order of the keys:\n\nGiven JSON\n```json\n{\n  \"ssn\": \"12345678\",\n  \"fname\": \"John\",\n  \"lname\": \"Smith\",\n  \"city\": \"NYC\"\n}\n```\n\nConverted to list\n```\n[\"NYC\", \"John\", \"Smith\", \"12345678\"]\n```\n\nNow each element of the above list must be converted to bytearrays, i.e. `Uint8Array` and the type of above list becomes `Uint8Array[]`.\n\n#### Setup\n\nBefore messages can be signed, 2 things are needed:\n\n- **Signature parameters**: Public values, that can be created by anyone but must be known to the signer and verifier to sign and verify respectively. To create them, the number of messages (attributes) being signed must be known and the size of the parameters increases with the number. In the above example, number of attributes is 4. These parameters can be generated randomly or deterministically by using a publicly known label. It is advised to use the latter as it allows for extending/shrinking the same parameters when number of messages change.\n- **Keypair**: To create and verify BBS signatures, the signer (issuer in case of a credential) needs to create a secret key to sign, public key to verify. \n\n  2 ways of generating signature parameters\n\n  ```ts\n  import { BBSSignatureParams } from '@docknetwork/crypto-wasm-ts';\n  const messageCount = 4;\n\n  // Randomly generated params\n  const paramsRandom = BBSSignatureParams.generate(messageCount);\n\n  // the following function will be useful throughout the documentation\n  function stringToBytes(message: string): Uint8Array {\n    return Uint8Array.from(Buffer.from(message, 'utf-8'));\n  }\n  const label = stringToBytes(\"My sig params\");\n  // Deterministically generated params\n  const paramsDeterministc = BBSSignatureParams.generate(messageCount, label);\n\n  // Deterministic params can be extended if messageCount changes, say to 5 or 3\n  const paramsDeterministc5 = paramsDeterministc.adapt(5);\n  const paramsDeterministc3 = paramsDeterministc.adapt(3);\n  ```\n  \n  Generating a keypair once signature parameters are created.\n\n  ```ts\n  import { BBSKeypair } from '@docknetwork/crypto-wasm-ts';\n  const keypair = BBSKeypair.generate(paramsDeterministc);\n  const sk = keypair.secretKey;\n  const pk = keypair.publicKey;\n  ```\n\n  #### ByteArray messages\n  Each one of the messages should be a Uint8Array \n\n  ```ts\n  const messages: Uint8Array[] = [\"NYC\", \"John\", \"Smith\", \"12345678\"].map(element =\u003e stringToBytes(element));\n  ```\n\n#### Signing and verification\n\nWhen the messages are arbitrary bytes, they need to be encoded to a field element (a number in certain range). You can either let the signing function encode it by passing \nthe `encode` argument as true to encode it using your own encoding function.\n  \n  Letting the signing function encode  \n  ```ts\n  import { BBSSignature } from '@docknetwork/crypto-wasm-ts';\n  \n  // The signing function will encode bytes to a field element as true is passed\n  const sig = BBSSignature.generate(messages, sk, paramsDeterministc, true);\n  \n  // As the messages are not encoded, pass true to the verification function to make it encode messages before verifying the signature.\n  const result = sig.verify(messages, pk, paramsDeterministc, true);\n  \n  console.assert(result.verified);\n  ```\n  \n  Passing pre-encoded messages to signing function\n  ```ts\n  const encodedMessages = [];\n\n  for (let i = 0; i \u003c messages.length; i++) {\n    encodedMessages.push(generateFieldElementFromBytes(messages[i]));\n  }\n  // The signing function will not encode as false is passed\n  const sig = BBSSignature.generate(encodedMessages, sk, params, false);\n\n  // As the messages are pre-encoded, pass false to the verification function to avoid encoding messages before verifying the signature.\n  const result = sig.verify(encodedMessages, pk, params, false);\n  console.assert(result.verified);\n  ```\n\n#### Proof of knowledge of signature\n\nProving and verifying knowledge of signature can be done with or without using the composite proof system but this doc will only describe using the composite proof system. For the other way, see tests [here](./tests/scheme.spec.ts)\n\nThe code for BBS lives [here](./src/bbs/). \n\n### Accumulators\n\n#### Setup\n\nSimilar to BBS signatures, accumulators also have a setup phase where public parameters and keys are generated and these \npublic values need to be published. The accumulator manager's signing key is needed to update the accumulator or create \na witness and the public key is needed to verify the (non)membership. This document talks only about Positive accumulator, \nfor universal accumulator see the corresponding tests.\n\n  Similar to BBS, parameters can be generated randomly or deterministically.\n  ```ts\n  // Randomly generated params\n  const paramsRandom = PositiveAccumulator.generateParams();\n  \n  const label = stringToBytes(\"My sig params\");\n  // Deterministically generated params\n  const params = PositiveAccumulator.generateParams(label);\n  ```\n\n  Generating a keypair once parameters are created.\n  ```ts\n  const keypair = PositiveAccumulator.generateKeypair(params);\n  ```\n\n  Initialize the accumulator\n  ```ts\n  const accumulator = PositiveAccumulator.initialize(params);\n  ```\n\nCare must be taken to not add duplicate elements in the accumulator or remove non-existent elements or creating witness of \nnon-existing elements. The accumulator itself cannot make such checks and thus this state must be tracked separately. \nThe interface for such a state is [IAccumulatorState](src/accumulator/IAccumulatorState.ts). Its strongly recommended that \nthis state should be passed as an argument to the add, remove, and other functions that expect it. However, it's not mandatory\nas the caller might have its own way of avoiding such issues. The tests below use an in-memory state `InMemoryState` which \nimplements `IAccumulatorState` interface.\n\n#### Updating the accumulator\n\nElements can be added/removed individually or in a batch. Before adding an element, it must be encoded to a field element. \nEncoding a positive integer can be done using `encodePositiveNumberAsAccumulatorMember`, arbitrary bytes can be encoded as \n`encodeBytesAsAccumulatorMember`.\n\n  Adding 2 elements in the accumulator\n  ```ts\n  import { InMemoryState } from '@docknetwork/crypto-wasm-ts';\n  \n  const state = new InMemoryState();\n  \n  const e1 = Accumulator.encodePositiveNumberAsAccumulatorMember(101);\n  const bytes: Uint8Array = stringToBytes(\"Some ID\");\n  const e2 = Accumulator.encodeBytesAsAccumulatorMember(bytes);\n  \n  await accumulator.add(e1, keypair.secretKey, state);\n  await accumulator.add(e2, keypair.secretKey, state);\n  ```\n  \n  Removing an existing element\n  ```ts\n  await accumulator.remove(e2, keypair.secretKey, state);\n  ```\n\n  Adding multiple elements in a batch\n  ```ts\n  const e3 = Accumulator.encodePositiveNumberAsAccumulatorMember(103);\n  const e4 = Accumulator.encodePositiveNumberAsAccumulatorMember(104);\n  \n  await accumulator.addBatch([e3, e4], keypair.secretKey, state);\n  ```\n\n  Adding and removing multiple elements in a batch\n  ```ts\n  // Elements to add\n  const additions: Uint8Array[] = [e1, e2];\n  // Elements to remove\n  const removals: Uint8Array[] = [e3, e4];\n  \n  await accumulator.addRemoveBatches(additions, removals, keypair.secretKey, state);\n  ```\n\n#### Generating witnesses\n\nOnce an element is added to the accumulator by the manager, a witness is required to verify the membership.\nAlso required is the accumulator value when the witness was created, this value should be publicly available. \n\n  Generating a membership witness\n  ```ts\n  // Note that the secret key is needed to create the witness\n  const witness = await accumulator.membershipWitness(e4, keypair.secretKey, state)\n  ```\n\n  Verify the membership\n  ```ts\n  // The accumulated value `accumulator.accumulated` is posted publicly\n  const verifAccumulator = PositiveAccumulator.fromAccumulated(accumulator.accumulated);\n\n  // Note that only public values needed to verify the membership\n  console.assert(verifAccumulator.verifyMembershipWitness(e4, witness, pk, params));\n  ```\n\n#### Updating witnesses\n\nAs the accumulator changes, the witness needs to be updated as well. The witness can be updated without the manager's \nhelp if the updates (additions, removals) are known.\n\n  Update witness after an addition\n  ```ts\n  // Say element e8 was added after the witness of e4 was created\n  witness.updatePostAdd(e8, e4, accumulator.accumulated);\n  ```\n\n  Update witness after a removal\n  ```ts\n  // Say element e1 was removed after the witness of e4 was created\n  witness.updatePostRemove(e1, e4, accumulator.accumulated);\n  ```\n\nThe above method of updating the witness by going over each update is slow. The manager can make this process more efficient \nfor all members by publishing a `WitnessUpdatePublicInfo` built using the updates to the older accumulator. All members can then \nuse this public information to update their witnesses.\n\n  Manager creates `WitnessUpdatePublicInfo` and then updates the accumulator\n  ```ts\n  // The current accumulated value is accumulator.accumulated\n  \n  // Elements to add\n  const additions: Uint8Array[] = [...];\n  // Elements to remove\n  const removals: Uint8Array[] = [...];\n  \n  // This will be published along with `additions` and `removals`\n  const witnessUpdateInfo = WitnessUpdatePublicInfo.new(accumulator.accumulated, additions, removals, keypair.secretKey);\n  \n  // Update the accumulator now\n  await accumulator.addRemoveBatches(additions, removals, keypair.secretKey, state);\n  ```\n\n  The member can now fetch the update information and update as\n  ```ts\n  witness.updateUsingPublicInfoPostBatchUpdate(e4, additions, removals, witnessUpdInfo);\n  ```\n\nThe member can update his witness given multiple such updates using `updateUsingPublicInfoPostMultipleBatchUpdates`. See the [tests](./tests/accumulator.spec.ts) for examples. \n\n#### Prefilled accumulator\n\nThe above workflow requires that after every addition to the accumulator, the new accumulator must be published along with witness \nupdate info so that other members can update their witnesses. This is however expensive as the accumulator and update info might be posted \non the blockchain and also every existing member has to update its witness. One way to mitigate that is to create pre-filled \naccumulators meaning that before publishing the accumulator the first time, the manager adds all the member ids in the accumulator.\nThis strategy assumes that member ids are either predictable like monotonically increasing numbers or the manager can internally keep \na map of random ids like UUIDs to a number. Now when the manager actually wants to allow a member to prove membership, he can \ncreate a witness for that member but the accumulator value remains same and thus the witness for existing members also remain same. \nIt should be noted though that changing the accumulator value causes change in all existing witnesses and thus its better \nto make a good estimate of the number of members during prefill stage. See [this test](tests/prefilled-positive-accumulator.spec.ts) for \na complete example using a positive accumulator.\n\n\nProof of membership and non-memberships can be done with or without using the composite proof system but this doc will only describe\nusing the composite proof system.\n\nThe code for accumulators lives [here](./src/accumulator).\n\n### Composite proofs\n\n#### Terminology\n\n- **Statement** - The kind of proof that needs to be done and the public parameters needed to verify that proof. Eg. a BBS signature\n  statement contains public key of the signer, signature params, any revealed messages, etc. Each statement in a proof has a unique index.\n- **Witness** - Private data that needs to be kept hidden from the verifier. This can be the messages/attributes that are not being disclosed, \n  the signature itself, the accumulator member, accumulator witness. Every witness corresponds to some `Statement`.\n- **WitnessRef** - A witness might consist of several hidden data points, hidden attributes for example. To refer to each data \n  point uniquely, a pair of indices is used where the 1st item is the `Statement` index and 2nd item is index of that data point in the witness. \n- **MetaStatement** - Describes a condition that must hold between witnesses of several statements or the same statement. Eg. to \n  express equality between attributes of 2 credentials, `MetaStatement` will refer to the `WitnessRef` of each attribute. This is\n  public information as well.\n- **SetupParam** - Represents (public) setup parameters of different protocols.\nThis is helpful when the same setup parameter needs to be passed to several `Statement`s\n- **ProofSpec** - This is the proof specification and its goal is to unambiguously define **all** what needs to be proven.\nThis is created from all `Statement`s, `MetaStatement`s and an optional context. Both prover and verifier should independently create this.\nThe prover uses the `ProofSpec` and all `Witness`es to create the proof and the verifier uses the`ProofSpec` to verify the proof.  \n\n#### Examples\n\n##### Selective disclosure \n\nA complete example is shown in this [test](tests/composite-proofs/single-signature.spec.ts).  \n\nProving knowledge of 1 BBS signature over the attributes and only disclosing some attributes. Say there are 5 attributes in the \ncredential: SSN, first name, last name, email and city, and they are present in the attribute list in that order. The prover wants \nto reveal his last name and city, but not any other attribute while proving that he possesses such a credential signed by the issuer.\n\n```ts\n// The attributes, [SSN, first name, last name, email, city]\nconst messages: Uint8Array[] = [\n  \"230-95-4628\", \"Harry\", \"Potter\", \"harry@potter.com\", \"Little Whinging\"\n].map(element =\u003e stringToBytes(element));\n\n// Public values\nconst params: BBSSignatureParams;\nconst pk: BBSPublicKey;\n\n// The signature\nconst sig: BBSSignature = ...;\n\n// Prover prepares the attributes he wants to disclose, \n// i.e. attribute index 2 and 4 (indexing is 0-based), and the ones he wants to hide. \nconst revealedMsgIndices: Set\u003cnumber\u003e = new Set();\nrevealedMsgIndices.add(2);\nrevealedMsgIndices.add(4);\n\n// revealedMsgs are the attributes disclosed to the verifier\nconst revealedMsgs: Map\u003cnumber, Uint8Array\u003e = new Map();\nrevealedMsgs.set(2, messages[2]);\nrevealedMsgs.set(4, messages[4]);\n\n// unrevealedMsgs are the attributes hidden from the verifier\nconst unrevealedMsgs: Map\u003cnumber, Uint8Array\u003e = new Map();\nunrevealedMsgs.set(0, messages[0]);\nunrevealedMsgs.set(1, messages[1]);\nunrevealedMsgs.set(1, messages[3]);\n```\n\nSince there is only 1 kind of proof, i.e. the knowledge of a BBS signature and the signed attributes, there would be only 1 `Statement`. \n\n```ts\nimport { Statement, Statements } from '@docknetwork/crypto-wasm-ts'\n\n// Create a BBS signature, true indicates that attributes/messages are arbitrary bytes and should be encoded first.\nconst statement1 = Statement.bbsSignatureProverConstantTime(paramsDeterministc, revealedMsgs, true);\nconst statements = new Statements();\nstatements.add(statement1);\n\n// Optional context of the proof, this can specify the reason why the proof was created or date of the proof, or self-attested attributes (as JSON string), etc.\nconst context = stringToBytes('some context');\n```\n\nOnce it has been established what needs to be proven, `ProofSpec` needs to be created which represents all the requirements. \nBoth the prover and verifier should independently construct this `ProofSpec`. Note that there are no `MetaStatements` as there are no \nother conditions on the witnesses and thus its empty.\n\n```ts\nimport { ProofSpec, MetaStatements } from '@docknetwork/crypto-wasm-ts';\n\nconst ms = new MetaStatements();\nconst proofSpec = new ProofSpec(statements, ms, [], context);\n```\n\nProver creates `Witness` using the signature and hidden attributes \n\n```ts\nimport { Witness, Witnesses } from '@docknetwork/crypto-wasm-ts';\n\nconst witness1 = Witness.bbsSignatureConstantTime(sig, unrevealedMsgs, true);\nconst witnesses = new Witnesses();\nwitnesses.add(witness1);\n```\n\nProver 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.\n\n```ts\nimport { CompositeProof } from '@docknetwork/crypto-wasm-ts';\n\nconst nonce = stringToBytes('a unique nonce given by verifier');\nconst proof = CompositeProof.generate(proofSpec, witnesses, nonce);\n```\n\nVerifier can now verify this proof. Note that the verifier does not and must not receive `ProofSpec` from prover,  \nit needs to generate on its own.  \nAlso, note the usage of `bbsSignatureVerifierConstantTime` instead.\n\n```ts\nconst statement1 = Statement.bbsSignatureVerifierConstantTime(sigParams, keyPair.publicKey, revealedMsgs, true);\nconst statements = new Statements();\nstatements.add(statement1);\nconst context = stringToBytes('some context');\n\nconst ms = new MetaStatements();\nconst verifierProofSpec = new ProofSpec(statements, ms, [], context);\n\nconsole.assert(proof.verify(verifierProofSpec, nonce).verified);\n```\n\n##### BBS signatures over null-valued messages \n\nThe examples above assumed all messages have values in them. However, in some cases, one or more attributes will be null within the credential.  \nAn example in which some of the messages correspond to attributes with null values (e.g. N/A) is a education qualification credential of a person. Someone with a highschool-level education will \nhave N/A for attributes like university name, major, etc.\n\nOne way to deal with this is to decide on some sentinel value like 0 or 'N/A' for all the null attributes\nand disclose those values to the verifier.  \nAnother is to have a certain attribute (e.g. first message) in the credential specify which attribute \nindices are null and always reveal this attribute.  \nA complete example of the latter is shown in this\n[test](tests/composite-proofs/variable-number-of-messages.spec.ts).\n\n##### Multiple BBS signatures\n\nA complete example is shown in this [test](tests/composite-proofs/many-bbs-signatures.spec.ts).\n\nProving knowledge of 2 BBS signatures over the attributes and only disclosing some attribute and proving equality of 1 attribute \nwithout disclosing it. Say there are 2 credentials and hence 2 BBS signatures. One credential has 5 attributes: SSN, first name, \nlast name, email and city and the other has 6 attributes name, email, city, employer, employee id and SSN and in that order. \nThe prover wants to prove that he has those 2 credentials, reveal his employer name and prove that SSN in both credentials is \nthe same without revealing the SSN.\n\n```ts\n// The attributes from 1st credential, [SSN, first name, last name, email, city]\nconst messages1: Uint8Array[] = [...];\n// The attributes from 2nd credential, [name, email, city, employer, employee id, SSN]\nconst messages2: Uint8Array[] = [...];\n\n// Public values for 1st issuer\nconst parasm1: BBSSignatureParams;\nconst pk1: BBSPublicKey;\n\n// Public values for 2nd issuer\nconst parasm2: BBSSignatureParams;\nconst pk2: BBSPublicKey;\n\n// The signature from 1st credential\nconst sig1: BBSSignature = ...;\n\n// The signature from 2nd credential\nconst sig2: BBSSignature = ...;\n```\n\nSince the prover is proving possession of 2 BBS signatures, there will be 2 `Statement`s. Also, for the 2nd signature prover is \nrevealing _employer_ attribute, which is at index 3.\n\n```ts\n// Statement for signature of 1st signer, not revealing any messages to the verifier\nconst statement1 = Statement.bbsSignatureProverConstantTime(params1, new Map(), true);\n\n// Statement for signature of 2nd signer, revealing 1 message to the verifier\nconst revealedMsgIndices: Set\u003cnumber\u003e = new Set();\nrevealedMsgIndices.add(3);\nconst revealedMsgs: Map\u003cnumber, Uint8Array\u003e = new Map();\nconst unrevealedMsgs2: Map\u003cnumber, Uint8Array\u003e = new Map();\nfor (let i = 0; i \u003c messageCount2; i++) {\n  if (revealedMsgIndices.has(i)) {\n    revealedMsgs.set(i, messages2[i]);\n  } else {\n    unrevealedMsgs2.set(i, messages2[i]);\n  }\n}\nconst statement2 = Statement.bbsSignatureProverConstantTime(params2, revealedMsgs, true);\n\n// Collect all the statements\nconst statements = new Statements();\nconst sId1 = statements.add(statement1);\nconst sId2 = statements.add(statement2);\n```\n\nThe prover has 2 prove that both credentials contain the same SSN which is same as saying for the 1st signature (1st `Statement`), \nattribute at index 0 is equal to 2nd signature's (2nd `Statement`) attribute index 5. This requires the use of a `MetaStatement` to \nexpress this condition, specifically `MetaStatement.witnessEquality` which takes the `WitnessRef` for each witness that needs to be \nproven equal. `WitnessRef` for SSN in 1st signature is (0, 0) and in 2nd signature is (1, 5). Create a `WitnessEqualityMetaStatement` to express that.\n\n```ts\n// For proving equality of SSN, messages1[0] == messages2[5], specify using MetaStatement\nconst witnessEq = new WitnessEqualityMetaStatement();\nwitnessEq.addWitnessRef(0, 0);\nwitnessEq.addWitnessRef(1, 5);\nconst ms = MetaStatement.witnessEquality(witnessEq);\n\nconst metaStatements = new MetaStatements();\nmetaStatements.add(ms);\n```\n\nIncase equality of additional attribute also needs to be proven say email, then `WitnessEqualityMetaStatement` needs to be created \nfor the `WitnessRef` of email in both signatures.\n\n```ts\n// For proving equality of email, messages1[3] == messages2[1], specify using MetaStatement\nconst witnessEq2 = new WitnessEqualityMetaStatement();\nwitnessEq2.addWitnessRef(sId1, 3);\nwitnessEq2.addWitnessRef(sId2, 1);\nconst ms2 = MetaStatement.witnessEquality(witnessEq2);\n\nmetaStatements.add(ms2);\n```\n\nSimilar to before, once it has been established what needs to be proven, `ProofSpec` needs to be created with all `Statements`s and `MetaStatement`s.\n```ts\nconst proofSpec = new ProofSpec(statements, metaStatements);\n```\n\nThe prover creates the witnesses with both signatures and messages that he is hiding from the verifier\n\n```ts\n// Using the messages and signature from 1st signer\nconst unrevealedMsgs1 = new Map(messages1.map((m, i) =\u003e [i, m]));\nconst witness1 = Witness.bbsSignatureConstantTime(sig1, unrevealedMsgs1, true);\n\n// Using the messages and signature from 2nd signer\nconst witness2 = Witness.bbsSignatureConstantTime(sig2, unrevealedMsgs2, true);\n\nconst witnesses = new Witnesses();\nwitnesses.add(witness1);\nwitnesses.add(witness2);\n\nconst proof = CompositeProof.generate(proofSpec, witnesses);\n```\n\nVerifier verifies the proof.\n\n```ts\nconsole.assert(proof.verify(proofSpec).verified);\n```\n\n##### BBS signature together with accumulator membership\n\nSay a prover has a credential where one of the attribute is added to an accumulator. The prover wants to prove that his attribute is a \nmember of the accumulator without revealing the attribute itself. Say the attributes are SSN, first name, last name, email and \nuser-id and the prover wants to prove that the user-id is present in the accumulator without revealing it to the verifier.\n\n```ts\n// The attributes, [SSN, first name, last name, email, user-id]\nconst messages: Uint8Array[] = [...];\n```\n\nBecause the attributes for accumulator and BBS signatures are encoded differently, attributes are pre-encoded.\n\n```ts\n// Encode messages for signing as well as adding to the accumulator\nconst encodedMessages = [];\nfor (let i = 0; i \u003c messageCount; i++) {\n  if (i === messageCount-1) {\n    // Last one, i.e. user id is added to the accumulator so encode accordingly\n    encodedMessages.push(Accumulator.encodeBytesAsAccumulatorMember(messages[i]));\n  } else {\n    encodedMessages.push(Signature.encodeMessageForSigning(messages[i]));\n  }\n}\n```\n\nBoth signer and accumulator manager will have public params and their secret keys \n\n```ts\nconst sigParams = BBSSignatureParams.generate(5, label);\n\n// Signers keys\nconst sigSk: BBSPlusSecretKey = ...;\nconst sigPk: BBSPublicKey = ...;\n\n// Accumulator manager's params, keys and state\nconst accumParams = PositiveAccumulator.generateParams(stringToBytes('Accumulator params'));\nconst accumKeypair = PositiveAccumulator.generateKeypair(accumParams);\nconst accumulator = PositiveAccumulator.initialize(accumParams);\nconst state = new InMemoryState();\n```\n\nSigner signs the credential and accumulator manager adds the attribute to the credential and sends the witness to the prover\n```ts\n// Signer signs the message\nconst sig: BBSSignature = ...;\n\n// user-id is at index 4 is the message list\nconst userIdIdx = 4;\nawait accumulator.add(encodedMessages[userIdIdx], accumKeypair.secret_key, state);\nconst accumWitness = await accumulator.membershipWitness(encodedMessages[userIdIdx], accumKeypair.secret_key, state)\n```\n\nTo prove accumulator membership in zero-knowledge, the prover and verifier agree on set of public parameters called the `ProvingKey`. \nThis is not specific to the accumulator and can be reused for any number of accumulators. Also a prover might use different \nproving keys when interacting with different verifiers. Its recommended generating the proving key deterministically by passing a label.\n\n```ts\nconst provingKey = Accumulator.generateMembershipProvingKey(stringToBytes('Our proving key'));\n```\n\nThe prover needs to prove 2 `Statement`s, knowledge of BBS signature and knowledge of accumulator member and corresponding witness.\n\n```ts\nconst statement1 = Statement.bbsSignatureProverConstantTime(sigParams, revealedMsgs, false);\nconst statement2 = Statement.accumulatorMembership(accumParams, accumKeypair.public_key, provingKey, accumulator.accumulated);\nconst statements = new Statements();\nstatements.add(statement1);\nstatements.add(statement2);\n```\n\nThe prover also needs to prove that the accumulator member is same as the credential attribute at index 4, the user id. \nThe `WitnessRef` of the accumulator member is (1, 0) as index of membership `Statement` is 1 and index of member is always 0.\n\n```ts\n// The last message in the signature is same as the accumulator member\nconst witnessEq = new WitnessEqualityMetaStatement();\n// Witness ref for last message in the signature\nwitnessEq.addWitnessRef(0, userIdIdx);\n// Witness ref for accumulator member\nwitnessEq.addWitnessRef(1, 0);\nconst ms = MetaStatement.witnessEquality(witnessEq);\n\nconst metaStatements = new MetaStatements();\nmetaStatements.add(ms);\n\nconst proofSpec = new ProofSpec(statements, metaStatements);\n```\n\nThe prover creates `Witness`es for all statements and then creates the proof. The `Witness` for `Statement.accumulatorMembership` contains\nthe member and the accumulator witness. \n\n```ts\nconst witness1 = Witness.bbsSignatureConstantTime(sig, unrevealedMsgs, false);\nconst witness2 = Witness.accumulatorMembership(encodedMessages[userIdIdx], accumWitness);\nconst witnesses = new Witnesses();\nwitnesses.add(witness1);\nwitnesses.add(witness2);\n\nconst proof = CompositeProof.generate(proofSpec, witnesses);\n```\n\n##### Getting a blind signature (Example applies to BBS+)\nDisclaimer: With BBS, there is no blinding (the commitment is computationally hiding, though it can be made perfectly hiding by adding a dummy attribute). However, it can be easily achieved with BBS+.\nA complete example is shown in this [test](tests/composite-proofs/blind-signature.spec.ts).\n\nA signature is blind when the signer is not aware of the message (or a part of the message) that he is signing, thus the signer is blind.\nBlind signature in credential is used when the holder does not want the signer to learn some attribute, eg. one of the credential \nattribute is a secret key and the holder does not want the signer to learn the secret key. Here the user creates a commitment to \nthe \"blinded\", i.e. hidden attributes to convince the signer that he is only hiding the certain attribute(s). Eg if a credential has 5 \nattributes secret1, name, secret2, email, city and the user wants to hide secret1 and secret2 from the signer, the signer wants to \nbe sure that the user is indeed hiding attributes at index 0 and 2, not others. The prover uses the composite proof system to \nprove that he knows that the commitment contains those 2 attributes\n\n```ts\n// Messages are secret1, name, secret2, email, city\n\n// Signature params for 5 attributes\nconst sigParams = BBSPlusSignatureParamsG1.generate(5, label);\n\n// Prepare messages that will be blinded (hidden) and known to signer\nconst blindedMessages = new Map();\n\n// User wants to hide messages at indices 0 and 2 from signer\nconst blindedIndices: number[] = [];\nblindedIndices.push(0);\nblindedMessages.set(0, stringToBytes('my-secret'));\nblindedIndices.push(2);\nblindedMessages.set(2, stringToBytes('my-another-secret'));\n```\n\nThe signature requester, prover in this case, creates a blind signature request. In addition to the request, it also returns \nrandomness `blinding` that goes into the commitment. This randomness is later used\n\n```ts\nimport { BBSPlusBlindSignatureG1 } from '@docknetwork/crypto-wasm-ts';\n\n// Blind signature request will contain a commitment, \nconst [blinding, request] = BBSPlusBlindSignatureG1.generateRequest(blindedMessages, params, true);\n```\n\nThe proof needs to be over only 1 `Statement`, the statement proving knowledge of the committed attributes in the commitment. \nTo create the commitment, a commitment key (public values) needs to be created from the signature params\n\n```ts\n// Take parts of the sig params corresponding to the blinded messages and create the commitment key commKey\nconst commKey = params.getParamsForIndices(request.blindedIndices);\nconst statement1 = Statement.pedersenCommitmentG1(bases, request.commitment);\n\nconst statements = new Statements();\nstatements.add(statement1);\n\nconst proofSpec = new ProofSpec(statements, new MetaStatements());\n```\n\nNow the prover creates witness for the commitment `Statement` using the randomness and the hidden attributes.\n```ts\nimport { getBBSWitnessForBlindSigRequest } from '@docknetwork/crypto-wasm-ts';\nconst witness1 = getBBSWitnessForBlindSigRequest(blindedMessages)\nconst witnesses = new Witnesses();\nwitnesses.add(witness1);\n\nconst proof = CompositeProof.generate(proofSpec, witnesses);\n```\n\nSigner now verifies the proof. Note that the signer independently creates the `ProofSpec` as he knows which attributes are being \nhidden from him. If the proof is correct, signer creates a blind signature using the known attributes and the commitment \nand sends to the prover.\n\n```ts\nconsole.assert(proof.verify(proofSpec).verified);\n\n// Signer is convinced that user knows the opening to the commitment\n\n// Signer creates a blind signature with these revealed messages and the commitment.\nrevealedMessages.set(1, stringToBytes('John Smith'));\nrevealedMessages.set(3, stringToBytes('john.smith@emample.com'));\nrevealedMessages.set(4, stringToBytes('New York'));\nconst blindSig = BBSPlusBlindSignatureG1.generate(request.commitment, revealedMessages, sk, params, true);\n```\n\nThe prover can now \"unblind\" the signature meaning he can convert a blind signature into a regular BBS signature \nwhich he can use in proof as shown in examples above\n\n```ts\n// Unbling the signature from the randomness of the commitment.\nconst sig = blindSig.unblind(blinding);\n\n// Combine blinded and revealed messages in an array\nconst messages = Array(blindedMessages.size + revealedMessages.size);\nfor (const [i, m] of blindedMessages.entries()) {\n  messages[i] = m;\n}\nfor (const [i, m] of revealedMessages.entries()) {\n  messages[i] = m;\n}\n\n// Signature can be verified\nconst result = sig.verify(messages, pk, params, true);\nconsole.assert(result.verified);\n```\n\n##### Pseudonyms\n\nA pseudonym is meant to be used as a unique identifier. It can be considered as a public key where the creator of the \npseudonym has the secret key, and it can prove the knowledge of this secret key. A pseudonym can also be bound to multiple \nattributes from multiple credentials. This concept was introduced in [Attribute-based Credentials for Trust](https://link.springer.com/book/10.1007/978-3-319-14439-9). \n\n**Motivation**: Proving knowledge of BBS signatures is unlinkable meaning the verifier cannot link to 2 proofs presented from the same\ncredential (signature). But this might not always be desirable for the verifier and the prover might agree to being linked for any \nproofs that he creates for that particular verifier without revealing any attribute of the credential.  \nA verifier wants to attach a unique identifier to a prover without either learning anything unintended (by prover)\nfrom the prover's signature nor can that unique identifier be used by other verifiers to identify the prover,\neg. a seller (as a verifier) should be able to identify repeat customers (prover) by using a unique identifier, but\nhe should not be able to share that unique identifier with other sellers using their own identifier for that prover.\n\n\nAbove is achieved by making the prover go through a one-time registration process with the verifier where the prover creates \na pseudonym and shares the pseudonym with the verifier. The prover on subsequent interactions share the pseudonym and \nproof of knowledge of the pseudonym's secret key with the verifier. Thus, pseudonyms allow for verifier-local and opt-in linkability.\n\nIn the [test](tests/composite-proofs/pseudonyms.spec.ts), the credential has 4 attributes, SSN, first name, \nlast name and email and during registration, the prover creates many pseudonyms, for different verifiers, some are bound to attributes \nand some not. See the test for more details.\n\n##### Social KYC\nA social KYC (Know Your Customer) credential claims that the subject owns certain social media profile like a twitter profile \ncredential claims that a user owns the twitter profile with certain handle. Here the issuer of the credential must verify \nthe user's control of the profile. One way to achieve that is for the user to post a unique issuer supplied challenge string \non his profile, like tweeting it when requesting twitter profile credential. This makes the process 2-step, in step 1 user \nrequests the challenge from issuer and which he tweets and in step 2, he asks the issuer to check the tweet and issue him \na credential. An alternate approach is for the user to post a commitment to some random value on his profile and then request \na credential from the issuer by supplying a proof of knowledge of the opening (committed random value) of the commitment. \nThe issuer is convinced that no one else could know the opening of the commitment which was posted by the user. Note that \nthe user is proving knowledge of the committed value and not revealing it to the issuer because revealing the value will \nallow the issuer to request a similar credential from some another issuer of it the revealed value is leaked then someone \nelse can impersonate the user.  \nThe [test](tests/composite-proofs/social-kyc.spec.ts) shows a complete example.\n\nThe code for composite proof lives [here](./src/composite-proof). See the tests [here](./tests/composite-proofs) for various scenarios.\nFor a more involved demo with multiple BBS signatures being used with accumulator and knowledge of signatures being proved \nbefore requesting blind signatures, see [here](./tests/demo.spec.ts). This test paints a picture where before getting any credential, \na user has to prove possession of a credential and membership in an accumulator (except the 1st credential).\n\n### Verifiable encryption using SAVER\n\nNote: This section assumes you have read some of the previous examples on composite proof.\n\nA complete example as a test is [here](./tests/composite-proofs/saver.spec.ts) \n\nSay a verifier wants the prover to encrypt an attribute from his credential for a 3rd party say a regulator. The verifier should be \nable to check that the prover did encrypt a specific attribute from his credential and not some arbitrary value. Also, the verifier\nshould be able to check that the ciphertext is encrypted for the specific public key. This is achieved through verifiable \nencryption and implemented using a protocol called [SAVER](https://eprint.iacr.org/2019/1270).  \nFor this, the decryptor needs to do a setup where it creates several parameters including encrytion key, decryption key, \nSNARK proving key and verification key, etc. The decryptor then publishes the public parameters. In the snippet below,\n`snarkPk`, `encryptionKey`, `decryptionKey` and `gens` are published.\n\n```ts\nimport { SaverEncryptionGens } from '@docknetwork/crypto-wasm-ts';\n\nconst encGens = SaverEncryptionGens.generate();\nconst [snarkPk, secretKey, encryptionKey, decryptionKey] = SaverDecryptor.setup(encGens);\n```\n\n`SaverDecryptor.setup` above takes an optional parameter `chunkBitSize` which can make the encryption and proving faster (or slower)\nwhile making decryption slower (or faster). Since encryption and proving are done more often, a higher default value of 16 \nis chosen for this parameter. Note that once parameters have been created with a certain value of `chunkBitSize`, the same value\nshould be used while encryption, decryption, proving and verification (as shown below). \n\n#### Encoding for verifiable encryption\n\nFor signers (issuers of credentials), it's important to encode attributes that need to be verifiably encrypted using a reversible \nencoding as the decryption might happen much later than the proof verification and thus the decryptor should be able to independently \nrecover the actual attributes. This situation is different from selective disclosure where the actual attributes are given to the \nverifier who can then encode the attributes before verifying the proof. One such pair of functions are `Signature.reversibleEncodeStringForSigning`\nand `Signature.reversibleDecodeStringForSigning` and you can see its use in the above-mentioned test. Theese conversions are abstracted in this [Encoders](./src/bbs-plus/encoder.ts) class and you can see the usage \nin [these tests](tests/composite-proofs/msg-js-obj/saver.spec.ts) of the  `Encoder` initialized [here](tests/composite-proofs/msg-js-obj/data-and-encoder.ts). \n\nFor creating the proof of knowledge of the BBS signature and verifiably encrypting an attribute, the prover creates the following 2 statements.\n\n```ts\nimport { SaverChunkedCommitmentKey } from '@docknetwork/crypto-wasm-ts';\n\n// Signer's parameters\nlet sigParams: BBSSignatureParams, sigPk: BBSPublicKey, sig: BBSSignature;\n// Signed messages\nlet messages: Uint8Array[];\n...\n...\n// The value used by decryptor during setup\nlet chunkBitSize = ...;\n...\n...\n// The following is either created by the verifier and is shared with the prover or created by the prover using a public bytes \n// as argument to `SaverChunkedCommitmentKey.generate`  \nconst gens = SaverChunkedCommitmentKey.generate(\u003csome public bytes\u003e);\n...\n...\n// Uncompressed form of `gens` created above\nconst commKey = gens.decompress();\n// Uncompressed form of other parameters created by decryptor\nconst saverEncGens = encGens.decompress();\nconst saverEk = encryptionKey.decompress();\nconst snarkProvingKey = snarkPk.decompress();\n...\n...\nconst statement1 = Statement.bbsSignatureProverConstantTime(sigParams, revealedMsgs, false);\nconst statement2 = Statement.saverProver(saverEncGens, commKey, saverEk, snarkProvingKey, chunkBitSize);\n\nconst proverStatements = new Statements();\nproverStatements.add(statement1);\nproverStatements.add(statement2);\n```\n\n`statement1` is the for proving knowledge of a BBS signature as seen in previous examples. `statement2` is for proving the encryption of message from a \nBBS signature. Some things to note about this statement.\n\n- The statement is created using `Statement.saverProver` because it is being created by a prover. A verifier would have\n  used `Statement.saverVerifier` to create it and one of the arguments would be different (shown below).\n- 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`\n- `saverEk` is the encryption key created by the decryptor during `setup` but is uncompressed.\n- `snarkProvingKey` is the proving key created by the decryptor during `setup` but is uncompressed.\n\nThe prover then establishes the equality between the message in the BBS signature and the message being encrypted by using\n`WitnessEqualityMetaStatement` as below. `encMsgIdx` is the index of the message being encrypted in the array of signed \nmessages under BBS, `messages`. For the second statement, there is only 1 witness, thus the index 0.\n\n```ts\nconst witnessEq = new WitnessEqualityMetaStatement();\nwitnessEq.addWitnessRef(0, encMsgIdx);\nwitnessEq.addWitnessRef(1, 0);\nconst metaStatements = new MetaStatements();\nmetaStatements.add(MetaStatement.witnessEquality(witnessEq));\n```\n\nThe prover then creates witness for both statements. The message `messages[encMsgIdx]` passed to `Witness.saver` is the\nmessage being encrypted. `unrevealedMsgs` passed to `Witness.bbsSignatureConstantTime` is created from `messages` and consists of\nmessages not being revealed to the verifier.\n\n```ts\nconst witness1 = Witness.bbsSignatureConstantTime(sig, unrevealedMsgs, false);\nconst witness2 = Witness.saver(messages[encMsgIdx]);\nconst witnesses = new Witnesses();\nwitnesses.add(witness1);\nwitnesses.add(witness2);\n```\n\nThe prover then creates a proof specification using `QuasiProofSpec`. This is different from `ProofSpec` object seen in \nprevious examples as it does not call WASM to get a proof specification object and thus is more efficient.  \nNow prover creates the proof using `CompositeProof.generateUsingQuasiProofSpec`\n\n```ts\nimport { QuasiProofSpec } from '@docknetwork/crypto-wasm-ts';\n\nconst proverProofSpec = new QuasiProofSpec(proverStatements, metaStatements);\nconst proof = CompositeProof.generateUsingQuasiProofSpec(proverProofSpec, witnesses);\n```\n\nSimilarly, the verifier also creates 2 statements and the same meta statement to verify the proof.  \n\n```ts\n// Get the uncompressed verifying key from the compressed proving key.\nconst snarkVerifyingKey = snarkPk.getVerifyingKeyUncompressed();\n\nconst statement1 = Statement.bbsSignatureProverConstantTime(sigParams, revealedMsgs, false);\nconst statement2 = Statement.saverVerifier(saverEncGens, commKey, saverEk, snarkVerifyingKey, chunkBitSize);\nconst verifierStatements = new Statements();\nverifierStatements.add(statement1);\nverifierStatements.add(statement2);\n\nconst witnessEq = new WitnessEqualityMetaStatement();\nwitnessEq.addWitnessRef(0, encMsgIdx);\nwitnessEq.addWitnessRef(1, 0);\nconst metaStatements = new MetaStatements();\nmetaStatements.add(MetaStatement.witnessEquality(witnessEq));\n```\n\nThe above has a few differences from the prover's statements:\n\n- Instead of using `Statement.saverProver`, verifier uses `Statement.saverVerifier`.\n- Instead of proving key, verifier uses verifying key for the snark.\n\nThe verifier now creates the proof specification and verifies the proof.\n\n```ts\nconst verifierProofSpec = new QuasiProofSpec(verifierStatements, metaStatements);\n// result.verified should be true for the proof to be valid.\nconst result = proof.verifyUsingQuasiProofSpec(verifierProofSpec);\n```\n\nThe verifier will now extract the ciphertext from the proof so that it can share that with the decryptor later. Here `1` \npassed to `proof.getSaverCiphertext` is the index (0-based) of the statement in the list of statements being proven and the \nstatement from verifiable encryption was the 2nd one.\n\n```ts\nconst ciphertext = proof.getSaverCiphertext(1);\n```\n\nThe decryptor can decrypt the ciphertext to get message that was encrypted.\n\n```ts\nconst saverDk = decryptionKey.decompress();\n// decrypted.message is the message\nconst decrypted = SaverDecryptor.decryptCiphertext(ciphertext, saverSk, saverDk, snarkVerifyingKey, chunkBitSize);\n```\n\nSometimes the verifier might want to know the decrypted message but might not trust that the decryptor to correctly tell \nhim the decrypted message. In this it can verify the decryption done by the decryptor as below\n\n```ts\n// result.verified should be true\nconst result = ciphertext.verifyDecryption(decrypted, saverDk, snarkVerifyingKey, saverEncGens, chunkBitSize);\n```\n\n### Bound check (range proof)\n\nNote: This section assumes you have read some of the previous examples on composite proof.\n\nA complete example as a test is [here](./tests/composite-proofs/bound-check.spec.ts)\n\nAllows a verifier to check that some attribute of the credential satisfies given bounds `min` and `max`, i.e. `min \u003c= message \u003c max` \nwithout learning the attribute itself. Both `min` and `max` are positive integers. This can be implemented using different protocols, \n- LegoGroth16, a protocol described in the SNARK framework [Legosnark](https://eprint.iacr.org/2019/142) in appendix H.2. Requires a trusted setup, which in practice is done by the verifier. \n- Bulletproofs++, a transparent (no trusted setup required) range proof protocol.\n- Set-membership check based range proof which require a trusted setup but offer 2 variations - one with keyed verification which has the most optimal execution \n  and the other that performs similar to LegoGroth16 for proving but worse in verification.\n\nThe above mentioned test uses all these variations. \n\n#### Encoding for negative or decimal numbers\n\nTo work with negative integers or decimal numbers, they must be converted to positive integers first and this conversion must happen before these are signed. \nWhen working with negative integers, add the absolute value of the smallest (negative) integer to all values including bounds. Eg, if the smallest negative \nnumber 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 \ncheck 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 \nnumbers, 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 \nmultiplied by 1000.  The [test](./tests/composite-proofs/bound-check.spec.ts) mentioned above shows these scenarios.  \nThe conversions defined in the above tests are abstracted in this [Encoders](./src/bbs-plus/encoder.ts) class and you can see the usage \nin [these tests](tests/composite-proofs/msg-js-obj/bound-check.spec.ts) of the  `Encoder` initialized [here](tests/composite-proofs/msg-js-obj/data-and-encoder.ts).  \n\n\nFor this, the verifier needs to first create the setup parameters which he then shares with the prover. Note that the \nverifier does not have to create them each time a proof needs to be verified, but only once and publish them somewhere \nsuch that all provers interacting with the proof can use them.  \nIn the following snippet, the verifier asks to prove that a certain message satisfies the lower and upper bounds `min` and `max`,\ni.e. `min \u003c= message \u003c max`. Note than both bounds are positive integers and lower bound is inclusive but upper bound is not.\nTo change from exclusive to inclusive bounds and vice-versa, add or subtract 1 from bounds. The snippet shows LegoGroth16.\n\n```ts\nimport { BoundCheckSnarkSetup } from '@docknetwork/crypto-wasm-ts';\n\nconst provingKey = BoundCheckSnarkSetup();\n```\n\nFor 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.\n\n```ts\n\n// Signer's parameters\nlet sigParams: BBSSignature, pk: BBSPublicKey, sig: BBSSignature;\n// Signed messages - already encoded\nlet messages: Uint8Array[];\n...\n// define the min and max bounds\nlet min: number = ...;\nlet max: number = ...;\n...\n// Decompress the proving key \nconst snarkProvingKey = provingKey.decompress();\nconst statement1 = Statement.bbsSignatureProverConstantTime(sigParams, revealedMsgs, false);\nconst statement2 = Statement.boundCheckLegoProver(min, max, snarkProvingKey);\nconst proverStatements = new Statements();\nproverStatements.add(statement1);\nproverStatements.add(statement2);\n```\n\n`statement1` is the for proving knowledge of a BBS signature as seen in previous examples. `statement2` is for proving the bounds of a message from the BBS signature. \n\nSome things to note about this statement:\n\n- The statement is created using `Statement.boundCheckLegoProver` because it is being created by a prover. A verifier would have\n  used `Statement.boundCheckLegoVerifier` to create it and one of the arguments would be different (shown below).\n- The argument `snarkProvingKey` is the public parameter created by the verifier. However, before they are passed to `Statement.boundCheckLegoProver`, 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.boundCheckLegoProverFromCompressedParams`\n\nThe prover then establishes the equality between the message in the BBS signature and the bounded message by using\n`WitnessEqualityMetaStatement` as below. `msgIdx` is the index of the bounded message in the array of signed messages \nunder BBS, `messages`. For the second statement, there is only 1 witness, thus the index 0.\n\n```ts\nimport { WitnessEqualityMetaStatement, MetaStatement, MetaStatements } from '@docknetwork/crypto-wasm-ts';\nconst witnessEq = new WitnessEqualityMetaStatement();\nconst msgIdx = 3;  // the index of the SSN number\nwitnessEq.addWitnessRef(0, msgIdx);\nwitnessEq.addWitnessRef(1, 0);\nconst metaStatements = new MetaStatements();\nmetaStatements.add(MetaStatement.witnessEquality(witnessEq));\n```\n\nThe prover then creates witness for both statements. The message `messages[msgIdx]` passed to `Witness.boundCheckLegoGroth16` is the\nbounded message. `unrevealedMsgs` passed to `Witness.bbsSignatureConstantTime` is created from `messages` and consists of\nmessages not being revealed to the verifier.\n\n```ts\nconst witness1 = Witness.bbsSignatureConstantTime(sig, unrevealedMsgs, false);\nconst witness2 = Witness.boundCheckLegoGroth16(messages[msgIdx]);\nconst witnesses = new Witnesses();\nwitnesses.add(witness1);\nwitnesses.add(witness2);\n```\n\nThe prover then creates a proof specification using `QuasiProofSpec`. This is different from `ProofSpec` object seen in\nprevious examples as it does not call WASM to get a proof specification object and thus is more efficient.  \nNow prover creates the proof using `CompositeProof.generateUsingQuasiProofSpec`\n\n```ts\nimport { QuasiProofSpec } from '@docknetwork/crypto-wasm-ts';\n\nconst proverProofSpec = new QuasiProofSpec(proverStatements, metaStatements);\nconst proof = CompositeProof.generateUsingQuasiProofSpec(proverProofSpec, witnesses);\n```\n\nSimilarly, the verifier also creates 2 statements and the same meta statement to verify the proof.\n\n```ts\n// Get the uncompressed verifying key from the compressed proving key.\nconst snarkVerifyingKey = provingKey.getVerifyingKeyUncompressed();\n\nconst statement1 = Statement.bbsSignatureVerifierConstantTime(sigParams, sigPk, revealedMsgs, false);\nconst statement2 = Statement.boundCheckLegoVerifier(min, max, snarkVerifyingKey);\nconst verifierStatements = new Statements();\nverifierStatements.add(statement1);\nverifierStatements.add(statement2);\n\nconst witnessEq = new WitnessEqualityMetaStatement();\nwitnessEq.addWitnessRef(0, msgIdx);\nwitnessEq.addWitnessRef(1, 0);\nconst metaStatements = new MetaStatements();\nmetaStatements.add(MetaStatement.witnessEquality(witnessEq));\n```\n\nThe above has a few differences from the prover's statements:\n\n- Instead of using `Statement.boundCheckLegoProver`, verifier uses `Statement.boundCheckLegoVerifier`.\n- Instead of proving key, verifier uses verifying key for the snark.\n\nThe verifier now creates the proof specification and verifies the proof.\n\n```ts\nconst verifierProofSpec = new QuasiProofSpec(verifierStatements, metaStatements);\n// result.verified should be true for the proof to be valid.\nconst result = proof.verifyUsingQuasiProofSpec(verifierProofSpec);\n```\n\n### Optimization\n\nYou might notice some public parameters are huge and also the statements involving them take noticeable time to create. Eg,\n`snarkProvingKey`, `snarkVerifyingKey`, `saverEk` are huge and thus creating `Statement.saverProver`, `Statement.saverVerifier`, \n`Statement.boundCheckLegoProver` and `Statement.boundCheckLegoVerifier` take some time to create. This becomes a bigger problem \nwhen several messages need to be encrypted for the same decryptor or bounds over several messages need to be proved.  \nTo solve this, the public parameters don't need to be passed directly to the `Statement`s. They can be wrapped in a `SetupParam`\nand then a reference to them is passed as an argument in place of the parameter itself to the `Statement`. See the snippet \nbelow for creating 2 statements for verifiable encryption for the same setup parameters:\n\n```ts\nimport { SetupParam } from '@docknetwork/crypto-wasm-ts';\n\n// Prover creates an array of `SetupParam`s\nconst proverSetupParams = [];\nproverSetupParams.push(SetupParam.saverEncryptionGensUncompressed(saverEncGens));\nproverSetupParams.push(SetupParam.saverCommitmentGensUncompressed(commKey));\nproverSetupParams.push(SetupParam.saverEncryptionKeyUncompressed(saverEk));\nproverSetupParams.push(SetupParam.saverProvingKeyUncompressed(snarkProvingKey));\n\n// Passing reference to parameters as array indices from `proverSetupParams`\nconst statement3 = Statement.saverProverFromSetupParamRefs(0, 1, 2, 3, chunkBitSize);\nconst statement4 = Statement.saverProverFromSetupParamRefs(0, 1, 2, 3, chunkBitSize);\n```\n\nNote the use of `Statement.saverProverFromSetupParamRefs` rather than `Statement.saverProver`. The arguments:\n\n- 0 for the encryption generators which are at index 0 in `proverSetupParams`\n- 1 for the commitment generators which are at index 1 in `proverSetupParams`\n- 2 for the encryption key which is at index 2 in `proverSetupParams`\n- 3 for the proving key which is at index 3 in `proverSetupParams`\n\nNow the prover creates the proof specification by passing `SetupParam`s array as well.\n\n```ts\nconst proverStatements = new Statements();\n...\nproverStatements.add(statement3);\nproverStatements.add(statement4);\n...\n...\n...\nconst proverProofSpec = new QuasiProofSpec(proverStatements, metaStatements, proverSetupParams);\nconst proof = CompositeProof.generateUsingQuasiProofSpec(proverProofSpec, witnesses);\n```\n\nSimilarly, the verifier can create his own `SetupParam`s array for his proof specification and then proof\n\n```ts\nconst verifierSetupParams = [];\nverifierSetupParams.push(SetupParam.saverEncryptionGensUncompressed(saverEncGens));\nverifierSetupParams.push(SetupParam.saverCommitmentGensUncompressed(commKey));\nverifierSetupParams.push(SetupParam.saverEncryptionKeyUncompressed(saverEk));\nverifierSetupParams.push(SetupParam.saverVerifyingKeyUncompressed(snarkVerifyingKey));\n\nconst statement5 = Statement.saverVerifierFromSetupParamRefs(0, 1, 2, 3, chunkBitSize);\nconst statement6 = Statement.saverVerifierFromSetupParamRefs(0, 1, 2, 3, chunkBitSize);\n\nconst verifierStatements = new Statements();\n...\n...\nverifierStatements.add(statement5);\nverifierStatements.add(statement6);\n\nconst verifierProofSpec = new QuasiProofSpec(verifierStatements, metaStatements, verifierSetupParams);\nconst result = proof.verifyUsingQuasiProofSpec(verifierProofSpec);\n```\n\nFor a complete example, see [these tests](./tests/composite-proofs/saver.spec.ts)\n\nSimilarly, for bound checks, use `Statement.boundCheckLegoProverFromSetupParamRefs` and `Statement.boundCheckVerifierFromSetupParamRefs`.  \nFor complete example, see [these tests](./tests/composite-proofs/bound-check.spec.ts)\n\n### Working with messages as JS objects\n\nThe above interfaces have been found to be a bit difficult to work with when signing messages/credentials that are represented as JS objects like\n\n```json\n{\n  \"fname\": \"John\",\n  \"lname\": \"Smith\",\n  \"sensitive\": {\n    \"secret\": \"my-secret-that-wont-tell-anyone\",\n    \"email\": \"john.smith@example.com\",\n    \"SSN\": \"123-456789-0\",\n    \"user-id\": \"user:123-xyz-#\"\n  },\n  \"location\": {\n    \"country\": \"USA\",\n    \"city\": \"New York\"\n  },\n  \"timeOfBirth\": 1662010849619,\n  \"physical\": {\n    \"height\": 181.5,\n    \"weight\": 210,\n    \"BMI\": 23.25\n  },\n  \"score\": -13.5\n}\n```\n\n[Here](./src/sign-verify-js-objs.ts) are some utilities to make this task a bit easier. The idea is to flatten the JSON, sort the keys alphabetically \nto have a list with deterministic order and then use the [encoder](./src/encoder.ts) to encode each value as a field element (a number between 0 and another large number).  \nThe encoder can be configured to use different encoding functions for different keys to convert values from different types \nlike string, positive or negative integers or decimal numbers to field elements.  \n[The tests here](tests/composite-proofs/msg-js-obj) contain plenty of examples.\n\n\n### Writing predicates in Circom\n\nSimple predicates like a range proof or equality of messages in zero knowledge are already hardcoded in the library but we \ncannot imagine all the possible predicates different use-cases can require. We expect developers to write these predicates \nin a programming language that we can then use to create zero-knowledge proofs. We currently support [Circom](https://docs.circom.io/), version 2.\nThe predicates can be written as Circom programs and then compiled for curve BLS12-381. The generated R1CS and WASM can then be feed \nto the composite proof system to generate a zero knowledge proof of the predicate.\n\nThe workflow is this:\n\n1. Express the predicates/arbitrary computation as a Circom program.\n2. Compile the above program to get the constraints (R1CS file) and witness generator (WASM file, takes input wires and calculates all the intermediate wires).\n3. Use the constraints from step 2 to generate SNARK proving and verification key of LegoGroth16.\n4. Use the R1CS and WASM files from step 2 and proving key from step 3 to create a LegoGroth16 proof.\n5. Use the verification key from step 3 to verify the LegoGroth16 proof.\n\nThe steps 1-3 are done by the verifier and the result of these steps, i.e. the program (`.circom` file), R1CS (`.r1cs` file), \nWASM (`.wasm` file), proving and verification key are shared with any potential prover (published or shared P2P). Step 4 is \ndone by the prover and step 5 again by the verifier. Since R1CS and WASM files are harder to inspect that Circom programs, \nto guard against a verifier tricking the prover to prove unintended predicates (and thus reveal more information than required), \na prover can take the Circom program and generate the R1CS and WASM files himself (do step 2 as well).\n\nSee some of the following tests for Circom usage:\n\n1. [The yearly income, calculate from monthly payslips is less/greater than certain amount.](./tests/composite-proofs/msg-js-obj/r1cs/yearly-income.spec.ts).\n2. [The sum of assets is greater than the sum of liabilities where are assets and liabilities are calculated from several credentials.](./tests/composite-proofs/msg-js-obj/r1cs/assets-liabilities.spec.ts)\n3. [The blood group is not AB-](./tests/composite-proofs/msg-js-obj/r1cs/blood-group.spec.ts)\n4. [The grade is either A+, A, B+, B or C but nothing else.](./tests/composite-proofs/msg-js-obj/r1cs/grade.spec.ts)\n5. [Either vaccinated less than 30 days ago OR last checked negative less than 2 days ago](./tests/composite-proofs/msg-js-obj/r1cs/vaccination.spec.ts)\n6. [All receipts (used as credentials) have different receipt (credential) ids](./tests/composite-proofs/msg-js-obj/r1cs/all_receipts_different.spec.ts). This test shows using multiple circuits in a single proof.\n7. [Certain attribute is the preimage of an MiMC hash](./tests/composite-proofs/msg-js-obj/r1cs/mimc-hash.spec.ts)\n\nThe Circom programs and corresponding R1CS and WASM files for the tests are [here](./tests/circom).\n\n### Anonymous credentials\n\nThe composite proof system is used to implement anonymous credentials. See [here](src/anonymous-credentials/) for details.\n\n[Slides](https://www.slideshare.net/SSIMeetup/anonymous-credentials-with-range-proofs-verifiable-encryption-zksnarks-circom-support-and-blinded-issuance-lovesh-harchandani) and [video](https://www.youtube.com/watch?v=e_E_6Fx5dro) for a presentation given at SSI meetup. Most of the presentation goes over the code, mostly anonymous credentials from this library.  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdocknetwork%2Fcrypto-wasm-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdocknetwork%2Fcrypto-wasm-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdocknetwork%2Fcrypto-wasm-ts/lists"}