Skip to content

Implementation of PLUME: nullifier friendly signature scheme on ECDSA

License

Notifications You must be signed in to change notification settings

signorecello/zk-nullifier-sig

 
 

Repository files navigation

PLUME: Verifiably Deterministic Signatures on ECDSA

This repository provides libraries for the construction of deterministic nullifiers on Ethereum keys, ERC 7524. We call them Privately Linked Unique Message Entities (or PLUMEs). PLUMEs enable zk voting, anonymous proof of solvency, and anonymous message board moderation to be possible with Ethereum keys directly, and so we think it is a critical primitive to push forward blockchain adoption. To understand how this primitive works and the reason for design decisions, we recommend checking out our blog post.

We hope that wallets integrate the javascript, rust, or C repositories for both software and hardware signature generation, and dapps integrate the zk proof in the circuits/ directory.

Installation

To install our npm package for JS/TS, do

yarn add plume-sig

To install our Cargo package for Rust, do

cargo add plume_rustcrypto

Docs and usage guides are linked to from the packages.

Contributions and Grants

If you would like to get a grant to create PLUME applications or improve the library, we have grants available from Ethereum Foundation PSE, Gitcoin Grants, and Aztec Foundation, and would give grants for any PRs to the repository! There are projects ideas both below in the README, as well as bountied every issue in Github has a $50 bounty on it. Feel free to pick one up, and dm us on Twitter/Telegram (@yush_g) for guidance and help, or join the discussion in the public channel in the PSE Discord for progress updates and community questions! This work was generously funded and supported by 0xPARC, Gitcoin donors, EF PSE, Distributed Lab, and Aztec Labs, and exists only due to the valuable work by contributors to this Github such as yush_g, Oren Yomtov, Richard Liu, Blake M Scurr, Piotr Roslaniec, Vu Voth, Weijie Koh, Vivek Bhupatiraju, Yevhenii Sekhin, Nikita Masych, Sergey Kaunov, José Pedro Sousa, and Michael Connor, who directly contributed to the code. Thanks to Poseidon Labs for a V2 proposal and Weiking Chen for his filed issues, and our auditors (0xbok), as well as all of the folks acknowledged in the research paper and blog post.

If you'd like to contribute, we offer $50 bounties in Eth/DAI for resolving any of the bugs in our issues! Each of them is quite small. That includes #28, #24, #14, and #13.

ZK Implementation

Plain implementations

  • rust-k256: Rust, using the k256 library
  • rust-arkworks: Rust, using arkworks
  • javascript (wasm): JavaScript via wasm-bindgen
  • typescript: Native typescript implementation

Wallet Implementations

  • Mina: Uses it for nullifiers in their code here and here. They use Poseidon for the hash function instead, which makes it slower to generate in hardware wallets, but faster to prove. Their docs for this scheme are here.
  • Taho: We have an open PR that we are waiting on them to merge!
  • Rabby: We have an open PR that we are waiting on them to merge!
  • Metamask: We have an open PR set (rpc, api, core) that we are waiting on them to merge! Snaps dropped support for secret key access so a Metamask Snap is no longer a tenable path, although we did have a snap as well.
  • Aztec: Noir implementation finished here, still pending audit.
  • Ledger: This app compiles and generates PLUME signatures on embedded hardware and works on Ledgers. Test via the Ledger app builder.
  • ZK Snap: The "holy grail of private voting", according to Ameen, co-author of the private voting report. Uses PLUME as a core component of an end-to-end private voting system.

Audits

The circom implementation was audited by PSE Security for the rust and javascript-wasm implementations, both V1 and V2, as well as for V1 circuits. We expect the halo2 circuits to be runnable on mobile (once we have audited that code circa ~April and put up a recursive proving infrastructure setup).

The Noir implementation is unaudited, but is runnable on mobile using native apps (ex. React Native). It also works on Android Progressive Web Apps, as Androids give us a little bit more of that precious RAM on the browser.

Usage

You probably want to run the plain versions to calculate the correct inputs. You can then use them to prove using the ZK implementations.

Typescript

Clone this repository and navigate to the typescript directory. Install dependencies:

npm i # or `pnpm i`, `bun i`, etc

The library will be built in typescript/dist, you can import it through npm links, gitpkg, copying it into your node_modules (YOLO!)... whatever floats your boat.

You can then use it in your project:

import { computeAllInputs } from 'plume-sig';

const messageBytes = new Uint8Array([ 104, 101, 108, 108, 111,  32, 110, 111, 105, 114 ]) // bytes for "hello noir"
const privateKey = "signers_private_key;
const { nullifier } = await computeAllInputs(messageBytes, privateKey);

Noir

Once you have your inputs, you can import the noir package into your project. Add the dependency to your Nargo.toml file:

plume = { tag = "main", git = "https://github.com/plume-sig/zk-nullifier-sig", directory = "circuits/noir/plume" }

