Skip to content

Commit

Permalink
feat: add initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Nov 30, 2024
1 parent 34dbea0 commit 514a121
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 37 deletions.
3 changes: 2 additions & 1 deletion .github/NIGHTLY_CANARY_DIED.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
title: "Tests fail on latest Nargo nightly release"
assignees: TomAFrench, kashbrti, jtriley-eth
---

The tests on this Noir project have started failing when using the latest nightly release of the Noir compiler. This likely means that there have been breaking changes for which this project needs to be updated to take into account.

Check the [{{env.WORKFLOW_NAME}}]({{env.WORKFLOW_URL}}) workflow for details.
Check the [{{env.WORKFLOW_NAME}}]({{env.WORKFLOW_URL}}) workflow for details.
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: 1.0.0-beta.0

- name: Install bb
run: |
npm install -g bbup
bbup -nv 0.34.0
bbup -nv 1.0.0-beta.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, 1.0.0-beta.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: 1.0.0-beta.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 = "schnorr"
type = "lib"
authors = [""]
compiler_version = ">=0.34.0"
compiler_version = ">=1.0.0"

[dependencies]
22 changes: 3 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
# noir-library-starter
# Schnorr

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
A Noir implementation for verification of Schnorr signatures.

## Noir version compatibility

TODO: Update Noir version numbers tested compatible with

This library is tested to work as of Noir version 1.0.0-beta.x.
This library is tested to work as of Noir version 1.0.0-beta.0.

## Benchmarks

Expand Down
158 changes: 147 additions & 11 deletions src/lib.nr
Original file line number Diff line number Diff line change
@@ -1,15 +1,151 @@
/// 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::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul};
use std::hash::{blake2s, pedersen_hash};

pub fn verify_signature<let N: u32>(
public_key: EmbeddedCurvePoint,
signature: [u8; 64],
message: [u8; N],
) -> bool {
//scalar lo/hi from bytes
let sig_s = scalar_from_bytes(signature, 0);
let sig_e = scalar_from_bytes(signature, 32);
// pub_key is on Grumpkin curve
let mut is_ok = (public_key.y * public_key.y == public_key.x * public_key.x * public_key.x - 17)
& (!public_key.is_infinite);

if ((sig_s.lo != 0) | (sig_s.hi != 0)) & ((sig_e.lo != 0) | (sig_e.hi != 0)) {
let (r_is_infinite, result) =
calculate_signature_challenge(public_key, sig_s, sig_e, message);

is_ok &= !r_is_infinite;
for i in 0..32 {
is_ok &= result[i] == signature[32 + i];
}
} else {
is_ok = false;
}
is_ok
}

pub fn assert_valid_signature<let N: u32>(
public_key: EmbeddedCurvePoint,
signature: [u8; 64],
message: [u8; N],
) {
//scalar lo/hi from bytes
let sig_s = scalar_from_bytes(signature, 0);
let sig_e = scalar_from_bytes(signature, 32);

// assert pub_key is on Grumpkin curve
assert(public_key.y * public_key.y == public_key.x * public_key.x * public_key.x - 17);
assert(public_key.is_infinite == false);
// assert signature is not null
assert((sig_s.lo != 0) | (sig_s.hi != 0));
assert((sig_e.lo != 0) | (sig_e.hi != 0));

let (r_is_infinite, result) = calculate_signature_challenge(public_key, sig_s, sig_e, message);

assert(!r_is_infinite);
for i in 0..32 {
assert(result[i] == signature[32 + i]);
}
}

fn calculate_signature_challenge<let N: u32>(
public_key: EmbeddedCurvePoint,
sig_s: EmbeddedCurveScalar,
sig_e: EmbeddedCurveScalar,
message: [u8; N],
) -> (bool, [u8; 32]) {
let g1 = EmbeddedCurvePoint {
x: 1,
y: 17631683881184975370165255887551781615748388533673675138860,
is_infinite: false,
};
let r = multi_scalar_mul([g1, public_key], [sig_s, sig_e]);
// compare the _hashes_ rather than field elements modulo r
let pedersen_hash = pedersen_hash([r.x, public_key.x, public_key.y]);
let pde: [u8; 32] = pedersen_hash.to_be_bytes();

let mut hash_input = [0; N + 32];
for i in 0..32 {
hash_input[i] = pde[i];
}
for i in 0..N {
hash_input[32 + i] = message[i];
}

let result = blake2s(hash_input);
(r.is_infinite, result)
}

// 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;
//Bytes to scalar: take the first (after the specified offset) 16 bytes of the input as the lo value, and the next 16 bytes as the hi value
fn scalar_from_bytes(bytes: [u8; 64], offset: u32) -> EmbeddedCurveScalar {
let mut v: Field = 1;
let mut lo: Field = 0;
let mut hi: Field = 0;
for i in 0..16 {
lo = lo + (bytes[offset + 31 - i] as Field) * v;
hi = hi + (bytes[offset + 15 - i] as Field) * v;
v = v * 256;
}
x
let sig_s = EmbeddedCurveScalar::new(lo, hi);
sig_s
}

mod test {
use std::embedded_curve_ops::EmbeddedCurvePoint;

use super::verify_signature;

#[test]
fn test_zero_signature() {
let public_key: EmbeddedCurvePoint = EmbeddedCurvePoint {
x: 1,
y: 17631683881184975370165255887551781615748388533673675138860,
is_infinite: false,
};
let signature: [u8; 64] = [0; 64];
let message: [u8; _] = [2; 64]; // every message
let verified = verify_signature(public_key, signature, message);
assert(!verified);
}

#[test]
fn smoke_test() {
let message: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let message_field: Field = 0x010203040506070809;
let pub_key_x: Field = 0x04b260954662e97f00cab9adb773a259097f7a274b83b113532bce27fa3fb96a;
let pub_key_y: Field = 0x2fd51571db6c08666b0edfbfbc57d432068bccd0110a39b166ab243da0037197;
let signature: [u8; 64] = [
1, 13, 119, 112, 212, 39, 233, 41, 84, 235, 255, 93, 245, 172, 186, 83, 157, 253, 76,
77, 33, 128, 178, 15, 214, 67, 105, 107, 177, 234, 77, 48, 27, 237, 155, 84, 39, 84,
247, 27, 22, 8, 176, 230, 24, 115, 145, 220, 254, 122, 135, 179, 171, 4, 214, 202, 64,
199, 19, 84, 239, 138, 124, 12,
];

// Regression for issue #2421
// We want to make sure that we can accurately verify a signature whose message is a slice vs. an array
let message_field_bytes: [u8; 10] = message_field.to_be_bytes();

// Is there ever a situation where someone would want
// to ensure that a signature was invalid?
// Check that passing a slice as the message is valid
let valid_signature = std::schnorr::verify_signature_slice(
pub_key_x,
pub_key_y,
signature,
message_field_bytes,
);
assert(valid_signature);
// Check that passing an array as the message is valid
let valid_signature =
std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message);
assert(valid_signature);
let pub_key = EmbeddedCurvePoint { x: pub_key_x, y: pub_key_y, is_infinite: false };
let valid_signature = std::schnorr::verify_signature_noir(pub_key, signature, message);
assert(valid_signature);
std::schnorr::assert_valid_signature(pub_key, signature, message);
}

}

0 comments on commit 514a121

Please sign in to comment.