diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 850ac38..b3f405a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -17,12 +17,12 @@ jobs: - name: Install Nargo uses: noir-lang/noirup@v0.1.3 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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1dec562..bd564ec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 @@ -38,7 +38,7 @@ jobs: - name: Install Nargo uses: noir-lang/noirup@v0.1.3 with: - toolchain: 0.34.0 + toolchain: 0.36.0 - name: Run formatter run: nargo fmt --check diff --git a/Nargo.toml b/Nargo.toml index ca37ec6..2564960 100644 --- a/Nargo.toml +++ b/Nargo.toml @@ -1,7 +1,7 @@ [package] -name = "noir_library" +name = "eddsa" type = "lib" authors = [""] -compiler_version = ">=0.34.0" +compiler_version = ">=0.36.0" [dependencies] diff --git a/README.md b/README.md index 1d25ec0..164111b 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,6 @@ -# 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 @@ -27,11 +8,11 @@ In your _Nargo.toml_ file, add the version of this library you would like to ins ``` [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` \ No newline at end of file +`PLACEHOLDER` diff --git a/src/lib.nr b/src/lib.nr index 80b968c..a1656c4 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -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( + 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::(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::(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::(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::(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::( + pub_key_x, + pub_key_y, + signature_s, + signature_r8_x, + signature_r8_y, + message, + ) }