You can prove your PLUME nullifier is valid like so:

let plume = Plume::new(message, scalar_c, scalar_s, pubkey_bg, nullifier);
plume.plume_v2();

The Noir PLUME implementation is fairly generic, however you need to provide your own hash_to_curve implementation. Currently we're only testing for secp256k1 and bn254.

So for secp256k1 you probably want to cast your values to Secp256k1Fq BigNum, Secp256k1 Curve, Secp256k1Scalar, etc. For example:

// use noir_bigcurve::curves::secp256k1::{Secp256k1, Secp256k1Fq, Secp256k1Scalar};

let c_bn = Secp256k1Fq::from_be_bytes(c);
let scalar_c: Secp256k1Scalar = ScalarField::from_bignum(c_bn);
let s_bn = Secp256k1Fq::from_be_bytes(s);
let scalar_s: Secp256k1Scalar = ScalarField::from_bignum(s_bn);
let pubkey_bg = Secp256k1 {
   x: Secp256k1Fq::from_be_bytes(pub_key_x),
   y: Secp256k1Fq::from_be_bytes(pub_key_y),
   is_infinity: false,
};
let nullifier = Secp256k1 {
   x: Secp256k1Fq::from_be_bytes(nullifier_x),
   y: Secp256k1Fq::from_be_bytes(nullifier_y),
   is_infinity: false,
};

Testing

Circom

Circom uses the great circom-tester library by iden3. Prepare your testing environment by cloning this repository and navigating to the javascript/ directory. Then install dependencies:

pnpm i

If you encounter an error No prebuilt binaries found, try switching to node v18.17.0 (using n, for example) to work around our dependency's build issue.

To run your tests, navigate to the circuits/circom directory and install the dependencies there:

pnpm i

Run the tests:

pnpm run flatten-deps && \
pnpm run test

Be prepared to wait around 20-40 minutes for the tests to complete.

Noir

Noir provides its own testing environment. Install Nargo:

curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash # installs noirup, the nargo installer
noirup # installs nargo

Then navigate to circuits/noir, and run tests:

nargo test

Tests should finish in around 30-60 seconds.

Open Work

We invite contributors to collaborate on this effort. There are great tasks for beginners (the issues), a halo2 intermediate level (circuits), cryptography intermediate level (the v1 improvement to make it v2 compatible below), and on the application layer (building apps that use PLUME).

  • Create a V3
    • Reduce number of arguments to c in V1 via Wei Dai's + Poseidons suggestions (potentially just g^sk, h[m, pk], g^r is fine) that are currently used in the V2, and write a proof in the Algebraic Group Model for the change.
  • [$500 Bounty] Fix stealthdrop circom MVP, the first anonymous airdrop to any Ethereum keys via PLUMEs -- repo and slides.
  • [$500 Bounty] Implement ZK voting via PLUMEs, as described in Poseidons proposal.

Resources

Paper

Thesis [most up to date version]: https://aayushg.com/thesis.pdf
Paper [slightly out of date]: https://eprint.iacr.org/2022/1255

Slides

http://slides.plume.run

Blog Post

blog.aayushg.com/nullifier

This describes the construction as well as explains our choices for the various hash/hash-to-curve functions.

ERC 7524

Official ERC: erc.plume.run

Discussion

ERC 7524 Taho Wallet Integration

Demo

https://nullifier.xyz

Talk

https://www.youtube.com/watch?v=6ajBnMdJGoY

Circom Proof Data

For the V1, See this PR. 6.5 million constraints. Mostly dominated by EC operations, but the hashes are very expensive too.

sha256 ~1.5M. hash_to_curve ~0.5M. a/b^c ~1.5 each (this is the sub circuit for the first 2 verification equations). the remaining 1.5M is probably dominated by calculating g^s and h^s.

For the V2, the sha256 is 0 cost in the circuit, but is added to the verification cost. This takes in-circuit constraints down to 5M and adds the sha to the verification.

Hash to Curve Circom Code and Explainer

https://github.com/geometryresearch/secp256k1_hash_to_curve/ https://geometry.dev/notebook/Hashing-to-the-secp256k1-Elliptic-Curve

Nullifier Calculation Spec

https://hackmd.io/uZQbMHrVSbOHvoI_HrJJlw

Circom Verification Spec

https://hackmd.io/VsojkopuSMuEA4vkYKSB8g?edit

V2 Spec and Discussion

notion.so/mantanetwork/PLUME-Discussion-6f4b7e7cf63e4e33976f6e697bf349ff

This includes some discussion on justifications for the V2 signature calculation, as well as concrete ways to use PLUME proofs + Proof of ECDSA to do nullifiers and voting respectively.

About

Implementation of PLUME: nullifier friendly signature scheme on ECDSA

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 62.8%
  • Noir 14.9%
  • TypeScript 11.9%
  • Circom 9.5%
  • Other 0.9%