Skip to content

Commit

Permalink
Merge branch 'plume-sig:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
RajeshRk18 authored Mar 8, 2024
2 parents bd2e48e + bc1d2f8 commit bae5701
Show file tree
Hide file tree
Showing 18 changed files with 1,022 additions and 768 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PLUME: Verifiably Deterministic Signatures on ECDSA

This repository provides libraries for the construction of deterministic nullifiers on Ethereum keys, [ERC 7524]([https://ethereum-magicians.org/t/erc-7524-plume-signature-in-wallets/15902](https://github.com/ethereum/EIPs/pull/7775)). 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 forwards blockchain adoption. To understand how this primitive works and the reason for design decisions, we recommend checking out [our blog post](https://blog.aayushg.com/posts/plume).
This repository provides libraries for the construction of deterministic nullifiers on Ethereum keys, [ERC 7524]([https://ethereum-magicians.org/t/erc-7524-plume-signature-in-wallets/15902](https://github.com/ethereum/EIPs/pull/7775)). 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](https://blog.aayushg.com/posts/plume).

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.

Expand All @@ -10,7 +10,7 @@ If you would like to get a grant to create PLUME applications or improve the lib

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](https://github.com/plume-sig/zk-nullifier-sig/issues/28), [#24](https://github.com/plume-sig/zk-nullifier-sig/issues/24),
[#14](https://github.com/plume-sig/zk-nullifier-sig/issues/14),and [#13](https://github.com/plume-sig/zk-nullifier-sig/issues/13).
[#14](https://github.com/plume-sig/zk-nullifier-sig/issues/14), and [#13](https://github.com/plume-sig/zk-nullifier-sig/issues/13).

## Implementations

Expand All @@ -20,14 +20,15 @@ If you'd like to contribute, we offer $50 bounties in Eth/DAI for resolving any

### Wallet Implementations

- Mina: Uses it for nullifiers [here](https://github.com/o1-labs/o1js/blob/main/src/lib/nullifier.ts) and [here](https://github.com/o1-labs/o1js/blob/main/src/mina-signer/src/nullifier.ts). We are working with them to be fully ERC compliant!
- Mina: Uses it for nullifiers in their code [here](https://github.com/o1-labs/o1js/blob/main/src/lib/nullifier.ts) and [here](https://github.com/o1-labs/o1js/blob/main/src/mina-signer/src/nullifier.ts). 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](https://docs.minaprotocol.com/zkapps/o1js-reference/classes/Nullifier).
- Taho: We have an [open PR](https://github.com/tahowallet/extension/pull/3638) that we are waiting on them to merge!
- Rabby: We have an [open PR](https://github.com/RabbyHub/Rabby/pull/2047) that we are waiting on them to merge!
- Metamask: We have an open PR set ([rpc](https://github.com/MetaMask/eth-json-rpc-middleware/pull/198
), [api](https://github.com/MetaMask/api-specs/pull/120), [core](https://github.com/MetaMask/metamask-extension/pull/17482)) that we are waiting on them to merge!
- Aztec: WIP, grant out to implement in Noir.
), [api](https://github.com/MetaMask/api-specs/pull/120), [core](https://github.com/MetaMask/metamask-extension/pull/17482)) that we are waiting on them to merge! Snaps [dropped support for secret key access](https://github.com/MetaMask/snaps/issues/1665) so a Metamask Snap is no longer a tenable path, although we did have a snap as well.
- Aztec: WIP, pending implementation in Noir.

### Audits
We have been audited by 0xbok for these three implementations V1 and V2 implementations, as well as for V1 circuits in circom. We expect the halo2 circuits to be runnable on mobile (once we have aduited that code and put up a recursive proving infrastructure setup).
We have been audited by 0xbok for these three implementations V1 and V2 implementations, as well as for V1 circuits in circom. We expect the halo2 circuits to be runnable on mobile (once we have audited that code and put up a recursive proving infrastructure setup).

## Testing the circom circuit

Expand Down
2 changes: 1 addition & 1 deletion circuits/circom/verify_nullifier.circom
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ template a_div_b_pow_c(n, k) {

b_pow_c_inv_y.underflow === 0;

// Calculates a^s * (b^c)-1
// Calculates a * (b^c)-1
component final_result = Secp256k1AddUnequal(n, k);

final_result.a <== a;
Expand Down
20 changes: 20 additions & 0 deletions javascript/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
JavaScript implementation of the PLUME signature scheme.

## API
### sign(message, privateKey)
Signs a message using the provided private key.
* `message` - String message to sign
* `privateKey` - Hex private key

Returns the PLUME signature.

### `verify(message, publicKey, signature)`
Verifies a signature matches the message and public key.
* `message` - Original string message
* `publicKey` - Hex public key
* `signature` - PLUME signature

Returns true if the signature is valid, false otherwise.

### License
MIT
3 changes: 2 additions & 1 deletion javascript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "plume-sig",
"version": "2.0.3",
"version": "2.0.5",
"repository": "https://github.com/plume-sig/zk-nullifier-sig/",
"pnpm": {
"overrides": {
"@noble/secp256k1": "$@noble/secp256k1"
Expand Down
20 changes: 12 additions & 8 deletions rust-arkworks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
[package]
name = "sig"
version = "0.1.0"
name = "plume_arkworks"
version = "0.0.1"
edition = "2021"
license = "MIT"
description = "Implementation of PLUME: nullifier friendly signature scheme on ECDSA; using the `arkworks-rs` libraries"
repository = "https://github.com/plume-sig/zk-nullifier-sig/"
categories = ["cryptography", "cryptography::cryptocurrencies"]
keywords = ["nullifier", "zero-knowledge", "ECDSA", "PLUME"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ark-ec = "~0.3.0"
ark-ff = "0.3.0"
ark-std = "0.3.0"
ark-serialize = "0.3.0"
ark-serialize-derive = "0.3.0"
thiserror = "1.0.30"
secp256k1 = { git = "https://github.com/geometryresearch/ark-secp256k1.git" }
ark-ff = "~0.3.0"
ark-std = "~0.3.0"
ark-serialize = "~0.3.0"
ark-serialize-derive = "~0.3.0"
secp256k1 = { git = "https://github.com/geometryresearch/ark-secp256k1.git", version = "0.1.0" }
rand_core = { version = "0.6", default-features = false, features = [
"getrandom",
] }
Expand Down
3 changes: 3 additions & 0 deletions rust-arkworks/README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/plume-sig/zk-nullifier-sig/blob/main/README.md
# HAZMAT
Please note that until `v0.1.0` this is very much a preview crate which lets you have some preliminary feel of the structure and the reference implementation approach.
36 changes: 28 additions & 8 deletions rust-arkworks/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
use thiserror::Error;
// Legacy definitions for errors which will be gone with arkworks upgrade to `>=0.4.0`.
// `use ark_ec::hashing::HashToCurveError;`

/// This is an error that could occur when running a cryptograhic primitive
#[derive(Error, Debug, PartialEq)]
pub enum CryptoError {
#[error("Cannot hash to curve")]
CannotHashToCurve,
// use thiserror::Error;

#[error("Cannot encode a point not on the curve")]
PointNotOnCurve,
// /// This is an error that could occur when running a cryptograhic primitive
// #[derive(Error, Debug, PartialEq)]
// pub enum CryptoError {
// #[error("Cannot hash to curve")]
// CannotHashToCurve,

// #[error("Cannot encode a point not on the curve")]
// PointNotOnCurve,
// }

// Let's outline what errors will be in `~0.4.0`
/// It's an interim `enum` between legacy definition of the errors and prospective which will be relying on [`ark_ec::hashing::HashToCurveError`].
#[derive(Debug, Clone)]
pub enum HashToCurveError {
/// Mimics the `ark_ec::hashing::HashToCurveError` enum
UnsupportedCurveError(String),
/// Mimics the `ark_ec::hashing::HashToCurveError` enum
MapToCurveError(String),
/* let's add two more items to absorb everything
in `crate::hash_to_curve` which is
subject to deprecation */
/// Absorbs any legacy error in [`mod@crate::hash_to_curve`]. They will be deprecated with upgrade to `~0.4.0`.
Legacy,
/// A special case for a reference function. It will be moved to <./examples> with the upgrade to `~0.4.0`.
ReferenceTryAndIncrement,
}
30 changes: 14 additions & 16 deletions rust-arkworks/src/hash_to_curve.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
use crate::error::CryptoError;
use crate::error::HashToCurveError;
use ark_ec::short_weierstrass_jacobian::GroupAffine;
use ark_ec::{AffineCurve, ProjectiveCurve};
use ark_ff::FromBytes;
use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
use elliptic_curve::sec1::ToEncodedPoint;
use k256::sha2::Sha256;
use k256::AffinePoint;
use k256::{ProjectivePoint, Secp256k1};
// TODO why not ark libs for these? oO
use k256::{sha2::Sha256, AffinePoint, ProjectivePoint, Secp256k1};
use secp256k1::Sec1EncodePoint;
use tiny_keccak::{Hasher, Shake, Xof};

pub fn hash_to_curve<Fp: ark_ff::PrimeField, P: ark_ec::SWModelParameters>(
msg: &[u8],
pk: &GroupAffine<P>,
) -> GroupAffine<P> {
let pk_encoded = pk.to_encoded_point(true);
let b = hex::decode(pk_encoded).unwrap();
) -> Result<GroupAffine<P>, HashToCurveError> {
let b = hex::decode(&pk.to_encoded_point(true)).expect(super::EXPECT_MSG_DECODE);
let x = [msg, b.as_slice()];
let x = x.concat().clone();
let x = x.as_slice();
Expand All @@ -24,7 +22,7 @@ pub fn hash_to_curve<Fp: ark_ff::PrimeField, P: ark_ec::SWModelParameters>(
&[x],
b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_",
)
.unwrap();
.map_err(|_| HashToCurveError::Legacy)?;

let pt_affine = pt.to_affine();

Expand All @@ -33,13 +31,13 @@ pub fn hash_to_curve<Fp: ark_ff::PrimeField, P: ark_ec::SWModelParameters>(

pub fn k256_affine_to_arkworks_secp256k1_affine<P: ark_ec::SWModelParameters>(
k_pt: AffinePoint,
) -> GroupAffine<P> {
) -> Result<GroupAffine<P>, HashToCurveError> {
let encoded_pt = k_pt.to_encoded_point(false);

let num_field_bytes = 40;

// extract k_pt.x
let k_pt_x_bytes = encoded_pt.x().unwrap();
let k_pt_x_bytes = encoded_pt.x().ok_or(HashToCurveError::Legacy)?;

// pad x bytes
let mut k_pt_x_bytes_vec = vec![0u8; num_field_bytes];
Expand All @@ -50,10 +48,10 @@ pub fn k256_affine_to_arkworks_secp256k1_affine<P: ark_ec::SWModelParameters>(
);
}
let reader = std::io::BufReader::new(k_pt_x_bytes_vec.as_slice());
let g_x = P::BaseField::read(reader).unwrap();
let g_x = P::BaseField::read(reader).map_err(|_| HashToCurveError::Legacy)?;

// extract k_pt.y
let k_pt_y_bytes = encoded_pt.y().unwrap();
let k_pt_y_bytes = encoded_pt.y().ok_or(HashToCurveError::Legacy)?;

// pad y bytes
let mut k_pt_y_bytes_vec = vec![0u8; num_field_bytes];
Expand All @@ -65,13 +63,13 @@ pub fn k256_affine_to_arkworks_secp256k1_affine<P: ark_ec::SWModelParameters>(
}

let reader = std::io::BufReader::new(k_pt_y_bytes_vec.as_slice());
let g_y = P::BaseField::read(reader).unwrap();
let g_y = P::BaseField::read(reader).map_err(|_| HashToCurveError::Legacy)?;

GroupAffine::<P>::new(g_x, g_y, false)
Ok(GroupAffine::<P>::new(g_x, g_y, false))
}

/// Kobi's hash_to_curve function, here for reference only
pub fn _try_and_increment<C: ProjectiveCurve>(msg: &[u8]) -> Result<C::Affine, CryptoError> {
pub fn _try_and_increment<C: ProjectiveCurve>(msg: &[u8]) -> Result<C::Affine, HashToCurveError> {
for nonce in 0u8..=255 {
let mut h = Shake::v128();
h.update(&[nonce]);
Expand All @@ -85,5 +83,5 @@ pub fn _try_and_increment<C: ProjectiveCurve>(msg: &[u8]) -> Result<C::Affine, C
}
}

Err(CryptoError::CannotHashToCurve)
Err(HashToCurveError::ReferenceTryAndIncrement)
}
Loading

0 comments on commit bae5701

Please sign in to comment.