From 1bdbde454fb7b016687ced6b7ee35849a778f9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C4=B0nan=C3=A7?= Date: Wed, 1 Feb 2023 15:06:38 +0300 Subject: [PATCH 1/4] add compatible hash both in Python and Cairo --- README.md | 21 +++++++++++--- scripts/test_pedersen_hash.sh | 3 ++ src/fast_pedersen_starkware.py | 53 ++++++++++++++++++++++++++++++++++ src/schnorr.cairo | 29 +++---------------- src/schnorrpy.py | 19 +++++------- tests/test_pedersen_hash.cairo | 45 +++++++++++++++++++++++++++++ tests/test_schnorr.cairo | 32 +++++++++++--------- 7 files changed, 149 insertions(+), 53 deletions(-) create mode 100755 scripts/test_pedersen_hash.sh create mode 100644 src/fast_pedersen_starkware.py create mode 100644 tests/test_pedersen_hash.cairo diff --git a/README.md b/README.md index bf890b7..fc57c63 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,22 @@ To test the verifier for the pedersen commitment, run ./scripts/test_pedersen.sh ``` +To test the verifier for Schnorr Signature, run +```bash +./scripts/test_schnorr.sh +``` + +To test the pedersen_hash function for, run +```bash +./scripts/test_pedersen_hash.sh +``` ## Next steps -- [ ] Update it to Cairo v.0.10.0 -- [ ] 64-bit messages for pedersen commitment? -- [ ] Add possible modifications on "encoding" and "decoding" -- [ ] Add Prostar when `ec_point` is supported by StarkNet. \ No newline at end of file +- [x] Update it to Cairo v.0.10.0 +- [x] Add hash functions for Schnorr prover in Python and the verifier in Cairo +- [ ] Update it to Cairo v.1.0.0 (when Elliptic Curve operations are ready for this specific version) +- [ ] Add Rust version of pedersen_commitment +- [ ] Add Rust version of Schnorr prover +- [ ] Implement Flashproofs prover and verifier in Rust +- [ ] Implement Flashproofs verifier in Cairo 1.0.0 + diff --git a/scripts/test_pedersen_hash.sh b/scripts/test_pedersen_hash.sh new file mode 100755 index 0000000..00fcbdc --- /dev/null +++ b/scripts/test_pedersen_hash.sh @@ -0,0 +1,3 @@ +cairo-compile tests/test_pedersen_hash.cairo --output test_pedersen_hash_compiled.json + +cairo-run --program=test_pedersen_hash_compiled.json --print_output --layout=all \ No newline at end of file diff --git a/src/fast_pedersen_starkware.py b/src/fast_pedersen_starkware.py new file mode 100644 index 0000000..cb6e0bf --- /dev/null +++ b/src/fast_pedersen_starkware.py @@ -0,0 +1,53 @@ +from fastecdsa.curve import Curve +from fastecdsa.point import Point + +from starkware.crypto.signature.signature import ( + ALPHA, + BETA, + CONSTANT_POINTS, + EC_ORDER, + FIELD_PRIME, + N_ELEMENT_BITS_HASH, + SHIFT_POINT, +) + +from starkware.python.utils import from_bytes, to_bytes + +curve = Curve("Curve0", FIELD_PRIME, ALPHA, BETA, EC_ORDER, *SHIFT_POINT) + +LOW_PART_BITS = 248 +LOW_PART_MASK = 2**248 - 1 +HASH_SHIFT_POINT = Point(*SHIFT_POINT, curve=curve) +P_0 = Point(*CONSTANT_POINTS[2], curve=curve) +P_1 = Point(*CONSTANT_POINTS[2 + LOW_PART_BITS], curve=curve) +P_2 = Point(*CONSTANT_POINTS[2 + N_ELEMENT_BITS_HASH], curve=curve) +P_3 = Point(*CONSTANT_POINTS[2 + N_ELEMENT_BITS_HASH + LOW_PART_BITS], curve=curve) + + +def process_single_element(element: int, p1, p2) -> Point: + assert 0 <= element < FIELD_PRIME, "Element integer value is out of range" + + high_nibble = element >> LOW_PART_BITS + low_part = element & LOW_PART_MASK + return low_part * p1 + high_nibble * p2 + + +def pedersen_hash(x: int, y: int) -> int: + """ + Computes the Starkware version of the Pedersen hash of x and y. + The hash is defined by: + shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3 + where x_low is the 248 low bits of x, x_high is the 4 high bits of x and similarly for y. + shift_point, P_0, P_1, P_2, P_3 are constant points generated from the digits of pi. + """ + return ( + HASH_SHIFT_POINT + process_single_element(x, P_0, P_1) + process_single_element(y, P_2, P_3) + ).x + + +def pedersen_hash_func(x: bytes, y: bytes) -> bytes: + """ + A variant of 'pedersen_hash', where the elements and their resulting hash are in bytes. + """ + assert len(x) == len(y) == 32, "Unexpected element length." + return to_bytes(pedersen_hash(*(from_bytes(element) for element in (x, y)))) \ No newline at end of file diff --git a/src/schnorr.cairo b/src/schnorr.cairo index a51f9bb..eedc08d 100644 --- a/src/schnorr.cairo +++ b/src/schnorr.cairo @@ -8,9 +8,11 @@ from src.constants import BASE_POINT_X, BASE_POINT_Y from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.uint256 import Uint256, uint256_unsigned_div_rem from starkware.cairo.common.serialize import serialize_word +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.hash import hash2 -func verify_schnorr_signature{output_ptr : felt*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint, _challenge : felt){ +func verify_schnorr_signature{output_ptr : felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint){ alloc_locals; assert_on_curve(alpha_G); @@ -18,36 +20,13 @@ func verify_schnorr_signature{output_ptr : felt*, range_check_ptr, bitwise_ptr : local G: EcPoint = EcPoint(BASE_POINT_X, BASE_POINT_Y); assert_on_curve(G); - // challenge needs to be hashed! So don't use this function for the privacy reason for now! - // In other versions, hash function will be added for hashing the challenge - //let (keccak_ptr : felt*) = alloc(); - //local keccak_ptr_start : felt* = keccak_ptr; - - //let (local keccak_inputs : felt*) = alloc(); - //assert keccak_inputs[0] = alpha_G.x; - //assert keccak_inputs[1] = alpha_G.y; - - //with keccak_ptr{ - //let (hashed) = keccak_felts(2, keccak_inputs); - //} - - - // Precompiled Q, which is equal to Q = 3618502788666131213697322783095070105526743751716087489154079457884512865583 for Starkcurve. - //local Q : Uint256 = Uint256(low = 243918903305429252644362009180409056559, high = 10633823966279327296825105735305134079); - - //let (_, r) = uint256_unsigned_div_rem(a = hashed, div = Q); - - //tempvar _challenge : felt = 1; - + let (_challenge) = hash2{hash_ptr=pedersen_ptr}(alpha_G.x, alpha_G.y); let (R) = ec_mul(G, response); let (c_k) = ec_mul(public_key, _challenge); let (R_) = ec_add(alpha_G, c_k); - // It doesn't give a correct result since the result of keccak_felts in Cairo doesn't match with the result of Web3.SolidityKeccak assert R = R_; - //finalize_keccak(keccak_ptr_start = keccak_ptr_start, keccak_ptr_end = keccak_ptr); - return(); } \ No newline at end of file diff --git a/src/schnorrpy.py b/src/schnorrpy.py index dc6993a..ad68af3 100644 --- a/src/schnorrpy.py +++ b/src/schnorrpy.py @@ -2,34 +2,31 @@ from src.starkcurve import _STARKCURVE, G, Q from fastecdsa.point import Point from web3 import Web3 +from src.fast_pedersen_starkware import * # Random number is generated without considering the security, maybe changed later from Crypto.Random import random from Crypto.Util import number class SchnorrSignature: - def prove(self, secret, c): + def prove(self, secret): alpha = number.getRandomRange(1, _STARKCURVE.q - 1) alpha_G = alpha * G - #x = alpha_G.x - #y = alpha_G.y + x = alpha_G.x + y = alpha_G.y + + c = pedersen_hash(x, y) - # challenge needs to be hashed! So don't use this function for the privacy reason for now! - # In other versions, hash function will be added for hashing the challenge - #c = 1 - response = (alpha + c * secret) % Q public_key = secret * G return alpha_G, response, public_key # off-chain verification for testing - def verify(self, alpha_G, response, public_key, challenge): - # challenge needs to be hashed! So don't use this function for the privacy reason for now! - # In other versions, hash function will be added for hashing the challenge - + def verify(self, alpha_G, response, public_key): x = alpha_G.x y = alpha_G.y + challenge = pedersen_hash(x, y) _R = response * G _Rprime = alpha_G + challenge * public_key assert _R == _Rprime \ No newline at end of file diff --git a/tests/test_pedersen_hash.cairo b/tests/test_pedersen_hash.cairo new file mode 100644 index 0000000..b47a204 --- /dev/null +++ b/tests/test_pedersen_hash.cairo @@ -0,0 +1,45 @@ +%builtins output pedersen + +from starkware.cairo.common.ec_point import EcPoint +from starkware.cairo.common.ec import assert_on_curve, ec_add, ec_double, ec_op +from starkware.cairo.common.cairo_builtins import EcOpBuiltin +from starkware.cairo.common.serialize import serialize_word +from src.math_utils import ec_mul +from src.pedersen_commitment_verifier import verify_pedersen_commitment +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.hash import hash2 +from src.constants import BASE_POINT_X, BASE_POINT_Y, BASE_BLINDING_POINT_X, BASE_BLINDING_POINT_Y + + +func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*}() { + alloc_locals; + serialize_word(2); + let x: felt = BASE_POINT_X; + let y: felt = BASE_POINT_Y; + let (res) = hash2{hash_ptr=pedersen_ptr}(x, y); + tempvar a: felt; + + %{ + import sys, os + cwd = os.getcwd() + sys.path.append(cwd) + + from src.fast_pedersen_starkware import * + #from src.schnorr import SchnorrSignature + base1 = ids.x + base2 = ids.y + + resulting = pedersen_hash(base1, base2) + ids.a = resulting + %} + + assert a = res; + + serialize_word(a); + serialize_word(res); + return(); +} + + + diff --git a/tests/test_schnorr.cairo b/tests/test_schnorr.cairo index 89c0197..cbd020d 100644 --- a/tests/test_schnorr.cairo +++ b/tests/test_schnorr.cairo @@ -1,4 +1,4 @@ -%builtins output range_check bitwise ec_op +%builtins output pedersen range_check bitwise ec_op from starkware.cairo.common.ec_point import EcPoint from starkware.cairo.common.ec import assert_on_curve, ec_add, ec_double, ec_op @@ -7,8 +7,10 @@ from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.serialize import serialize_word from src.math_utils import ec_mul from src.schnorr import verify_schnorr_signature +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.hash import hash2 -func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { +func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { alloc_locals; local message: felt; @@ -26,19 +28,23 @@ func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_o from src.schnorrpy import SchnorrSignature - zz = SchnorrSignature() - # For testing purposes, challenge is taken as 1. - # After adding a suitable hash function for both cairo and python int the future versions, schnorr prover and verifier can be used. - (aa,ab,ac) = zz.prove(13, 1) - - ids.alpha_G.x = aa.x - ids.alpha_G.y = aa.y - ids.response = ab - ids.public_key.x = ac.x - ids.public_key.y = ac.y + secret = 12 + schnorr = SchnorrSignature() + (alpha, response, pk) = schnorr.prove(secret) + print("Off-chain proof sent") + schnorr.verify(alpha, response, pk) + print("Off-chain verification done") + print("Assertting on-chain verification") + print("If there is no error, on-chain verification is completed") + + ids.alpha_G.x = alpha.x + ids.alpha_G.y = alpha.y + ids.response = response + ids.public_key.x = pk.x + ids.public_key.y = pk.y %} - verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key, _challenge = 1); + verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key); return (); } \ No newline at end of file From 5f5a8fb45a798c45c649d876c720abe7c57db358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C4=B0nan=C3=A7?= Date: Tue, 10 Jan 2023 17:06:37 +0300 Subject: [PATCH 2/4] add final version for Cairo 0.10.0 --- src/schnorr.cairo | 38 ++++++++++++++++++++++++++++++++++++++ src/schnorrpy.py | 26 ++++++++++++++++++++++++++ tests/test_schnorr.cairo | 25 +++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/src/schnorr.cairo b/src/schnorr.cairo index eedc08d..247d353 100644 --- a/src/schnorr.cairo +++ b/src/schnorr.cairo @@ -8,11 +8,17 @@ from src.constants import BASE_POINT_X, BASE_POINT_Y from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.uint256 import Uint256, uint256_unsigned_div_rem from starkware.cairo.common.serialize import serialize_word +<<<<<<< HEAD from starkware.cairo.common.cairo_builtins import HashBuiltin from starkware.cairo.common.hash import hash2 func verify_schnorr_signature{output_ptr : felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint){ +======= + + +func verify_schnorr_signature{output_ptr : felt*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint, _challenge : felt){ +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) alloc_locals; assert_on_curve(alpha_G); @@ -20,13 +26,45 @@ func verify_schnorr_signature{output_ptr : felt*, pedersen_ptr: HashBuiltin*, ra local G: EcPoint = EcPoint(BASE_POINT_X, BASE_POINT_Y); assert_on_curve(G); +<<<<<<< HEAD let (_challenge) = hash2{hash_ptr=pedersen_ptr}(alpha_G.x, alpha_G.y); +======= + // challenge needs to be hashed! So don't use this function for the privacy reason for now! + // In other versions, hash function will be added for hashing the challenge + //let (keccak_ptr : felt*) = alloc(); + //local keccak_ptr_start : felt* = keccak_ptr; + + //let (local keccak_inputs : felt*) = alloc(); + //assert keccak_inputs[0] = alpha_G.x; + //assert keccak_inputs[1] = alpha_G.y; + + //with keccak_ptr{ + //let (hashed) = keccak_felts(2, keccak_inputs); + //} + + + // Precompiled Q, which is equal to Q = 3618502788666131213697322783095070105526743751716087489154079457884512865583 for Starkcurve. + //local Q : Uint256 = Uint256(low = 243918903305429252644362009180409056559, high = 10633823966279327296825105735305134079); + + //let (_, r) = uint256_unsigned_div_rem(a = hashed, div = Q); + + //tempvar _challenge : felt = 1; + +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) let (R) = ec_mul(G, response); let (c_k) = ec_mul(public_key, _challenge); let (R_) = ec_add(alpha_G, c_k); +<<<<<<< HEAD assert R = R_; +======= + // It doesn't give a correct result since the result of keccak_felts in Cairo doesn't match with the result of Web3.SolidityKeccak + assert R = R_; + + //finalize_keccak(keccak_ptr_start = keccak_ptr_start, keccak_ptr_end = keccak_ptr); + +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) return(); } \ No newline at end of file diff --git a/src/schnorrpy.py b/src/schnorrpy.py index ad68af3..6c1f0fd 100644 --- a/src/schnorrpy.py +++ b/src/schnorrpy.py @@ -2,13 +2,17 @@ from src.starkcurve import _STARKCURVE, G, Q from fastecdsa.point import Point from web3 import Web3 +<<<<<<< HEAD from src.fast_pedersen_starkware import * +======= +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) # Random number is generated without considering the security, maybe changed later from Crypto.Random import random from Crypto.Util import number class SchnorrSignature: +<<<<<<< HEAD def prove(self, secret): alpha = number.getRandomRange(1, _STARKCURVE.q - 1) @@ -18,15 +22,37 @@ def prove(self, secret): c = pedersen_hash(x, y) +======= + def prove(self, secret, c): + alpha = number.getRandomRange(1, _STARKCURVE.q - 1) + + alpha_G = alpha * G + #x = alpha_G.x + #y = alpha_G.y + + # challenge needs to be hashed! So don't use this function for the privacy reason for now! + # In other versions, hash function will be added for hashing the challenge + #c = 1 + +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) response = (alpha + c * secret) % Q public_key = secret * G return alpha_G, response, public_key # off-chain verification for testing +<<<<<<< HEAD def verify(self, alpha_G, response, public_key): x = alpha_G.x y = alpha_G.y challenge = pedersen_hash(x, y) +======= + def verify(self, alpha_G, response, public_key, challenge): + # challenge needs to be hashed! So don't use this function for the privacy reason for now! + # In other versions, hash function will be added for hashing the challenge + + x = alpha_G.x + y = alpha_G.y +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) _R = response * G _Rprime = alpha_G + challenge * public_key assert _R == _Rprime \ No newline at end of file diff --git a/tests/test_schnorr.cairo b/tests/test_schnorr.cairo index cbd020d..39d7580 100644 --- a/tests/test_schnorr.cairo +++ b/tests/test_schnorr.cairo @@ -1,4 +1,8 @@ +<<<<<<< HEAD %builtins output pedersen range_check bitwise ec_op +======= +%builtins output range_check bitwise ec_op +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) from starkware.cairo.common.ec_point import EcPoint from starkware.cairo.common.ec import assert_on_curve, ec_add, ec_double, ec_op @@ -7,10 +11,15 @@ from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.serialize import serialize_word from src.math_utils import ec_mul from src.schnorr import verify_schnorr_signature +<<<<<<< HEAD from starkware.cairo.common.cairo_builtins import HashBuiltin from starkware.cairo.common.hash import hash2 func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { +======= + +func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) alloc_locals; local message: felt; @@ -28,6 +37,7 @@ func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwis from src.schnorrpy import SchnorrSignature +<<<<<<< HEAD secret = 12 schnorr = SchnorrSignature() (alpha, response, pk) = schnorr.prove(secret) @@ -45,6 +55,21 @@ func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwis %} verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key); +======= + zz = SchnorrSignature() + # For testing purposes, challenge is taken as 1. + # After adding a suitable hash function for both cairo and python int the future versions, schnorr prover and verifier can be used. + (aa,ab,ac) = zz.prove(13, 1) + + ids.alpha_G.x = aa.x + ids.alpha_G.y = aa.y + ids.response = ab + ids.public_key.x = ac.x + ids.public_key.y = ac.y + %} + + verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key, _challenge = 1); +>>>>>>> a6cc721 (add final version for Cairo 0.10.0) return (); } \ No newline at end of file From 77220970a766e8a77e83ab9cd344afbd69c04f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C4=B0nan=C3=A7?= Date: Thu, 2 Feb 2023 14:38:20 +0300 Subject: [PATCH 3/4] schnorr modified --- src/schnorr.cairo | 38 -------------------------------------- src/schnorrpy.py | 26 -------------------------- tests/test_schnorr.cairo | 25 ------------------------- 3 files changed, 89 deletions(-) diff --git a/src/schnorr.cairo b/src/schnorr.cairo index 247d353..eedc08d 100644 --- a/src/schnorr.cairo +++ b/src/schnorr.cairo @@ -8,17 +8,11 @@ from src.constants import BASE_POINT_X, BASE_POINT_Y from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.uint256 import Uint256, uint256_unsigned_div_rem from starkware.cairo.common.serialize import serialize_word -<<<<<<< HEAD from starkware.cairo.common.cairo_builtins import HashBuiltin from starkware.cairo.common.hash import hash2 func verify_schnorr_signature{output_ptr : felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint){ -======= - - -func verify_schnorr_signature{output_ptr : felt*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint, _challenge : felt){ ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) alloc_locals; assert_on_curve(alpha_G); @@ -26,45 +20,13 @@ func verify_schnorr_signature{output_ptr : felt*, range_check_ptr, bitwise_ptr : local G: EcPoint = EcPoint(BASE_POINT_X, BASE_POINT_Y); assert_on_curve(G); -<<<<<<< HEAD let (_challenge) = hash2{hash_ptr=pedersen_ptr}(alpha_G.x, alpha_G.y); -======= - // challenge needs to be hashed! So don't use this function for the privacy reason for now! - // In other versions, hash function will be added for hashing the challenge - //let (keccak_ptr : felt*) = alloc(); - //local keccak_ptr_start : felt* = keccak_ptr; - - //let (local keccak_inputs : felt*) = alloc(); - //assert keccak_inputs[0] = alpha_G.x; - //assert keccak_inputs[1] = alpha_G.y; - - //with keccak_ptr{ - //let (hashed) = keccak_felts(2, keccak_inputs); - //} - - - // Precompiled Q, which is equal to Q = 3618502788666131213697322783095070105526743751716087489154079457884512865583 for Starkcurve. - //local Q : Uint256 = Uint256(low = 243918903305429252644362009180409056559, high = 10633823966279327296825105735305134079); - - //let (_, r) = uint256_unsigned_div_rem(a = hashed, div = Q); - - //tempvar _challenge : felt = 1; - ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) let (R) = ec_mul(G, response); let (c_k) = ec_mul(public_key, _challenge); let (R_) = ec_add(alpha_G, c_k); -<<<<<<< HEAD assert R = R_; -======= - // It doesn't give a correct result since the result of keccak_felts in Cairo doesn't match with the result of Web3.SolidityKeccak - assert R = R_; - - //finalize_keccak(keccak_ptr_start = keccak_ptr_start, keccak_ptr_end = keccak_ptr); - ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) return(); } \ No newline at end of file diff --git a/src/schnorrpy.py b/src/schnorrpy.py index 6c1f0fd..ad68af3 100644 --- a/src/schnorrpy.py +++ b/src/schnorrpy.py @@ -2,17 +2,13 @@ from src.starkcurve import _STARKCURVE, G, Q from fastecdsa.point import Point from web3 import Web3 -<<<<<<< HEAD from src.fast_pedersen_starkware import * -======= ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) # Random number is generated without considering the security, maybe changed later from Crypto.Random import random from Crypto.Util import number class SchnorrSignature: -<<<<<<< HEAD def prove(self, secret): alpha = number.getRandomRange(1, _STARKCURVE.q - 1) @@ -22,37 +18,15 @@ def prove(self, secret): c = pedersen_hash(x, y) -======= - def prove(self, secret, c): - alpha = number.getRandomRange(1, _STARKCURVE.q - 1) - - alpha_G = alpha * G - #x = alpha_G.x - #y = alpha_G.y - - # challenge needs to be hashed! So don't use this function for the privacy reason for now! - # In other versions, hash function will be added for hashing the challenge - #c = 1 - ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) response = (alpha + c * secret) % Q public_key = secret * G return alpha_G, response, public_key # off-chain verification for testing -<<<<<<< HEAD def verify(self, alpha_G, response, public_key): x = alpha_G.x y = alpha_G.y challenge = pedersen_hash(x, y) -======= - def verify(self, alpha_G, response, public_key, challenge): - # challenge needs to be hashed! So don't use this function for the privacy reason for now! - # In other versions, hash function will be added for hashing the challenge - - x = alpha_G.x - y = alpha_G.y ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) _R = response * G _Rprime = alpha_G + challenge * public_key assert _R == _Rprime \ No newline at end of file diff --git a/tests/test_schnorr.cairo b/tests/test_schnorr.cairo index 39d7580..cbd020d 100644 --- a/tests/test_schnorr.cairo +++ b/tests/test_schnorr.cairo @@ -1,8 +1,4 @@ -<<<<<<< HEAD %builtins output pedersen range_check bitwise ec_op -======= -%builtins output range_check bitwise ec_op ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) from starkware.cairo.common.ec_point import EcPoint from starkware.cairo.common.ec import assert_on_curve, ec_add, ec_double, ec_op @@ -11,15 +7,10 @@ from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.serialize import serialize_word from src.math_utils import ec_mul from src.schnorr import verify_schnorr_signature -<<<<<<< HEAD from starkware.cairo.common.cairo_builtins import HashBuiltin from starkware.cairo.common.hash import hash2 func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { -======= - -func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) alloc_locals; local message: felt; @@ -37,7 +28,6 @@ func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_o from src.schnorrpy import SchnorrSignature -<<<<<<< HEAD secret = 12 schnorr = SchnorrSignature() (alpha, response, pk) = schnorr.prove(secret) @@ -55,21 +45,6 @@ func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_o %} verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key); -======= - zz = SchnorrSignature() - # For testing purposes, challenge is taken as 1. - # After adding a suitable hash function for both cairo and python int the future versions, schnorr prover and verifier can be used. - (aa,ab,ac) = zz.prove(13, 1) - - ids.alpha_G.x = aa.x - ids.alpha_G.y = aa.y - ids.response = ab - ids.public_key.x = ac.x - ids.public_key.y = ac.y - %} - - verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key, _challenge = 1); ->>>>>>> a6cc721 (add final version for Cairo 0.10.0) return (); } \ No newline at end of file From 9712e053371c5317c9e45a600e68476b96146485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C4=B0nan=C3=A7?= Date: Thu, 2 Feb 2023 14:48:19 +0300 Subject: [PATCH 4/4] add flashproofs, rebase schnorr.cairo --- .gitignore | 7 +- scripts/test_flashproofs_cairo.sh | 3 + src/flashproofs.cairo | 267 ++++++++++++++++++++++++++++++ src/schnorr.cairo | 29 +++- src/schnorrpy.py | 19 ++- tests/test_schnorr.cairo | 32 ++-- 6 files changed, 320 insertions(+), 37 deletions(-) create mode 100644 scripts/test_flashproofs_cairo.sh create mode 100644 src/flashproofs.cairo diff --git a/.gitignore b/.gitignore index 7c476d2..745b4b0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,4 @@ Cargo.lock **/*.rs.bk ./tests/test_keccak.cairo -./scripts/test_keccak.sh -./scripts/test_flashproofs_cairo.sh -./src/flashproofs.cairo - - - +./scripts/test_keccak.sh \ No newline at end of file diff --git a/scripts/test_flashproofs_cairo.sh b/scripts/test_flashproofs_cairo.sh new file mode 100644 index 0000000..6cf0e10 --- /dev/null +++ b/scripts/test_flashproofs_cairo.sh @@ -0,0 +1,3 @@ +cairo-compile src/flashproofs.cairo --output flashproofs_compiled.json + +cairo-run --program=flashproofs_compiled.json --print_output --layout=all \ No newline at end of file diff --git a/src/flashproofs.cairo b/src/flashproofs.cairo new file mode 100644 index 0000000..23f5015 --- /dev/null +++ b/src/flashproofs.cairo @@ -0,0 +1,267 @@ +// BASE_POINT is the generator point used in the ECDSA scheme +// https://docs.starkware.co/starkex-v4/crypto/stark-curve + +// To generate BASE_BLINDING_POINT, a cryptographic random number is generated +// BASE_BLINDING_POINT is the result of elliptic curve scalar multiplication of +// "cryptographic number" and "BASE_POINT", which the operation is done as offline + +// Note that the generated number is less than the order of the starkcurve: +// 3618502788666131213697322783095070105526743751716087489154079457884512865583 +// The order of the elliptic curve is found thanks to: +// https://crypto.stackexchange.com/questions/95666/how-to-find-out-what-the-order-of-the-base-point-of-the-elliptic-curve-is + +%builtins output range_check bitwise + +from starkware.cairo.common.ec_point import EcPoint +from starkware.cairo.common.ec import assert_on_curve, ec_add, ec_double, ec_op +from starkware.cairo.common.cairo_builtins import EcOpBuiltin +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from src.math_utils import ec_mul, mul_mod_Q +from starkware.cairo.common.serialize import serialize_word +from starkware.cairo.common.cairo_keccak.keccak import keccak_felts, finalize_keccak +from starkware.cairo.common.alloc import alloc +from src.constants import BASE_POINT_X, BASE_POINT_Y, BASE_BLINDING_POINT_X, BASE_BLINDING_POINT_Y + +struct MyStruct { + first_member: felt, + second_member: MyStruct*, +} + + +struct Part1 { + cy: EcPoint, + cts: EcPoint*, + css: EcPoint*, + cqs: EcPoint*, + eInvs: felt*, + cts_size : felt, + css_size : felt, + cqs_size : felt, + eInvs_size : felt, +} + + +struct Part2 { + vs: felt*, + u: felt, + epsilon: felt, +} + +// Bu aşağıdaki G ve H noktasını değiştiricez. Bunlar büyük ihtimalle generator evet +// bunlar bn curve generatorları -> biz bunları kendi generatorlarımız ile değiştiricez +// GX, GY, HX, HY Starkcurve üzerinde generator noktalarımız var + +// const AA +// const BB +// const PP +// const NN + +// There are exactly 16 points from G1X, G1Y to ---> G16X, G16Y + +func verifyRangeArgument(part1: Part1, part2: Part2, nbits: felt, k: felt, L: felt) -> (res : felt){ + + return(res=1); +} + +func generateGS(L : felt, gs_temp: EcPoint*) -> (gs : EcPoint*){ + assert gs_temp[0] = EcPoint(x=1, y=2); + assert gs_temp[1] = EcPoint(x=5, y=3); + assert gs_temp[2] = EcPoint(x=1, y=2); + assert gs_temp[3] = EcPoint(x=5, y=3); + assert gs_temp[4] = EcPoint(x=1, y=2); + assert gs_temp[5] = EcPoint(x=5, y=3); + assert gs_temp[6] = EcPoint(x=1, y=2); + assert gs_temp[7] = EcPoint(x=5, y=3); + assert gs_temp[8] = EcPoint(x=1, y=2); + assert gs_temp[9] = EcPoint(x=5, y=3); + assert gs_temp[10] = EcPoint(x=1, y=2); + + if(L == 16){ + assert gs_temp[11] = EcPoint(x=1, y=2); + assert gs_temp[12] = EcPoint(x=5, y=3); + assert gs_temp[13] = EcPoint(x=1, y=2); + assert gs_temp[14] = EcPoint(x=5, y=3); + assert gs_temp[15] = EcPoint(x=1, y=2); + } + + return(gs = gs_temp); +} + + +// mod NN'de keccak fonksiyonu kullanıyor. +// anonymous_zether'da kullanılan group order ile aynı order +// BN_128 curve'ün orderı ve de parametrelerini kullanmışlar. +// Kısacası, group orderdan küçük bir sayıda olan bir tane felt dönecek şekilde implement +// edebiliriz. +// mod fonksiyonu kullanıyor. +//compute_challenges ilçersinde mul_mod isimli bir fonksiyon var + +func computeBaseChallenge{output_ptr : felt*, range_check_ptr, + bitwise_ptr : BitwiseBuiltin*}(cy: EcPoint, + cts_length: felt, cts: EcPoint*, + css_length: felt, css: EcPoint*, + cqs_length: felt, cqs: EcPoint*) -> (challenge : felt){ + alloc_locals; + local size = 2 * (cts_length + css_length + cqs_length); + let (local cs_temp : felt*) = alloc(); + let (cs) = fill_cs(src = cts, cs = cs_temp, length = cts_length); + let new_cs : felt* = cs + cts_length*2; + let (cs) = fill_cs(src = css, cs = new_cs, length = css_length); + let new_cs : felt* = cs + css_length*2; + let (cs) = fill_cs(src = cqs, cs = new_cs, length = cqs_length); + let new_cs : felt* = cs - cts_length*2 - css_length*2; + // Constructing a felt* that keccak functions will use inside + // Use new_cs in keccak_felts function. + //serialize_word(new_cs[0]); + //serialize_word(new_cs[1]); + //serialize_word(new_cs[2]); + //serialize_word(new_cs[3]); + //serialize_word(new_cs[4]); + //serialize_word(new_cs[5]); + //serialize_word(new_cs[6]); + //serialize_word(new_cs[7]); + //serialize_word(new_cs[8]); + //serialize_word(new_cs[9]); + //serialize_word(new_cs[10]); + //serialize_word(new_cs[11]); + let (keccak_ptr : felt*) = alloc(); + local keccak_ptr_start: felt* = keccak_ptr; + with keccak_ptr{ + let (hashed) = keccak_felts(size, new_cs); + } + // hashed is Uint256. So, we need a function which + // takes the mod of Uint256 with % starkcurve.Q and returns + // corresponding felt + finalize_keccak(keccak_ptr_start = keccak_ptr_start, keccak_ptr_end = keccak_ptr); + return(challenge = 5); +} + +func computeChallenges{output_ptr : felt*, range_check_ptr, + bitwise_ptr : BitwiseBuiltin*}(part1 : Part1, k : felt) + -> (){ + + // -> challenges : felt* + alloc_locals; + + local challengeNeg1 = part1.eInvs[0]; + let (challenge) = computeBaseChallenge(part1.cy, + part1.cts_size, part1.cts, + part1.css_size, part1.css, + part1.cqs_size, part1.cqs); + + + let (res) = mul_mod_Q(challenge, challengeNeg1); + serialize_word(res); + + return(); + + +} + +func fill_arr(src: felt*, dest: felt*, length: felt) -> (dest: felt*){ + if (length == 0){ + return(dest=dest); + } + assert dest[0] = src[0]; + fill_arr(src = src + 1, dest = dest + 1, length = length - 1); + return(dest=dest); +} + +func fill_cs(src: EcPoint*, cs: felt*, length: felt) -> (cs: felt*){ + if (length == 0){ + return(cs=cs); + } + assert cs[0] = src[0].x; + assert cs[1] = src[0].y; + fill_cs(src = src + EcPoint.SIZE, cs = cs + EcPoint.SIZE, length = length - 1); + return(cs=cs); +} +// Test for constructing gs +//func main{output_ptr : felt*}(){ +// alloc_locals; +// let (local gs_temp: EcPoint*) = alloc(); +// //assert gs[0] = EcPoint(x=1, y=2); +// //assert gs[1] = EcPoint(x=5, y=3); +// //tempvar x = 5; +// //serialize_word(x); +// //serialize_word(gs[0].x); +// //serialize_word(gs[0].y); +// //serialize_word(gs[1].x); +// //serialize_word(gs[1].y); +// +// let (gs) = generateGS(L=11, gs_temp = gs_temp); +// serialize_word(gs[0].x); +// serialize_word(gs[10].x); +// serialize_word(gs[9].x); +// +// +// return(); +//} +// Test for array concat +//func main{output_ptr: felt*}(){ +// // Concating arrays +// alloc_locals; +// let (local dest_temp : felt*) = alloc(); +// let (local src : felt*) = alloc(); +// assert src[0] = 1; +// assert src[1] = 2; +// assert src[2] = 3; +// let (dest) = fill_arr(src = src, dest = dest_temp, length = 3); +// let new_dest : felt* = dest + 3; +// let (dest) = fill_arr(src = src, dest = new_dest, length = 3); +// let new_dest : felt* = dest - 3; +// serialize_word(new_dest[0]); +// serialize_word(new_dest[1]); +// serialize_word(new_dest[2]); +// serialize_word(new_dest[3]); +// serialize_word(new_dest[4]); +// serialize_word(new_dest[5]); +// +// return(); +// +//} +// Test for computeBaseChallenge function +//func main{output_ptr : felt*, range_check_ptr, +// bitwise_ptr : BitwiseBuiltin*}(){ +// alloc_locals; +// let (local src : EcPoint*) = alloc(); +// let (local src1 : EcPoint*) = alloc(); +// let (local src2 : EcPoint*) = alloc(); +// local cy : EcPoint = EcPoint(7474, 84848); +// assert src[0] = EcPoint(2, 3); +// assert src[1] = EcPoint(5, 7); +// assert src1[0] = EcPoint(1, 1); +// assert src2[0] = EcPoint(4, 8); +// assert src2[1] = EcPoint(1234, 356); +// assert src2[2] = EcPoint(6, 35); +// let (x) = computeBaseChallenge(cy, 2, src, 1, src1, 3, src2); +// return(); +//} + +// Test for computeChallenges +func main{output_ptr : felt*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*}(){ + alloc_locals; + + let (local cts : EcPoint*) = alloc(); + let (local css : EcPoint*) = alloc(); + let (local cqs : EcPoint*) = alloc(); + local cy : EcPoint = EcPoint(7474, 84848); + let (local eInvs : felt*) = alloc(); + + assert cts[0] = EcPoint(2, 3); + assert cts[1] = EcPoint(5, 7); + assert css[0] = EcPoint(1, 1); + assert cqs[0] = EcPoint(4, 8); + assert cqs[1] = EcPoint(1234, 356); + assert cqs[2] = EcPoint(6, 35); + assert eInvs[0] = 1; + assert eInvs[1] = 2; + + local part1 : Part1 = Part1(cy, cts, css, cqs, eInvs, 2, 1, 3, 2); + local k = 8; + + computeChallenges(part1 = part1, k = k); + + + return(); +} \ No newline at end of file diff --git a/src/schnorr.cairo b/src/schnorr.cairo index eedc08d..a51f9bb 100644 --- a/src/schnorr.cairo +++ b/src/schnorr.cairo @@ -8,11 +8,9 @@ from src.constants import BASE_POINT_X, BASE_POINT_Y from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.uint256 import Uint256, uint256_unsigned_div_rem from starkware.cairo.common.serialize import serialize_word -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.hash import hash2 -func verify_schnorr_signature{output_ptr : felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint){ +func verify_schnorr_signature{output_ptr : felt*, range_check_ptr, bitwise_ptr : BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}(alpha_G : EcPoint, response : felt, public_key : EcPoint, _challenge : felt){ alloc_locals; assert_on_curve(alpha_G); @@ -20,13 +18,36 @@ func verify_schnorr_signature{output_ptr : felt*, pedersen_ptr: HashBuiltin*, ra local G: EcPoint = EcPoint(BASE_POINT_X, BASE_POINT_Y); assert_on_curve(G); - let (_challenge) = hash2{hash_ptr=pedersen_ptr}(alpha_G.x, alpha_G.y); + // challenge needs to be hashed! So don't use this function for the privacy reason for now! + // In other versions, hash function will be added for hashing the challenge + //let (keccak_ptr : felt*) = alloc(); + //local keccak_ptr_start : felt* = keccak_ptr; + + //let (local keccak_inputs : felt*) = alloc(); + //assert keccak_inputs[0] = alpha_G.x; + //assert keccak_inputs[1] = alpha_G.y; + + //with keccak_ptr{ + //let (hashed) = keccak_felts(2, keccak_inputs); + //} + + + // Precompiled Q, which is equal to Q = 3618502788666131213697322783095070105526743751716087489154079457884512865583 for Starkcurve. + //local Q : Uint256 = Uint256(low = 243918903305429252644362009180409056559, high = 10633823966279327296825105735305134079); + + //let (_, r) = uint256_unsigned_div_rem(a = hashed, div = Q); + + //tempvar _challenge : felt = 1; + let (R) = ec_mul(G, response); let (c_k) = ec_mul(public_key, _challenge); let (R_) = ec_add(alpha_G, c_k); + // It doesn't give a correct result since the result of keccak_felts in Cairo doesn't match with the result of Web3.SolidityKeccak assert R = R_; + //finalize_keccak(keccak_ptr_start = keccak_ptr_start, keccak_ptr_end = keccak_ptr); + return(); } \ No newline at end of file diff --git a/src/schnorrpy.py b/src/schnorrpy.py index ad68af3..dc6993a 100644 --- a/src/schnorrpy.py +++ b/src/schnorrpy.py @@ -2,31 +2,34 @@ from src.starkcurve import _STARKCURVE, G, Q from fastecdsa.point import Point from web3 import Web3 -from src.fast_pedersen_starkware import * # Random number is generated without considering the security, maybe changed later from Crypto.Random import random from Crypto.Util import number class SchnorrSignature: - def prove(self, secret): + def prove(self, secret, c): alpha = number.getRandomRange(1, _STARKCURVE.q - 1) alpha_G = alpha * G - x = alpha_G.x - y = alpha_G.y - - c = pedersen_hash(x, y) + #x = alpha_G.x + #y = alpha_G.y + # challenge needs to be hashed! So don't use this function for the privacy reason for now! + # In other versions, hash function will be added for hashing the challenge + #c = 1 + response = (alpha + c * secret) % Q public_key = secret * G return alpha_G, response, public_key # off-chain verification for testing - def verify(self, alpha_G, response, public_key): + def verify(self, alpha_G, response, public_key, challenge): + # challenge needs to be hashed! So don't use this function for the privacy reason for now! + # In other versions, hash function will be added for hashing the challenge + x = alpha_G.x y = alpha_G.y - challenge = pedersen_hash(x, y) _R = response * G _Rprime = alpha_G + challenge * public_key assert _R == _Rprime \ No newline at end of file diff --git a/tests/test_schnorr.cairo b/tests/test_schnorr.cairo index cbd020d..89c0197 100644 --- a/tests/test_schnorr.cairo +++ b/tests/test_schnorr.cairo @@ -1,4 +1,4 @@ -%builtins output pedersen range_check bitwise ec_op +%builtins output range_check bitwise ec_op from starkware.cairo.common.ec_point import EcPoint from starkware.cairo.common.ec import assert_on_curve, ec_add, ec_double, ec_op @@ -7,10 +7,8 @@ from starkware.cairo.common.cairo_builtins import BitwiseBuiltin from starkware.cairo.common.serialize import serialize_word from src.math_utils import ec_mul from src.schnorr import verify_schnorr_signature -from starkware.cairo.common.cairo_builtins import HashBuiltin -from starkware.cairo.common.hash import hash2 -func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { +func main{output_ptr: felt*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*, ec_op_ptr: EcOpBuiltin*}() { alloc_locals; local message: felt; @@ -28,23 +26,19 @@ func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwis from src.schnorrpy import SchnorrSignature - secret = 12 - schnorr = SchnorrSignature() - (alpha, response, pk) = schnorr.prove(secret) - print("Off-chain proof sent") - schnorr.verify(alpha, response, pk) - print("Off-chain verification done") - print("Assertting on-chain verification") - print("If there is no error, on-chain verification is completed") - - ids.alpha_G.x = alpha.x - ids.alpha_G.y = alpha.y - ids.response = response - ids.public_key.x = pk.x - ids.public_key.y = pk.y + zz = SchnorrSignature() + # For testing purposes, challenge is taken as 1. + # After adding a suitable hash function for both cairo and python int the future versions, schnorr prover and verifier can be used. + (aa,ab,ac) = zz.prove(13, 1) + + ids.alpha_G.x = aa.x + ids.alpha_G.y = aa.y + ids.response = ab + ids.public_key.x = ac.x + ids.public_key.y = ac.y %} - verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key); + verify_schnorr_signature(alpha_G = alpha_G, response = response, public_key = public_key, _challenge = 1); return (); } \ No newline at end of file