Skip to content

Commit

Permalink
feat: add initial implementation (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Nov 21, 2024
1 parent cc75d2c commit 96b1524
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 40 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ jobs:
- name: Install Nargo
uses: noir-lang/[email protected]
with:
toolchain: 0.34.0
toolchain: 0.36.0

- name: Install bb
run: |
npm install -g bbup
bbup -nv 0.34.0
bbup -nv 0.36.0
- name: Build Noir benchmark programs
run: nargo export
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
toolchain: [nightly, 0.34.0]
toolchain: [nightly, 0.36.0]
steps:
- name: Checkout sources
uses: actions/checkout@v4
Expand All @@ -38,7 +38,7 @@ jobs:
- name: Install Nargo
uses: noir-lang/[email protected]
with:
toolchain: 0.34.0
toolchain: 0.36.0

- name: Run formatter
run: nargo fmt --check
Expand Down
4 changes: 2 additions & 2 deletions Nargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "noir_library"
name = "eddsa"
type = "lib"
authors = [""]
compiler_version = ">=0.34.0"
compiler_version = ">=0.36.0"

[dependencies]
27 changes: 4 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
# noir-library-starter

This repository is a template used by the noir-lang org when creating internally maintained libraries.

This provides out of the box:

- A simple CI setup to test and format the library
- A canary flagging up compilation failures on nightly releases.
- A [release-please](https://github.com/googleapis/release-please) setup to ease creating releases for the library.

Feel free to use this template as a starting point to create your own Noir libraries.

---

# LIBRARY_NAME

Add a brief description of the library

## Benchmarks

TODO
# eddsa

A library which exports the `eddsa_verify` function which formerly existed within the Noir stdlib.

## Installation

In your _Nargo.toml_ file, add the version of this library you would like to install under dependency:

```
[dependencies]
LIBRARY = { tag = "v0.1.0", git = "https://github.com/noir-lang/LIBRARY_NAME" }
eddsa = { tag = "v0.1.0", git = "https://github.com/noir-lang/eddsa" }
```

## `library`

### Usage

`PLACEHOLDER`
`PLACEHOLDER`
146 changes: 135 additions & 11 deletions src/lib.nr
Original file line number Diff line number Diff line change
@@ -1,15 +1,139 @@
/// This doesn't really do anything by ensures that there is a test for CI to run.
#[test]
fn smoke_test() {
assert(true);
use std::default::Default;
use std::ec::consts::te::baby_jubjub;
use std::ec::tecurve::affine::Point as TEPoint;
use std::hash::Hasher;

pub fn eddsa_verify<H>(
pub_key_x: Field,
pub_key_y: Field,
signature_s: Field,
signature_r8_x: Field,
signature_r8_y: Field,
message: Field,
) -> bool
where
H: Hasher + Default,
{
// Verifies by testing:
// S * B8 = R8 + H(R8, A, m) * A8
let bjj = baby_jubjub();

let pub_key = TEPoint::new(pub_key_x, pub_key_y);
assert(bjj.curve.contains(pub_key));

let signature_r8 = TEPoint::new(signature_r8_x, signature_r8_y);
assert(bjj.curve.contains(signature_r8));
// Ensure S < Subgroup Order
assert(signature_s.lt(bjj.suborder));
// Calculate the h = H(R, A, msg)
let mut hasher = H::default();
hasher.write(signature_r8_x);
hasher.write(signature_r8_y);
hasher.write(pub_key_x);
hasher.write(pub_key_y);
hasher.write(message);
let hash: Field = hasher.finish();
// Calculate second part of the right side: right2 = h*8*A
// Multiply by 8 by doubling 3 times. This also ensures that the result is in the subgroup.
let pub_key_mul_2 = bjj.curve.add(pub_key, pub_key);
let pub_key_mul_4 = bjj.curve.add(pub_key_mul_2, pub_key_mul_2);
let pub_key_mul_8 = bjj.curve.add(pub_key_mul_4, pub_key_mul_4);
// We check that A8 is not zero.
assert(!pub_key_mul_8.is_zero());
// Compute the right side: R8 + h * A8
let right = bjj.curve.add(signature_r8, bjj.curve.mul(hash, pub_key_mul_8));
// Calculate left side of equation left = S * B8
let left = bjj.curve.mul(signature_s, bjj.base8);

left.eq(right)
}

// This is an example benchmark.
// Changes to the number of constraints generated by this function will show in PRs.
#[export]
fn bench_test(mut x: Field) -> Field {
for _ in 0..100 {
x *= x;
// Returns the public key of the given secret key as (pub_key_x, pub_key_y)
pub fn eddsa_to_pub(secret: Field) -> (Field, Field) {
let bjj = baby_jubjub();
let pub_key = bjj.curve.mul(secret, bjj.curve.gen);
(pub_key.x, pub_key.y)
}

mod tests {
use std::ec::consts::te::baby_jubjub;
use std::ec::tecurve::affine::Point as TEPoint;
use std::hash::poseidon::PoseidonHasher;
use std::hash::poseidon2::Poseidon2Hasher;

use super::{eddsa_to_pub, eddsa_verify};

#[test]
fn main() {
let priv_key_a = 123;
let priv_key_b = 456;
let msg = 789;

let bjj = baby_jubjub();

let pub_key_a = bjj.curve.mul(priv_key_a, bjj.curve.gen);
let pub_key_b = bjj.curve.mul(priv_key_b, bjj.curve.gen);
let (pub_key_a_x, pub_key_a_y) = eddsa_to_pub(priv_key_a);
let (pub_key_b_x, pub_key_b_y) = eddsa_to_pub(priv_key_b);
assert(TEPoint::new(pub_key_a_x, pub_key_a_y) == pub_key_a);
assert(TEPoint::new(pub_key_b_x, pub_key_b_y) == pub_key_b);
// Manually computed as fields can't use modulo. Importantantly the commitment is within
// the subgroup order. Note that choice of hash is flexible for this step.
// let r_a = hash::pedersen_commitment([_priv_key_a, msg])[0] % bjj.suborder; // modulus computed manually
let r_a = 1414770703199880747815475415092878800081323795074043628810774576767372531818;
// let r_b = hash::pedersen_commitment([_priv_key_b, msg])[0] % bjj.suborder; // modulus computed manually
let r_b = 571799555715456644614141527517766533395606396271089506978608487688924659618;

let r8_a = bjj.curve.mul(r_a, bjj.base8);
let r8_b = bjj.curve.mul(r_b, bjj.base8);
// let h_a: [Field; 6] = hash::poseidon::bn254::hash_5([
// r8_a.x,
// r8_a.y,
// pub_key_a.x,
// pub_key_a.y,
// msg,
// ]);
// let h_b: [Field; 6] = hash::poseidon::bn254::hash_5([
// r8_b.x,
// r8_b.y,
// pub_key_b.x,
// pub_key_b.y,
// msg,
// ]);
// let s_a = (r_a + _priv_key_a * h_a) % bjj.suborder; // modulus computed manually
let s_a = 30333430637424319196043722294837632681219980330991241982145549329256671548;
// let s_b = (r_b + _priv_key_b * h_b) % bjj.suborder; // modulus computed manually
let s_b = 1646085314320208098241070054368798527940102577261034947654839408482102287019;
// User A verifies their signature over the message
assert(eddsa_verify::<PoseidonHasher>(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg));
// User B's signature over the message can't be used with user A's pub key
assert(!eddsa_verify::<PoseidonHasher>(pub_key_a.x, pub_key_a.y, s_b, r8_b.x, r8_b.y, msg));
// User A's signature over the message can't be used with another message
assert(
!eddsa_verify::<PoseidonHasher>(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg + 1),
);
// Using a different hash should fail
assert(
!eddsa_verify::<Poseidon2Hasher>(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg),
);
}
x
}

#[export]
fn bench_eddsa_poseidon(
pub_key_x: Field,
pub_key_y: Field,
signature_s: Field,
signature_r8_x: Field,
signature_r8_y: Field,
message: Field,
) -> bool {
eddsa_verify::<std::hash::poseidon::PoseidonHasher>(
pub_key_x,
pub_key_y,
signature_s,
signature_r8_x,
signature_r8_y,
message,
)
}

0 comments on commit 96b1524

Please sign in to comment.