From 3b535c85f31ce4b575df37a5f349bc370d0f3412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Wed, 19 Feb 2025 17:58:53 +0000 Subject: [PATCH 1/5] refactoring ecdh to work properly as a lib, extending it to use with other curves --- packages/ecdh/Nargo.toml | 6 +-- packages/ecdh/Prover.toml | 68 -------------------------------- packages/ecdh/README.md | 39 ++++++++---------- packages/ecdh/src/bjj.nr | 24 +++++++++++ packages/ecdh/src/globals.nr | 6 --- packages/ecdh/src/lib.nr | 62 +++-------------------------- packages/ecdh/src/main.nr | 45 --------------------- packages/ecdh/tests/ecdh.test.ts | 47 ---------------------- tests/Nargo.toml | 6 ++- tests/src/ecdh/mod.nr | 30 ++++++++++++++ tests/src/lib.nr | 1 + 11 files changed, 84 insertions(+), 250 deletions(-) delete mode 100644 packages/ecdh/Prover.toml create mode 100644 packages/ecdh/src/bjj.nr delete mode 100644 packages/ecdh/src/globals.nr delete mode 100644 packages/ecdh/src/main.nr delete mode 100644 packages/ecdh/tests/ecdh.test.ts create mode 100644 tests/src/ecdh/mod.nr diff --git a/packages/ecdh/Nargo.toml b/packages/ecdh/Nargo.toml index 04393ca..03f5c2d 100644 --- a/packages/ecdh/Nargo.toml +++ b/packages/ecdh/Nargo.toml @@ -1,8 +1,8 @@ [package] name = "ecdh" -type = "bin" -authors = ["YashBit"] +type = "lib" +authors = ["YashBit", "signorecello"] compiler_version = ">=0.39.0" [dependencies] -ec = { tag = "v0.1.2", git = "https://github.com/noir-lang/ec" } +ec = { tag = "master", git = "https://github.com/noir-lang/ec" } diff --git a/packages/ecdh/Prover.toml b/packages/ecdh/Prover.toml deleted file mode 100644 index b5bf9a8..0000000 --- a/packages/ecdh/Prover.toml +++ /dev/null @@ -1,68 +0,0 @@ -private_key1 = [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", -] -private_key2 = [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", -] diff --git a/packages/ecdh/README.md b/packages/ecdh/README.md index 3c428df..367788e 100644 --- a/packages/ecdh/README.md +++ b/packages/ecdh/README.md @@ -1,35 +1,28 @@ -# ECDH Library using Baby JubJub Curve +# ECDH Library -This repository introduces a set of functions and utilities for elliptic curve cryptography using the Baby JubJub curve. The library provides essential operations for the Elliptic Curve Diffie-Hellman (ECDH) protocol, along with utility functions for key generation, field element conversion, and shared key computation. +This repository introduces a set of functions and utilities for elliptic curve cryptography using. The library provides essential operations for the Elliptic Curve Diffie-Hellman (ECDH) protocol, along with utility functions for key generation, field element conversion, and shared key computation. -## Features - -- **Byte Array to Field Element Conversion:** Utility functions to convert byte arrays into field elements for use in elliptic curve operations. -- **Public Key Generation:** Generate public keys from private keys using the Baby JubJub curve, with both standard and optimized methods. -- **Shared Key Computation:** Compute shared keys for secure communication using the ECDH protocol. -- **Main Function:** A main function to generate ECDH shared keys, demonstrating the usage of the library. -- **Testing:** Comprehensive tests to verify the correctness of key derivation and field conversion functions. - -## Compiling the Circuit +At the moment only Baby JubJub is supported, feel free to contribute with more curves. -To compile the circuit, navigate one directory above the source code and run the following command: +## Features -```bash -nargo compile -``` +- **Public Key Generation:** Generate public keys from private keys. +- **Shared Key Computation:** Compute shared keys for secure communication. -## Running Tests +## Usage -To run the tests for this library, use the following command: +To use this library, add the dependency to your `Nargo.toml`: -```bash -bun run test +```toml +ecdh = { git = "https://github.com/privacy-scaling-explorations/zk-kit.noir", tag = "main", directory = "packages/ecdh" } ``` -## Running Tests with Mocha +You can then import the ECDH implementation for the curve you're working with, like so: -To run the tests using Mocha with TypeScript, use the following command: +```rust +use ecdh::bjj::BJJ; // only Baby JubJub is supported at the moment +let ecdh = BJJ::new(your_private_key); +let public_key = ecdh.derive_public_key(); -```bash -npx mocha -r ts-node/register tests/ecdh.tests.ts +let shared_key = ecdh.derive_shared_key(someone_elses_public_key); ``` diff --git a/packages/ecdh/src/bjj.nr b/packages/ecdh/src/bjj.nr new file mode 100644 index 0000000..3d6a43b --- /dev/null +++ b/packages/ecdh/src/bjj.nr @@ -0,0 +1,24 @@ +use crate::ECDHTrait; +use ec::consts::te::{baby_jubjub, BabyJubjub}; +use ec::tecurve::affine::Point; + +pub struct BJJ { + pub private_key: Field, + pub bjj: BabyJubjub, +} + +impl ECDHTrait for BJJ { + fn new(private_key: Field) -> Self { + let bjj = baby_jubjub(); + Self { bjj, private_key } + } + + fn derive_public_key(self) -> Point { + self.bjj.curve.mul(self.private_key, self.bjj.base8) + } + + fn derive_shared_key(self, public_key: Point) -> Field { + let shared_key = self.bjj.curve.mul(self.private_key, public_key); + shared_key.x + } +} diff --git a/packages/ecdh/src/globals.nr b/packages/ecdh/src/globals.nr deleted file mode 100644 index 225f205..0000000 --- a/packages/ecdh/src/globals.nr +++ /dev/null @@ -1,6 +0,0 @@ -// Globals Edward Curves supported Baby JubJub -use ec::consts::te::{baby_jubjub, BabyJubjub}; -use ec::tecurve::affine::Point; - -global BJJ: BabyJubjub = baby_jubjub(); -global G: Point = BJJ.base8; diff --git a/packages/ecdh/src/lib.nr b/packages/ecdh/src/lib.nr index 58f3645..30e8792 100644 --- a/packages/ecdh/src/lib.nr +++ b/packages/ecdh/src/lib.nr @@ -1,58 +1,8 @@ -mod globals; -use ec::tecurve::affine::{Curve, Point}; +mod bjj; +use ec::tecurve::affine::Point; -// @@@@@@ Core ECDH Implementation - -/// Converts a byte array to a field element. -/// # Arguments -/// * `bytes` - A fixed-size array of 32 bytes. -/// * `big_endian` - A boolean indicating if the byte array is in big-endian format. -/// # Returns -/// A `Field` element representing the converted byte array. -pub fn field_from_bytes(bytes: [u8; 32], big_endian: bool) -> Field { - let mut as_field: Field = 0; - let mut offset: Field = 1; - - for i in 0..32 { - let index = if big_endian { 31 - i } else { i }; - as_field += (bytes[index] as Field) * offset; - offset *= 256; - } - - as_field -} - -/// Computes a public key from a private key using the Baby JubJub curve. -/// # Arguments -/// * `private_key` - The private key as a `Field` element. -/// # Returns -/// The corresponding `Point` on the Baby JubJub curve. -pub fn derive_public_key(private_key: Field) -> Point { - let base_point = Point::new( - 5299619240641551281634865583518297030282874472190772894086521144482721001553, - 16950150798460657717958625567821834550301663161624707787222815936182638968203, - ); - let baby_jubjub_curve = Curve::new(168700, 168696, base_point); - baby_jubjub_curve.mul(private_key, base_point) -} - -/// Optimized public key derivation using Baby JubJub curve. -/// # Arguments -/// * `private_key` - The private key as a `Field` element. -/// # Returns -/// The public key as a `Point` on the Baby JubJub curve. -pub fn derive_public_key_optimized(private_key: Field) -> Point { - let X = globals::BJJ.curve.mul(private_key, globals::G); - X -} - -/// Computes a shared secret key using ECDH with the Baby JubJub curve. -/// # Arguments -/// * `private_key` - The private key as a `Field` element. -/// * `public_key` - The public key as a `Point` on the Baby JubJub curve. -/// # Returns -/// The shared secret key as a `Field` element. -pub fn derive_shared_key(private_key: Field, public_key: Point) -> Field { - let shared_key = globals::BJJ.curve.mul(private_key, public_key); - shared_key.x +pub trait ECDHTrait { + fn new(private_key: Field) -> Self; + fn derive_public_key(self) -> Point; + fn derive_shared_key(self, public_key: Point) -> Field; } diff --git a/packages/ecdh/src/main.nr b/packages/ecdh/src/main.nr deleted file mode 100644 index d893b66..0000000 --- a/packages/ecdh/src/main.nr +++ /dev/null @@ -1,45 +0,0 @@ -mod lib; -// ECDH Circuit -fn main(private_key1: [u8; 32], private_key2: [u8; 32]) { - // Convert private keys to fields - let private_key1_as_field = lib::field_from_bytes(private_key1, true); - let private_key2_as_field = lib::field_from_bytes(private_key2, true); - - // Generate public keys from private keys - let public_key1 = lib::derive_public_key_optimized(private_key1_as_field); - let public_key2 = lib::derive_public_key_optimized(private_key2_as_field); - - // Generate Both ECDH Shared Keys - Scalar Multiply - let shared_key1 = lib::derive_shared_key(private_key2_as_field, public_key1); - let shared_key2 = lib::derive_shared_key(private_key1_as_field, public_key2); - assert(shared_key1 == shared_key2); -} - -// ########## Tests ########## - -// Description: This test verifies the correctness of the optimized public key derivation function. -// It ensures that the `derive_public_key_optimized` function produces the same result as the -// standard `derive_public_key` function for a given test input. - -#[test] -fn test_derive_public_key() { - let test_field_value = 0x3fbbccb240537392421955b07a0d65eded9e7637995bf2f9cfe29e19b580e4; - let derived_public_key = lib::derive_public_key_optimized(test_field_value); - let test_derived_public_key = lib::derive_public_key(test_field_value); - assert(derived_public_key.x == test_derived_public_key.x); - assert(derived_public_key.y == test_derived_public_key.y); -} - -/// Test that the `field_from_bytes` function correctly converts a 32-byte array of zeroes to a field element with a value of `0`. -/// -/// This test ensures that: -/// - The input is a 32-byte array initialized with zeroes. -/// - The `field_from_bytes` function is called with the input and a boolean flag set to `true`. -/// - The output is asserted to be `0`, verifying that the conversion is correct. -#[test] -// Define the test function for `field_from_bytes` -fn field_from_bytes_correct() { - let bytes: [u8; 32] = [0; 32]; - let field_result = lib::field_from_bytes(bytes, true); - assert_eq(field_result, 0); -} diff --git a/packages/ecdh/tests/ecdh.test.ts b/packages/ecdh/tests/ecdh.test.ts deleted file mode 100644 index 0cedf60..0000000 --- a/packages/ecdh/tests/ecdh.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { UltraHonkBackend } from '@aztec/bb.js' -import { Noir } from '@noir-lang/noir_js' -import { ProofData } from '@noir-lang/types' -import { expect } from 'chai' -import { randomBytes } from 'crypto' -import { readFileSync } from 'fs' -import { resolve } from 'path' -import 'mocha' - -function generatePrivateKey(): Uint8Array { - return randomBytes(32) -} - -describe('ECDH Circuit Tests', function() { - let noir: Noir - let backend: UltraHonkBackend - let correctProof: ProofData - - beforeEach(async () => { - const circuitFile = readFileSync(resolve(__dirname, '../../../target/ecdh.json'), 'utf-8') - const circuit = JSON.parse(circuitFile) - backend = new UltraHonkBackend(circuit.bytecode) - noir = new Noir(circuit) - const pk1 = generatePrivateKey() - const pk2 = generatePrivateKey() - - // Convert Uint8Array to regular arrays - const input = { - private_key1: Array.from(pk1), - private_key2: Array.from(pk2), - } - - const { witness } = await noir.execute(input) - - correctProof = await backend.generateProof(witness) - }) - - it('Should generate valid proof for correct input', async function() { - expect(correctProof.proof).to.be.instanceOf(Uint8Array) - }) - - it('Should verify valid proof for correct input', async function() { - expect(correctProof).to.not.be.undefined // Ensure proof is generated - const verification = await backend.verifyProof(correctProof) - expect(verification).to.be.true - }) -}) diff --git a/tests/Nargo.toml b/tests/Nargo.toml index b00c3ec..24b8af5 100644 --- a/tests/Nargo.toml +++ b/tests/Nargo.toml @@ -1,9 +1,11 @@ [package] -name = "merkle_trees" +name = "zk_kit_tests" type = "lib" -authors = ["fabianschu", "signorecello"] +authors = ["signorecello"] compiler_version = ">=0.39.0" [dependencies] bignum = { git = "https://github.com/noir-lang/noir-bignum", tag = "zpedro/default_trait" } trees = { path = "../packages/merkle-trees" } +ecdh = { path = "../packages/ecdh" } +ec = { tag = "master", git = "https://github.com/noir-lang/ec" } diff --git a/tests/src/ecdh/mod.nr b/tests/src/ecdh/mod.nr new file mode 100644 index 0000000..f633f1e --- /dev/null +++ b/tests/src/ecdh/mod.nr @@ -0,0 +1,30 @@ +use ec::tecurve::affine::Point; +use ecdh::bjj::BJJ; + +global alice_sk: Field = 0x60c0102756aac2cf5d7277792a469bff83dfe3d3e7e50ad5a55383f3a89283e; +global bob_sk: Field = 0x86cdaad8886954a2eb20142fb98468d476a2d6c7b2c571af42cdc041b1a923c; + +#[test] +fn test_pk() { + let ecdh = BJJ::new(alice_sk); + let expected_pk = Point::new( + 0x17d6ff45a37eee4ca071bb451c09b47184ed67492c6bac9279fb0a7d2d4ad1dc, + 0x02d3a6338eee1ae8a38b278ce6c13179889c780f5e0d7294b904b0fa52c7dc3e, + ); + let pk = ecdh.derive_public_key(); + assert(pk == expected_pk); +} + +#[test] +fn test_shared_k() { + let alice_ecdh = BJJ::new(alice_sk); + let bob_ecdh = BJJ::new(bob_sk); + + let alice_pk: Point = alice_ecdh.derive_public_key(); + let bob_pk: Point = bob_ecdh.derive_public_key(); + + let shared_key_with_bob = alice_ecdh.derive_shared_key(bob_pk); + let shared_key_with_alice = bob_ecdh.derive_shared_key(alice_pk); + + assert(shared_key_with_bob == shared_key_with_alice); +} diff --git a/tests/src/lib.nr b/tests/src/lib.nr index 6817ad9..e7beb26 100644 --- a/tests/src/lib.nr +++ b/tests/src/lib.nr @@ -1,2 +1,3 @@ mod mt; mod smt; +mod ecdh; From 331bf880e159cfceaea9dcdfbeae0979fe42fefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 20 Feb 2025 09:03:47 +0000 Subject: [PATCH 2/5] bumping noirup in github actions --- .github/workflows/main.yml | 4 ++-- packages/ecdh/Nargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 404bd4b..3c85e38 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,7 +32,7 @@ jobs: bun-version: latest - uses: noir-lang/noirup@v0.1.3 with: - toolchain: 0.39.0 + toolchain: 1.0.0-beta.1 - run: bun i - run: bun fmt @@ -44,5 +44,5 @@ jobs: - uses: actions/checkout@v4 - uses: noir-lang/noirup@v0.1.3 with: - toolchain: 0.39.0 + toolchain: 1.0.0-beta.1 - run: nargo test diff --git a/packages/ecdh/Nargo.toml b/packages/ecdh/Nargo.toml index 03f5c2d..72ca70c 100644 --- a/packages/ecdh/Nargo.toml +++ b/packages/ecdh/Nargo.toml @@ -2,7 +2,7 @@ name = "ecdh" type = "lib" authors = ["YashBit", "signorecello"] -compiler_version = ">=0.39.0" +compiler_version = ">=1.0.0-beta.1" [dependencies] ec = { tag = "master", git = "https://github.com/noir-lang/ec" } From e1c08ea91d92dd2ac0f9ed07a95418f7bb6a566c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 20 Feb 2025 09:04:09 +0000 Subject: [PATCH 3/5] bumping noirup in github actions --- packages/merkle-trees/Nargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/merkle-trees/Nargo.toml b/packages/merkle-trees/Nargo.toml index cb516e2..c486584 100644 --- a/packages/merkle-trees/Nargo.toml +++ b/packages/merkle-trees/Nargo.toml @@ -2,4 +2,4 @@ name = "merkle_trees" type = "lib" authors = ["fabianschu", "signorecello"] -compiler_version = ">=0.39.0" +compiler_version = ">=0.1.0-beta.1" From 1a6e5c3a4bb6f064be3a85db021f5136cf052d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 20 Feb 2025 09:25:01 +0000 Subject: [PATCH 4/5] bumping noirup in github actions --- packages/ecdh/Nargo.toml | 1 - packages/merkle-trees/Nargo.toml | 1 - tests/Nargo.toml | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/ecdh/Nargo.toml b/packages/ecdh/Nargo.toml index 72ca70c..da02122 100644 --- a/packages/ecdh/Nargo.toml +++ b/packages/ecdh/Nargo.toml @@ -2,7 +2,6 @@ name = "ecdh" type = "lib" authors = ["YashBit", "signorecello"] -compiler_version = ">=1.0.0-beta.1" [dependencies] ec = { tag = "master", git = "https://github.com/noir-lang/ec" } diff --git a/packages/merkle-trees/Nargo.toml b/packages/merkle-trees/Nargo.toml index c486584..421765c 100644 --- a/packages/merkle-trees/Nargo.toml +++ b/packages/merkle-trees/Nargo.toml @@ -2,4 +2,3 @@ name = "merkle_trees" type = "lib" authors = ["fabianschu", "signorecello"] -compiler_version = ">=0.1.0-beta.1" diff --git a/tests/Nargo.toml b/tests/Nargo.toml index 24b8af5..235c409 100644 --- a/tests/Nargo.toml +++ b/tests/Nargo.toml @@ -2,7 +2,6 @@ name = "zk_kit_tests" type = "lib" authors = ["signorecello"] -compiler_version = ">=0.39.0" [dependencies] bignum = { git = "https://github.com/noir-lang/noir-bignum", tag = "zpedro/default_trait" } From 14ebd7b748e240ecaeabf107c71f3cd8d829d11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 20 Feb 2025 09:41:50 +0000 Subject: [PATCH 5/5] moving bignum to main branch --- tests/Nargo.toml | 2 +- tests/src/smt/bignum.nr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Nargo.toml b/tests/Nargo.toml index 235c409..97be875 100644 --- a/tests/Nargo.toml +++ b/tests/Nargo.toml @@ -4,7 +4,7 @@ type = "lib" authors = ["signorecello"] [dependencies] -bignum = { git = "https://github.com/noir-lang/noir-bignum", tag = "zpedro/default_trait" } +bignum = { git = "https://github.com/noir-lang/noir-bignum", tag = "main" } trees = { path = "../packages/merkle-trees" } ecdh = { path = "../packages/ecdh" } ec = { tag = "master", git = "https://github.com/noir-lang/ec" } diff --git a/tests/src/smt/bignum.nr b/tests/src/smt/bignum.nr index c247cad..a4d0985 100644 --- a/tests/src/smt/bignum.nr +++ b/tests/src/smt/bignum.nr @@ -17,7 +17,7 @@ fn hasher(input: [BN; N]) -> BN { #[test] fn test_sparse_merkle_tree_add_bignum() { - let mut smt = SparseMerkleTree::new(hasher, hasher); + let mut smt: SparseMerkleTree = SparseMerkleTree::new(hasher, hasher); let key = BN::from_slice([0x01, 0x00, 0x00]); let value = BN::from_slice([