From 8706363128b71cb7077a0eecad2e079ffc807010 Mon Sep 17 00:00:00 2001 From: Mathieu <60658558+enitrat@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:26:27 +0200 Subject: [PATCH] feat: add checks on input values in ec operations (#963) * feat: add checks on input values in ec operations * remove scalar check in ec_mul --- crates/evm/src/precompiles.cairo | 8 +- .../evm/src/precompiles/ec_operations.cairo | 107 ++++++++++++++++++ .../{ => ec_operations}/ec_add.cairo | 102 ++--------------- .../{ => ec_operations}/ec_mul.cairo | 13 ++- 4 files changed, 127 insertions(+), 103 deletions(-) create mode 100644 crates/evm/src/precompiles/ec_operations.cairo rename crates/evm/src/precompiles/{ => ec_operations}/ec_add.cairo (67%) rename crates/evm/src/precompiles/{ => ec_operations}/ec_mul.cairo (93%) diff --git a/crates/evm/src/precompiles.cairo b/crates/evm/src/precompiles.cairo index 686a44816..150917b61 100644 --- a/crates/evm/src/precompiles.cairo +++ b/crates/evm/src/precompiles.cairo @@ -1,8 +1,6 @@ mod blake2f; -mod ec_add; - -mod ec_mul; +mod ec_operations; mod ec_recover; @@ -15,8 +13,8 @@ mod p256verify; mod sha256; pub use blake2f::Blake2f; -pub use ec_add::EcAdd; -pub use ec_mul::EcMul; +pub use ec_operations::ec_add::EcAdd; +pub use ec_operations::ec_mul::EcMul; pub use ec_recover::EcRecover; pub use identity::Identity; pub use modexp::ModExp; diff --git a/crates/evm/src/precompiles/ec_operations.cairo b/crates/evm/src/precompiles/ec_operations.cairo new file mode 100644 index 000000000..d696d0fee --- /dev/null +++ b/crates/evm/src/precompiles/ec_operations.cairo @@ -0,0 +1,107 @@ +pub(crate) mod ec_add; +pub(crate) mod ec_mul; +use core::circuit::CircuitElement as CE; +use core::circuit::CircuitInput as CI; +use core::circuit::{ + u96, u384, CircuitElement, CircuitInput, circuit_add, circuit_sub, circuit_mul, circuit_inverse, + EvalCircuitTrait, CircuitOutputsTrait, CircuitModulus, CircuitInputs +}; +use core::num::traits::Zero; +use garaga::core::circuit::AddInputResultTrait2; + +const BN254_ORDER: u256 = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001; +const BN254_PRIME: u256 = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; +const BN254_PRIME_LIMBS: [ + u96 + ; 4] = [ + 0x6871ca8d3c208c16d87cfd47, 0xb85045b68181585d97816a91, 0x30644e72e131a029, 0x0 +]; + +// Check if a point is on the curve. +// Point at infinity (0,0) will return false. +pub fn is_on_curve(x: u384, y: u384) -> bool { + let (b, _x, _y) = (CE::> {}, CE::> {}, CE::> {}); + + // Compute (y^2 - (x^3 + b)) % p_bn254 + let x2 = circuit_mul(_x, _x); + let x3 = circuit_mul(x2, _x); + let y2 = circuit_mul(_y, _y); + let rhs = circuit_add(x3, b); + let check = circuit_sub(y2, rhs); + + let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS) + .unwrap(); // BN254 prime field modulus + + let mut circuit_inputs = (check,).new_inputs(); + // Prefill constants: + circuit_inputs = circuit_inputs.next_2([3, 0, 0, 0]); + // Fill inputs: + circuit_inputs = circuit_inputs.next_2(x); + circuit_inputs = circuit_inputs.next_2(y); + + let outputs = circuit_inputs.done_2().eval(modulus).unwrap(); + let zero_check: u384 = outputs.get_output(check); + return zero_check.is_zero(); +} + + +// Double BN254 EC point without checking if the point is on the curve +pub fn double_ec_point_unchecked(x: u384, y: u384) -> (u384, u384) { + // CONSTANT stack + let in0 = CE::> {}; // 0x3 + // INPUT stack + let (_x, _y) = (CE::> {}, CE::> {}); + + let x2 = circuit_mul(_x, _x); + let num = circuit_mul(in0, x2); + let den = circuit_add(_y, _y); + let inv_den = circuit_inverse(den); + let slope = circuit_mul(num, inv_den); + let slope_sqr = circuit_mul(slope, slope); + + let nx = circuit_sub(circuit_sub(slope_sqr, _x), _x); + let ny = circuit_sub(circuit_mul(slope, circuit_sub(_x, nx)), _y); + + let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS) + .unwrap(); // BN254 prime field modulus + + let mut circuit_inputs = (nx, ny,).new_inputs(); + // Prefill constants: + circuit_inputs = circuit_inputs.next_2([0x3, 0x0, 0x0, 0x0]); // in0 + // Fill inputs: + circuit_inputs = circuit_inputs.next_2(x); // in1 + circuit_inputs = circuit_inputs.next_2(y); // in2 + + let outputs = circuit_inputs.done_2().eval(modulus).unwrap(); + + (outputs.get_output(nx), outputs.get_output(ny)) +} + + +// returns true if a == b mod p_bn254 +pub fn eq_mod_p(a: u384, b: u384) -> bool { + let in1 = CircuitElement::> {}; + let in2 = CircuitElement::> {}; + let sub = circuit_sub(in1, in2); + + let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS) + .unwrap(); // BN254 prime field modulus + + let outputs = (sub,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap(); + + return outputs.get_output(sub).is_zero(); +} + +// returns true if a == -b mod p_bn254 +pub fn eq_neg_mod_p(a: u384, b: u384) -> bool { + let _a = CE::> {}; + let _b = CE::> {}; + let check = circuit_add(_a, _b); + + let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS) + .unwrap(); // BN254 prime field modulus + + let outputs = (check,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap(); + + return outputs.get_output(check).is_zero(); +} diff --git a/crates/evm/src/precompiles/ec_add.cairo b/crates/evm/src/precompiles/ec_operations/ec_add.cairo similarity index 67% rename from crates/evm/src/precompiles/ec_add.cairo rename to crates/evm/src/precompiles/ec_operations/ec_add.cairo index 5af4a1fa7..ab1ae8ecc 100644 --- a/crates/evm/src/precompiles/ec_add.cairo +++ b/crates/evm/src/precompiles/ec_operations/ec_add.cairo @@ -9,6 +9,10 @@ use core::circuit::{ use core::num::traits::Zero; use core::option::Option; use core::starknet::{EthAddress}; +use crate::precompiles::ec_operations::{ + eq_mod_p, eq_neg_mod_p, is_on_curve, double_ec_point_unchecked, BN254_ORDER, BN254_PRIME_LIMBS, + BN254_PRIME +}; use evm::errors::EVMError; use evm::precompiles::Precompile; use garaga::core::circuit::AddInputResultTrait2; @@ -18,12 +22,6 @@ use utils::helpers::{load_word, U8SpanExTrait}; const BASE_COST: u64 = 150; const U256_BYTES_LEN: usize = 32; -const PRIME: [ - u96 - ; 4] = [ - 0x6871ca8d3c208c16d87cfd47, 0xb85045b68181585d97816a91, 0x30644e72e131a029, 0x0 -]; - pub impl EcAdd of Precompile { #[inline(always)] fn address() -> EthAddress { @@ -69,6 +67,9 @@ pub impl EcAdd of Precompile { fn ec_add(x1: u256, y1: u256, x2: u256, y2: u256) -> Option<(u256, u256)> { + if x1 >= BN254_PRIME || y1 >= BN254_PRIME || x2 >= BN254_PRIME || y2 >= BN254_PRIME { + return Option::None; + } if x1 == 0 && y1 == 0 { if x2 == 0 && y2 == 0 { // Both are points at infinity, return either of them. @@ -145,35 +146,6 @@ pub fn ec_safe_add(x1: u384, y1: u384, x2: u384, y2: u384) -> Option<(u384, u384 } } - -// Check if a point is on the curve. -// Point at infinity (0,0) will return false. -pub fn is_on_curve(x: u384, y: u384) -> bool { - let (b, _x, _y) = (CE::> {}, CE::> {}, CE::> {}); - - // Compute (y^2 - (x^3 + b)) % p_bn254 - let x2 = circuit_mul(_x, _x); - let x3 = circuit_mul(x2, _x); - let y2 = circuit_mul(_y, _y); - let rhs = circuit_add(x3, b); - let check = circuit_sub(y2, rhs); - - let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME) - .unwrap(); // BN254 prime field modulus - - let mut circuit_inputs = (check,).new_inputs(); - // Prefill constants: - circuit_inputs = circuit_inputs.next_2([3, 0, 0, 0]); - // Fill inputs: - circuit_inputs = circuit_inputs.next_2(x); - circuit_inputs = circuit_inputs.next_2(y); - - let outputs = circuit_inputs.done_2().eval(modulus).unwrap(); - let zero_check: u384 = outputs.get_output(check); - return zero_check.is_zero(); -} - - // Add two BN254 EC points without checking if: // - the points are on the curve // - the points are not the same @@ -191,7 +163,7 @@ fn add_ec_point_unchecked(xP: u384, yP: u384, xQ: u384, yQ: u384) -> (u384, u384 let nx = circuit_sub(circuit_sub(slope_sqr, _xP), _xQ); let ny = circuit_sub(circuit_mul(slope, circuit_sub(_xP, nx)), _yP); - let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME) + let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS) .unwrap(); // BN254 prime field modulus let mut circuit_inputs = (nx, ny,).new_inputs(); @@ -206,64 +178,6 @@ fn add_ec_point_unchecked(xP: u384, yP: u384, xQ: u384, yQ: u384) -> (u384, u384 (outputs.get_output(nx), outputs.get_output(ny)) } -// Double BN254 EC point without checking if the point is on the curve -pub fn double_ec_point_unchecked(x: u384, y: u384) -> (u384, u384) { - // CONSTANT stack - let in0 = CE::> {}; // 0x3 - // INPUT stack - let (_x, _y) = (CE::> {}, CE::> {}); - - let x2 = circuit_mul(_x, _x); - let num = circuit_mul(in0, x2); - let den = circuit_add(_y, _y); - let inv_den = circuit_inverse(den); - let slope = circuit_mul(num, inv_den); - let slope_sqr = circuit_mul(slope, slope); - - let nx = circuit_sub(circuit_sub(slope_sqr, _x), _x); - let ny = circuit_sub(circuit_mul(slope, circuit_sub(_x, nx)), _y); - - let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME) - .unwrap(); // BN254 prime field modulus - - let mut circuit_inputs = (nx, ny,).new_inputs(); - // Prefill constants: - circuit_inputs = circuit_inputs.next_2([0x3, 0x0, 0x0, 0x0]); // in0 - // Fill inputs: - circuit_inputs = circuit_inputs.next_2(x); // in1 - circuit_inputs = circuit_inputs.next_2(y); // in2 - - let outputs = circuit_inputs.done_2().eval(modulus).unwrap(); - - (outputs.get_output(nx), outputs.get_output(ny)) -} -// returns true if a == b mod p_bn254 -fn eq_mod_p(a: u384, b: u384) -> bool { - let in1 = CircuitElement::> {}; - let in2 = CircuitElement::> {}; - let sub = circuit_sub(in1, in2); - - let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME) - .unwrap(); // BN254 prime field modulus - - let outputs = (sub,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap(); - - return outputs.get_output(sub).is_zero(); -} - -// returns true if a == -b mod p_bn254 -fn eq_neg_mod_p(a: u384, b: u384) -> bool { - let _a = CE::> {}; - let _b = CE::> {}; - let check = circuit_add(_a, _b); - - let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME) - .unwrap(); // BN254 prime field modulus - - let outputs = (check,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap(); - - return outputs.get_output(check).is_zero(); -} #[cfg(test)] mod tests { diff --git a/crates/evm/src/precompiles/ec_mul.cairo b/crates/evm/src/precompiles/ec_operations/ec_mul.cairo similarity index 93% rename from crates/evm/src/precompiles/ec_mul.cairo rename to crates/evm/src/precompiles/ec_operations/ec_mul.cairo index 110d69591..ee611a9c6 100644 --- a/crates/evm/src/precompiles/ec_mul.cairo +++ b/crates/evm/src/precompiles/ec_operations/ec_mul.cairo @@ -1,14 +1,16 @@ use core::circuit::u384; use core::option::Option; use core::starknet::{EthAddress}; +use crate::precompiles::ec_operations::ec_add::ec_safe_add; + +use crate::precompiles::ec_operations::{ + eq_mod_p, eq_neg_mod_p, is_on_curve, double_ec_point_unchecked, BN254_ORDER, BN254_PRIME_LIMBS, + BN254_PRIME +}; use evm::errors::EVMError; use evm::precompiles::Precompile; - -use evm::precompiles::ec_add::{is_on_curve, double_ec_point_unchecked, ec_safe_add}; use utils::helpers::{load_word, ToBytes, U8SpanExTrait}; -// const BN254_ORDER: u256 = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001; - const BASE_COST: u64 = 6000; const U256_BYTES_LEN: usize = 32; @@ -52,6 +54,9 @@ pub impl EcMul of Precompile { // Returns Option::None in case of error. fn ec_mul(x1: u256, y1: u256, s: u256) -> Option<(u256, u256)> { + if x1 >= BN254_PRIME || y1 >= BN254_PRIME { + return Option::None; + } if x1 == 0 && y1 == 0 { // Input point is at infinity, return it return Option::Some((x1, y1));