Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement ECDH #8

Merged
merged 29 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0d3b065
ECDH Basics, tsconfig.json - No tests yet
Jul 26, 2024
a6ec58f
feat(ecdh): eCDH Encryption without internal tests
Jul 26, 2024
5bab3bb
feat(ecdh): ds
Jul 29, 2024
3502e23
feat(circuit): ds
Jul 29, 2024
3b4cba8
feat(circuit): ds
Jul 29, 2024
399ced1
feat(circuit): new Improved Circuit
Jul 29, 2024
d1be79c
refactor: noir Tests in JS
Jul 29, 2024
0d957d9
feat(ecdh): yO
Jul 30, 2024
120fd63
feat(ecdh): ds
Jul 30, 2024
ed1c46e
feat(ecdh): eCDH Test
Jul 30, 2024
f8f046f
Basic Unit Test Added
Aug 5, 2024
4d880f1
Formatted yarn fmt
Aug 5, 2024
25068d4
feat(ecdh): hI
Aug 5, 2024
820bb61
feat(ecdh): k
Aug 6, 2024
3c26856
refactor: l
Aug 11, 2024
9960f44
refactor(ecdh): Added two more tests, ECC BabyJubJub
Aug 12, 2024
83878f7
refactor(ecdh): NoirJS
Aug 13, 2024
696fb69
refactor(ecdh): README UPDATED
Aug 17, 2024
6375464
refactor(ecdh): refactor: mocha test files, test file
Aug 23, 2024
1385f9e
refactor(ecdh): refactor: package.json, .toml files
Aug 26, 2024
40dab0e
refactor(ecdh): refactor - .toml, lib.nr and main.nr in ECDH
Aug 26, 2024
fa95ff4
refactor(ecdh): refactor: package.json
Aug 26, 2024
b4c0027
refactor(ecdh): refactor: globals, lib, ecdh.test.ts
Aug 26, 2024
2a6bd28
refactor(ecdh): refactor: ecdh.test.ts
Aug 26, 2024
5d69406
refactor(ecdh): refactor: yarn fmt
Aug 26, 2024
973e3b7
refactor(ecdh): refactor: .gitignore added target
Aug 26, 2024
0ef2486
refactor(ecdh): refactor: removed wait times in test.ts
Aug 26, 2024
115004f
refactor(ecdh): git commit -m "refactor: package.json"
Aug 27, 2024
116894c
refactor(ecdh): refactor: yarn.lock, package.json
Aug 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dprint.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"yarn.lock",
".yarn",
"packages/merkle-trees/src/globals.nr",
"packages/ecdh/src/globals.nr",
],
"plugins": [
"https://plugins.dprint.dev/json-0.19.3.wasm",
Expand Down
1 change: 1 addition & 0 deletions .husky/commit-msg
YashBit marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install commitlint --edit $1

7 changes: 7 additions & 0 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"require": "ts-node/register",
"spec": "packages/ecdh/tests/**/*.test.ts",
"timeout": 20000,
"recursive": true,
"exit": true
}
2 changes: 1 addition & 1 deletion Nargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["packages/merkle-trees"]
members = ["packages/ecdh", "packages/merkle-trees"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
## 📦 Packages

- [merkle-trees](https://github.com/privacy-scaling-explorations/zk-kit.noir/tree/main/packages/merkle-trees)
- [ecdh](https://github.com/privacy-scaling-explorations/zk-kit.noir/tree/main/packages/ecdh)

## 👥 Ways to contribute

Expand Down
23 changes: 17 additions & 6 deletions package.json
sripwoud marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"check": "dprint check",
"fmt": "dprint fmt",
"postinstall": "husky && git config --local core.editor cat",
"test": "nargo test",
"build": "nargo compile",
"test": "npm run build && mocha && nargo test --workspace",
"version:release": "changelogithub"
},
"keywords": [
Expand All @@ -25,12 +26,22 @@
],
"packageManager": "yarn@4.2.1",
"devDependencies": {
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.2",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@types/chai": "^4",
"@types/mocha": "^10",
"changelogithub": "patch:changelogithub@npm%3A0.13.3#~/.yarn/patches/changelogithub-npm-0.13.3-1783949906.patch",
"czg": "^1.9.1",
"dprint": "^0.46.3",
"husky": "^9.0.11",
"lint-staged": "^15.2.2"
}
"husky": "4",
sripwoud marked this conversation as resolved.
Show resolved Hide resolved
"lint-staged": "^15.2.2",
"ts-node": "^10.9.2",
"typescript": "^5.5.4",
"@noir-lang/backend_barretenberg": "^0.30.0",
"@noir-lang/noir_js": "^0.30.0",
"@noir-lang/noir_wasm": "^0.32.0",
"chai": "^4.5.0",
"mocha": "^10.7.0"
YashBit marked this conversation as resolved.
Show resolved Hide resolved
},
"dependencies": {}
}
5 changes: 5 additions & 0 deletions packages/ecdh/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "ecdh"
type = "bin"
YashBit marked this conversation as resolved.
Show resolved Hide resolved
authors = ["YashBit"]
compiler_version = ">=0.30.0"
37 changes: 37 additions & 0 deletions packages/ecdh/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# ECDH Library using Baby JubJub Curve

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.

## 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

To compile the circuit, navigate one directory above the source code and run the following command:

```bash
nargo compile
```


## Running Tests

To run the tests for this library, use the following command:

```bash
yarn test
```

## Running Tests with Mocha

To run the tests using Mocha with TypeScript, use the following command:

```bash
npx mocha -r ts-node/register tests/ecdh.tests.ts
```
5 changes: 5 additions & 0 deletions packages/ecdh/src/globals.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Globals Edward Curves supported Baby JubJub
use dep::std::ec::consts::te::{baby_jubjub};

global BJJ = baby_jubjub();
global G = BJJ.base8;
58 changes: 58 additions & 0 deletions packages/ecdh/src/lib.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
mod globals;
use dep::std::ec::tecurve::affine::{Curve, 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
}
45 changes: 45 additions & 0 deletions packages/ecdh/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
mod lib;
// ECDH Circuit
fn main(private_key1: [u8; 32], private_key2: [u8; 32]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit confusing that this is a lib but has a main function, can this be renamed? Something like generate_shared

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we need to have a main function and a package of type bin, don't we @signorecello?
Because we do want to compile something: we need the compilation artifact for the node test.

Or is there another way?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean we shouldn't name libraries according to their use in tests... For the node test we would actually write a bin project and compile that one

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@YashBit saw you resolved this one, but idk what's your opinion

// 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);
}
49 changes: 49 additions & 0 deletions packages/ecdh/tests/ecdh.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { BarretenbergBackend } from '@noir-lang/backend_barretenberg';
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: BarretenbergBackend;
let correctProof: ProofData;

beforeEach(async () => {
const circuitFile = readFileSync(resolve(__dirname, '../../../target/ecdh.json'), 'utf-8');
const circuit = JSON.parse(circuitFile);
backend = new BarretenbergBackend(circuit);
noir = new Noir(circuit, backend);
const pk1 = generatePrivateKey();
const pk2 = generatePrivateKey();

// Convert Uint8Array to regular arrays
const input = {
private_key1: Array.from(pk1),
private_key2: Array.from(pk2),
};

correctProof = await noir.generateProof(input);
});

it('Should generate valid proof for correct input', async function() {
this.timeout(25000); // Optional: Increase timeout if needed
// Assert that correctProof is defined and has the expected structure
expect(correctProof).to.not.be.undefined;
expect(correctProof.proof).to.be.instanceOf(Uint8Array);
});

it('Should verify valid proof for correct input', async function() {
this.timeout(25000); // Increase timeout if needed
expect(correctProof).to.not.be.undefined; // Ensure proof is generated
YashBit marked this conversation as resolved.
Show resolved Hide resolved
const verification = await noir.verifyProof(correctProof);
expect(verification).to.be.true;
});
});
43 changes: 43 additions & 0 deletions target/ecdh.json

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": ".",
"module": "commonjs",
"target": "es6",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true, // Add this line
"types": ["mocha", "node"]
},
"include": [
"./src/**/*.ts",
"./tests/**/*.ts"
],
"exclude": [
"./dist"
]
}
Loading
Loading