From 7e0e76ec1a8218959ad1db94e9edea139302d3fb Mon Sep 17 00:00:00 2001 From: DCMMC Date: Sun, 3 Dec 2023 02:49:39 +0800 Subject: [PATCH 1/2] add support for secp256r1: fix ec-double and check_is_on_curve --- .../configs/secp256r1/bench_ecdsa.config | 9 ++ .../configs/secp256r1/bench_ecdsa.t.config | 1 + .../configs/secp256r1/bench_schnorr.config | 9 ++ .../configs/secp256r1/ecdsa_circuit.config | 1 + .../secp256r1/scalar_multiplication.config | 9 ++ .../configs/secp256r1/schnorr_circuit.config | 1 + halo2-ecc/src/bn254/pairing.rs | 4 +- halo2-ecc/src/ecc/mod.rs | 25 ++- halo2-ecc/src/ecc/pippenger.rs | 10 +- halo2-ecc/src/ecc/tests.rs | 3 +- halo2-ecc/src/lib.rs | 1 + halo2-ecc/src/secp256r1/mod.rs | 12 ++ halo2-ecc/src/secp256r1/tests/ecdsa.rs | 146 ++++++++++++++++++ halo2-ecc/src/secp256r1/tests/ecdsa_tests.rs | 55 +++++++ halo2-ecc/src/secp256r1/tests/mod.rs | 109 +++++++++++++ .../src/secp256r1/tests/sm_unsafe_scalars.rs | 141 +++++++++++++++++ 16 files changed, 520 insertions(+), 16 deletions(-) create mode 100644 halo2-ecc/configs/secp256r1/bench_ecdsa.config create mode 100644 halo2-ecc/configs/secp256r1/bench_ecdsa.t.config create mode 100644 halo2-ecc/configs/secp256r1/bench_schnorr.config create mode 100644 halo2-ecc/configs/secp256r1/ecdsa_circuit.config create mode 100644 halo2-ecc/configs/secp256r1/scalar_multiplication.config create mode 100644 halo2-ecc/configs/secp256r1/schnorr_circuit.config create mode 100644 halo2-ecc/src/secp256r1/mod.rs create mode 100644 halo2-ecc/src/secp256r1/tests/ecdsa.rs create mode 100644 halo2-ecc/src/secp256r1/tests/ecdsa_tests.rs create mode 100644 halo2-ecc/src/secp256r1/tests/mod.rs create mode 100644 halo2-ecc/src/secp256r1/tests/sm_unsafe_scalars.rs diff --git a/halo2-ecc/configs/secp256r1/bench_ecdsa.config b/halo2-ecc/configs/secp256r1/bench_ecdsa.config new file mode 100644 index 00000000..07591eae --- /dev/null +++ b/halo2-ecc/configs/secp256r1/bench_ecdsa.config @@ -0,0 +1,9 @@ +{"strategy":"Simple","degree":19,"num_advice":1,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":18,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":17,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":17,"num_advice":4,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":16,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":16,"num_advice":8,"num_lookup_advice":2,"num_fixed":1,"lookup_bits":15,"limb_bits":90,"num_limbs":3} +{"strategy":"Simple","degree":15,"num_advice":17,"num_lookup_advice":3,"num_fixed":1,"lookup_bits":14,"limb_bits":90,"num_limbs":3} +{"strategy":"Simple","degree":14,"num_advice":34,"num_lookup_advice":6,"num_fixed":1,"lookup_bits":13,"limb_bits":91,"num_limbs":3} +{"strategy":"Simple","degree":13,"num_advice":68,"num_lookup_advice":12,"num_fixed":1,"lookup_bits":12,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":12,"num_advice":139,"num_lookup_advice":24,"num_fixed":2,"lookup_bits":11,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":11,"num_advice":291,"num_lookup_advice":53,"num_fixed":4,"lookup_bits":10,"limb_bits":88,"num_limbs":3} \ No newline at end of file diff --git a/halo2-ecc/configs/secp256r1/bench_ecdsa.t.config b/halo2-ecc/configs/secp256r1/bench_ecdsa.t.config new file mode 100644 index 00000000..33fb34d8 --- /dev/null +++ b/halo2-ecc/configs/secp256r1/bench_ecdsa.t.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":15,"num_advice":17,"num_lookup_advice":3,"num_fixed":1,"lookup_bits":14,"limb_bits":90,"num_limbs":3} \ No newline at end of file diff --git a/halo2-ecc/configs/secp256r1/bench_schnorr.config b/halo2-ecc/configs/secp256r1/bench_schnorr.config new file mode 100644 index 00000000..07591eae --- /dev/null +++ b/halo2-ecc/configs/secp256r1/bench_schnorr.config @@ -0,0 +1,9 @@ +{"strategy":"Simple","degree":19,"num_advice":1,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":18,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":18,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":17,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":17,"num_advice":4,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":16,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":16,"num_advice":8,"num_lookup_advice":2,"num_fixed":1,"lookup_bits":15,"limb_bits":90,"num_limbs":3} +{"strategy":"Simple","degree":15,"num_advice":17,"num_lookup_advice":3,"num_fixed":1,"lookup_bits":14,"limb_bits":90,"num_limbs":3} +{"strategy":"Simple","degree":14,"num_advice":34,"num_lookup_advice":6,"num_fixed":1,"lookup_bits":13,"limb_bits":91,"num_limbs":3} +{"strategy":"Simple","degree":13,"num_advice":68,"num_lookup_advice":12,"num_fixed":1,"lookup_bits":12,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":12,"num_advice":139,"num_lookup_advice":24,"num_fixed":2,"lookup_bits":11,"limb_bits":88,"num_limbs":3} +{"strategy":"Simple","degree":11,"num_advice":291,"num_lookup_advice":53,"num_fixed":4,"lookup_bits":10,"limb_bits":88,"num_limbs":3} \ No newline at end of file diff --git a/halo2-ecc/configs/secp256r1/ecdsa_circuit.config b/halo2-ecc/configs/secp256r1/ecdsa_circuit.config new file mode 100644 index 00000000..a13e7075 --- /dev/null +++ b/halo2-ecc/configs/secp256r1/ecdsa_circuit.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":18,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":17,"limb_bits":88,"num_limbs":3} \ No newline at end of file diff --git a/halo2-ecc/configs/secp256r1/scalar_multiplication.config b/halo2-ecc/configs/secp256r1/scalar_multiplication.config new file mode 100644 index 00000000..1d97cc06 --- /dev/null +++ b/halo2-ecc/configs/secp256r1/scalar_multiplication.config @@ -0,0 +1,9 @@ +{"strategy":"Simple", +"degree":18, +"num_advice":2, +"num_lookup_advice":1, +"num_fixed":1, +"lookup_bits":17, +"limb_bits":88, +"num_limbs":3, +"window_bits":4} \ No newline at end of file diff --git a/halo2-ecc/configs/secp256r1/schnorr_circuit.config b/halo2-ecc/configs/secp256r1/schnorr_circuit.config new file mode 100644 index 00000000..a13e7075 --- /dev/null +++ b/halo2-ecc/configs/secp256r1/schnorr_circuit.config @@ -0,0 +1 @@ +{"strategy":"Simple","degree":18,"num_advice":2,"num_lookup_advice":1,"num_fixed":1,"lookup_bits":17,"limb_bits":88,"num_limbs":3} \ No newline at end of file diff --git a/halo2-ecc/src/bn254/pairing.rs b/halo2-ecc/src/bn254/pairing.rs index dbd7382f..bb0f2351 100644 --- a/halo2-ecc/src/bn254/pairing.rs +++ b/halo2-ecc/src/bn254/pairing.rs @@ -257,7 +257,7 @@ pub fn miller_loop_BN( let f_sq = fp12_chip.mul(ctx, &f, &f); f = fp12_multiply_with_line_equal::(ecc_chip.field_chip(), ctx, &f_sq, &R, P); } - R = ecc_chip.double(ctx, &R); + R = ecc_chip.double::(ctx, &R); assert!(pseudo_binary_encoding[i] <= 1 && pseudo_binary_encoding[i] >= -1); if pseudo_binary_encoding[i] != 0 { @@ -350,7 +350,7 @@ pub fn multi_miller_loop_BN( } } for r in r.iter_mut() { - *r = ecc_chip.double(ctx, r.clone()); + *r = ecc_chip.double::(ctx, r.clone()); } assert!(pseudo_binary_encoding[i] <= 1 && pseudo_binary_encoding[i] >= -1); diff --git a/halo2-ecc/src/ecc/mod.rs b/halo2-ecc/src/ecc/mod.rs index b410b1e0..5aa3debd 100644 --- a/halo2-ecc/src/ecc/mod.rs +++ b/halo2-ecc/src/ecc/mod.rs @@ -300,17 +300,21 @@ where /// # Assumptions /// * `P.y != 0` /// * `P` is not the point at infinity (undefined behavior otherwise) -pub fn ec_double>( +pub fn ec_double, C>( chip: &FC, ctx: &mut Context, P: impl Into>, -) -> EcPoint { +) -> EcPoint +where + C: CurveAffine, +{ let P = P.into(); // removed optimization that computes `2 * lambda` while assigning witness to `lambda` simultaneously, in favor of readability. The difference is just copying `lambda` once let two_y = chip.scalar_mul_no_carry(ctx, &P.y, 2); let three_x = chip.scalar_mul_no_carry(ctx, &P.x, 3); let three_x_sq = chip.mul_no_carry(ctx, three_x, &P.x); - let lambda = chip.divide_unsafe(ctx, three_x_sq, two_y); + let lambda_numerator = chip.add_constant_no_carry(ctx, three_x_sq, C::a()); + let lambda = chip.divide_unsafe(ctx, lambda_numerator, two_y); // x_3 = lambda^2 - 2 x % p let lambda_sq = chip.mul_no_carry(ctx, &lambda, &lambda); @@ -595,6 +599,8 @@ where { let lhs = chip.mul_no_carry(ctx, &P.y, &P.y); let mut rhs = chip.mul(ctx, &P.x, &P.x).into(); + + rhs = chip.add_constant_no_carry(ctx, rhs, C::a()); rhs = chip.mul_no_carry(ctx, rhs, &P.x); rhs = chip.add_constant_no_carry(ctx, rhs, C::b()); @@ -692,7 +698,7 @@ where let mut rand_start_vec = Vec::with_capacity(k + window_bits); rand_start_vec.push(base); for idx in 1..(k + window_bits) { - let base_mult = ec_double(chip, ctx, &rand_start_vec[idx - 1]); + let base_mult = ec_double::(chip, ctx, &rand_start_vec[idx - 1]); rand_start_vec.push(base_mult); } assert!(rand_start_vec.len() >= k + window_bits); @@ -743,7 +749,7 @@ where // compute \sum_i x_i P_i + (2^{k + 1} - 1) * A for idx in 0..num_windows { for _ in 0..window_bits { - curr_point = ec_double(chip, ctx, curr_point); + curr_point = ec_double::(chip, ctx, curr_point); } for (cached_points, rounded_bits) in cached_points.chunks(cache_size).zip(rounded_bits.chunks(rounded_bitlen)) @@ -961,12 +967,15 @@ impl<'chip, F: BigPrimeField, FC: FieldChip> EccChip<'chip, F, FC> { ec_sub_unequal(self.field_chip, ctx, P, Q, is_strict) } - pub fn double( + pub fn double( &self, ctx: &mut Context, P: impl Into>, - ) -> EcPoint { - ec_double(self.field_chip, ctx, P) + ) -> EcPoint + where + C: CurveAffine, + { + ec_double::(self.field_chip, ctx, P) } pub fn is_equal( diff --git a/halo2-ecc/src/ecc/pippenger.rs b/halo2-ecc/src/ecc/pippenger.rs index 736a9f34..109f089d 100644 --- a/halo2-ecc/src/ecc/pippenger.rs +++ b/halo2-ecc/src/ecc/pippenger.rs @@ -261,7 +261,7 @@ where let mut any_points = Vec::with_capacity(num_rounds); any_points.push(any_base); for _ in 1..num_rounds { - any_points.push(ec_double(chip, ctx, any_points.last().unwrap())); + any_points.push(ec_double::(chip, ctx, any_points.last().unwrap())); } // now begins multi-threading @@ -318,7 +318,7 @@ where // we have agg[j] = G'[j] + (2^num_rounds - 1) * any_base // let any_point = (2^num_rounds - 1) * any_base // TODO: can we remove all these random point operations somehow? - let mut any_point = ec_double(chip, ctx, any_points.last().unwrap()); + let mut any_point = ec_double::(chip, ctx, any_points.last().unwrap()); any_point = ec_sub_unequal(chip, ctx, any_point, &any_points[0], true); // compute sum_{k=0..scalar_bits} agg[k] * 2^k - (sum_{k=0..scalar_bits} 2^k) * rand_point @@ -326,13 +326,13 @@ where let mut sum = agg.pop().unwrap().into(); let mut any_sum = any_point.clone(); for g in agg.iter().rev() { - any_sum = ec_double(chip, ctx, any_sum); + any_sum = ec_double::(chip, ctx, any_sum); // cannot use ec_double_and_add_unequal because you cannot guarantee that `sum != g` - sum = ec_double(chip, ctx, sum); + sum = ec_double::(chip, ctx, sum); sum = ec_add_unequal(chip, ctx, sum, g, true); } - any_sum = ec_double(chip, ctx, any_sum); + any_sum = ec_double::(chip, ctx, any_sum); any_sum = ec_sub_unequal(chip, ctx, any_sum, any_point, true); ec_sub_strict(chip, ctx, sum, any_sum) diff --git a/halo2-ecc/src/ecc/tests.rs b/halo2-ecc/src/ecc/tests.rs index 02f549e3..e3aabb95 100644 --- a/halo2-ecc/src/ecc/tests.rs +++ b/halo2-ecc/src/ecc/tests.rs @@ -9,6 +9,7 @@ use crate::halo2_proofs::{ plonk::*, }; use halo2_base::gates::RangeChip; +use halo2_base::halo2_proofs::halo2curves::secp256k1::Secp256k1Affine; use halo2_base::utils::bigint_to_fe; use halo2_base::utils::testing::base_test; use halo2_base::utils::value_to_option; @@ -47,7 +48,7 @@ fn basic_g1_tests( println!("add unequal witness OK"); // test double - let doub = chip.double(ctx, &P_assigned); + let doub = chip.double::(ctx, &P_assigned); assert_eq!(doub.x.0.truncation.to_bigint(limb_bits), doub.x.0.value); assert_eq!(doub.y.0.truncation.to_bigint(limb_bits), doub.y.0.value); { diff --git a/halo2-ecc/src/lib.rs b/halo2-ecc/src/lib.rs index 5b3f191a..a7d8d1cd 100644 --- a/halo2-ecc/src/lib.rs +++ b/halo2-ecc/src/lib.rs @@ -10,6 +10,7 @@ pub mod fields; pub mod bn254; pub mod grumpkin; pub mod secp256k1; +pub mod secp256r1; pub use halo2_base; pub(crate) use halo2_base::halo2_proofs; diff --git a/halo2-ecc/src/secp256r1/mod.rs b/halo2-ecc/src/secp256r1/mod.rs new file mode 100644 index 00000000..aa5ccda1 --- /dev/null +++ b/halo2-ecc/src/secp256r1/mod.rs @@ -0,0 +1,12 @@ +use crate::halo2_proofs::halo2curves::secp256r1::{Fp, Fq}; + +use crate::ecc; +use crate::fields::fp; + +pub type FpChip<'range, F> = fp::FpChip<'range, F, Fp>; +pub type FqChip<'range, F> = fp::FpChip<'range, F, Fq>; +pub type Secp256r1Chip<'chip, F> = ecc::EccChip<'chip, F, FpChip<'chip, F>>; +pub const SECP_B: u64 = 7; + +#[cfg(test)] +mod tests; diff --git a/halo2-ecc/src/secp256r1/tests/ecdsa.rs b/halo2-ecc/src/secp256r1/tests/ecdsa.rs new file mode 100644 index 00000000..c3feb4da --- /dev/null +++ b/halo2-ecc/src/secp256r1/tests/ecdsa.rs @@ -0,0 +1,146 @@ +#![allow(non_snake_case)] +use std::fs::File; +use std::io::BufReader; +use std::io::Write; +use std::{fs, io::BufRead}; + +use super::*; +use crate::fields::FpStrategy; +use crate::halo2_proofs::{ + arithmetic::CurveAffine, + halo2curves::bn256::Fr, + halo2curves::secp256r1::{Fp, Fq, Secp256r1Affine}, +}; +use crate::secp256r1::{FpChip, FqChip}; +use crate::{ + ecc::{ecdsa::ecdsa_verify_no_pubkey_check, EccChip}, + fields::FieldChip, +}; +use halo2_base::gates::RangeChip; +use halo2_base::utils::{biguint_to_fe, fe_to_biguint, modulus, BigPrimeField}; +use halo2_base::Context; +use serde::{Deserialize, Serialize}; +use test_log::test; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct CircuitParams { + strategy: FpStrategy, + degree: u32, + num_advice: usize, + num_lookup_advice: usize, + num_fixed: usize, + lookup_bits: usize, + limb_bits: usize, + num_limbs: usize, +} + +#[derive(Clone, Copy, Debug)] +pub struct ECDSAInput { + pub r: Fq, + pub s: Fq, + pub msghash: Fq, + pub pk: Secp256r1Affine, +} + +pub fn ecdsa_test( + ctx: &mut Context, + range: &RangeChip, + params: CircuitParams, + input: ECDSAInput, +) -> F { + let fp_chip = FpChip::::new(range, params.limb_bits, params.num_limbs); + let fq_chip = FqChip::::new(range, params.limb_bits, params.num_limbs); + + let [m, r, s] = [input.msghash, input.r, input.s].map(|x| fq_chip.load_private(ctx, x)); + + let ecc_chip = EccChip::>::new(&fp_chip); + let pk = ecc_chip.load_private_unchecked(ctx, (input.pk.x, input.pk.y)); + // test ECDSA + let res = ecdsa_verify_no_pubkey_check::( + &ecc_chip, ctx, pk, r, s, m, 4, 4, + ); + *res.value() +} + +pub fn random_ecdsa_input(rng: &mut StdRng) -> ECDSAInput { + let sk = ::ScalarExt::random(rng.clone()); + let pk = Secp256r1Affine::from(Secp256r1Affine::generator() * sk); + let msghash = ::ScalarExt::random(rng.clone()); + + let k = ::ScalarExt::random(rng); + let k_inv = k.invert().unwrap(); + + let r_point = Secp256r1Affine::from(Secp256r1Affine::generator() * k).coordinates().unwrap(); + let x = r_point.x(); + let x_bigint = fe_to_biguint(x); + let r = biguint_to_fe::(&(x_bigint % modulus::())); + let s = k_inv * (msghash + (r * sk)); + + ECDSAInput { r, s, msghash, pk } +} + +pub fn run_test(input: ECDSAInput) { + let path = "configs/secp256r1/ecdsa_circuit.config"; + let params: CircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + + let res = base_test() + .k(params.degree) + .lookup_bits(params.lookup_bits) + .run(|ctx, range| ecdsa_test(ctx, range, params, input)); + assert_eq!(res, Fr::ONE); +} + +#[test] +fn test_secp256r1_ecdsa() { + let mut rng = StdRng::seed_from_u64(0); + let input = random_ecdsa_input(&mut rng); + run_test(input); +} + +#[test] +fn bench_secp256r1_ecdsa() -> Result<(), Box> { + let config_path = "configs/secp256r1/bench_ecdsa.config"; + let bench_params_file = + File::open(config_path).unwrap_or_else(|e| panic!("{config_path} does not exist: {e:?}")); + fs::create_dir_all("results/secp256r1").unwrap(); + fs::create_dir_all("data").unwrap(); + let results_path = "results/secp256r1/ecdsa_bench.csv"; + let mut fs_results = File::create(results_path).unwrap(); + writeln!(fs_results, "degree,num_advice,num_lookup,num_fixed,lookup_bits,limb_bits,num_limbs,proof_time,proof_size,verify_time")?; + + let mut rng = StdRng::seed_from_u64(0); + let bench_params_reader = BufReader::new(bench_params_file); + for line in bench_params_reader.lines() { + let bench_params: CircuitParams = serde_json::from_str(line.unwrap().as_str()).unwrap(); + let k = bench_params.degree; + println!("---------------------- degree = {k} ------------------------------",); + + let stats = + base_test().k(k).lookup_bits(bench_params.lookup_bits).unusable_rows(20).bench_builder( + random_ecdsa_input(&mut rng), + random_ecdsa_input(&mut rng), + |pool, range, input| { + ecdsa_test(pool.main(), range, bench_params, input); + }, + ); + + writeln!( + fs_results, + "{},{},{},{},{},{},{},{:?},{},{:?}", + bench_params.degree, + bench_params.num_advice, + bench_params.num_lookup_advice, + bench_params.num_fixed, + bench_params.lookup_bits, + bench_params.limb_bits, + bench_params.num_limbs, + stats.proof_time.time.elapsed(), + stats.proof_size, + stats.verify_time.time.elapsed() + )?; + } + Ok(()) +} diff --git a/halo2-ecc/src/secp256r1/tests/ecdsa_tests.rs b/halo2-ecc/src/secp256r1/tests/ecdsa_tests.rs new file mode 100644 index 00000000..594f6617 --- /dev/null +++ b/halo2-ecc/src/secp256r1/tests/ecdsa_tests.rs @@ -0,0 +1,55 @@ +use crate::halo2_proofs::{ + arithmetic::CurveAffine, + halo2curves::secp256r1::{Fq, Secp256r1Affine}, +}; + +use halo2_base::utils::{biguint_to_fe, fe_to_biguint, modulus}; +use rand::random; +use test_case::test_case; + +use super::ecdsa::{run_test, ECDSAInput}; + +fn custom_parameters_ecdsa(sk: u64, msg_hash: u64, k: u64) -> ECDSAInput { + let sk = ::ScalarExt::from(sk); + let pubkey = Secp256r1Affine::from(Secp256r1Affine::generator() * sk); + let msg_hash = ::ScalarExt::from(msg_hash); + + let k = ::ScalarExt::from(k); + let k_inv = k.invert().unwrap(); + + let r_point = Secp256r1Affine::from(Secp256r1Affine::generator() * k).coordinates().unwrap(); + let x = r_point.x(); + let x_bigint = fe_to_biguint(x); + + let r = biguint_to_fe::(&(x_bigint % modulus::())); + let s = k_inv * (msg_hash + (r * sk)); + + ECDSAInput { r, s, msghash: msg_hash, pk: pubkey } +} + +#[test] +#[should_panic(expected = "assertion failed: `(left == right)`")] +fn test_ecdsa_msg_hash_zero() { + let input = custom_parameters_ecdsa(random::(), 0, random::()); + run_test(input); +} + +#[test] +#[should_panic(expected = "assertion failed: `(left == right)`")] +fn test_ecdsa_private_key_zero() { + let input = custom_parameters_ecdsa(0, random::(), random::()); + run_test(input); +} + +#[test_case(1, 1, 1; "")] +fn test_ecdsa_custom_valid_inputs(sk: u64, msg_hash: u64, k: u64) { + let input = custom_parameters_ecdsa(sk, msg_hash, k); + run_test(input); +} + +#[test_case(1, 1, 1; "")] +fn test_ecdsa_custom_valid_inputs_negative_s(sk: u64, msg_hash: u64, k: u64) { + let mut input = custom_parameters_ecdsa(sk, msg_hash, k); + input.s = -input.s; + run_test(input); +} diff --git a/halo2-ecc/src/secp256r1/tests/mod.rs b/halo2-ecc/src/secp256r1/tests/mod.rs new file mode 100644 index 00000000..e12afc1c --- /dev/null +++ b/halo2-ecc/src/secp256r1/tests/mod.rs @@ -0,0 +1,109 @@ +#![allow(non_snake_case)] +use std::fs::File; + +use crate::ff::Field; +use crate::group::Curve; +use halo2_base::{ + gates::RangeChip, + halo2_proofs::halo2curves::secp256k1::{Fq, Secp256k1Affine}, + utils::{biguint_to_fe, fe_to_biguint, testing::base_test, BigPrimeField}, + Context, +}; +use num_bigint::BigUint; +use rand::rngs::StdRng; +use rand_core::SeedableRng; +use serde::{Deserialize, Serialize}; + +use crate::{ + ecc::EccChip, + fields::{FieldChip, FpStrategy}, + secp256k1::{FpChip, FqChip}, +}; + +pub mod ecdsa; +pub mod ecdsa_tests; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +struct CircuitParams { + strategy: FpStrategy, + degree: u32, + num_advice: usize, + num_lookup_advice: usize, + num_fixed: usize, + lookup_bits: usize, + limb_bits: usize, + num_limbs: usize, +} + +fn sm_test( + ctx: &mut Context, + range: &RangeChip, + params: CircuitParams, + base: Secp256k1Affine, + scalar: Fq, + window_bits: usize, +) { + let fp_chip = FpChip::::new(range, params.limb_bits, params.num_limbs); + let fq_chip = FqChip::::new(range, params.limb_bits, params.num_limbs); + let ecc_chip = EccChip::>::new(&fp_chip); + + let s = fq_chip.load_private(ctx, scalar); + let P = ecc_chip.assign_point(ctx, base); + + let sm = ecc_chip.scalar_mult::( + ctx, + P, + s.limbs().to_vec(), + fq_chip.limb_bits, + window_bits, + ); + + let sm_answer = (base * scalar).to_affine(); + + let sm_x = sm.x.value(); + let sm_y = sm.y.value(); + assert_eq!(sm_x, fe_to_biguint(&sm_answer.x)); + assert_eq!(sm_y, fe_to_biguint(&sm_answer.y)); +} + +fn run_test(base: Secp256k1Affine, scalar: Fq) { + let path = "configs/secp256k1/ecdsa_circuit.config"; + let params: CircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + + base_test().k(params.degree).lookup_bits(params.lookup_bits).run(|ctx, range| { + sm_test(ctx, range, params, base, scalar, 4); + }); +} + +#[test] +fn test_secp_sm_random() { + let mut rng = StdRng::seed_from_u64(0); + run_test(Secp256k1Affine::random(&mut rng), Fq::random(&mut rng)); +} + +#[test] +fn test_secp_sm_minus_1() { + let rng = StdRng::seed_from_u64(0); + let base = Secp256k1Affine::random(rng); + let mut s = -Fq::one(); + let mut n = fe_to_biguint(&s); + loop { + run_test(base, s); + if &n % BigUint::from(2usize) == BigUint::from(0usize) { + break; + } + n /= 2usize; + s = biguint_to_fe(&n); + } +} + +#[test] +fn test_secp_sm_0_1() { + let rng = StdRng::seed_from_u64(0); + let base = Secp256k1Affine::random(rng); + run_test(base, Fq::ZERO); + run_test(base, Fq::ONE); +} diff --git a/halo2-ecc/src/secp256r1/tests/sm_unsafe_scalars.rs b/halo2-ecc/src/secp256r1/tests/sm_unsafe_scalars.rs new file mode 100644 index 00000000..d6febee9 --- /dev/null +++ b/halo2-ecc/src/secp256r1/tests/sm_unsafe_scalars.rs @@ -0,0 +1,141 @@ +use crate::{ + ecc::fixed_base::scalar_multiply, + fields::{fp::FpChip, FpStrategy}, +}; +use ff::PrimeField; +use halo2_base::{ + gates::{builder::GateThreadBuilder, RangeChip}, + halo2_proofs::halo2curves::secp256r1::{Fp, Secp256r1, Secp256r1Affine}, +}; +use std::fs::File; + +use super::*; + +const GROUP_ORDER: Fp = + Fp::from_raw([0xbfd25e8cd0364141, 0xbaaedce6af48a03b, 0xfffffffffffffffe, 0xffffffffffffffff]); + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +struct SMCircuitParams { + strategy: FpStrategy, + degree: u32, + num_advice: usize, + num_lookup_advice: usize, + num_fixed: usize, + lookup_bits: usize, + limb_bits: usize, + num_limbs: usize, + window_bits: usize, +} + +fn scalar_multiply_test(params: SMCircuitParams, point: Secp256r1Affine, scalar: Fp) { + let mut builder = GateThreadBuilder::mock(); + + std::env::set_var("LOOKUP_BITS", params.lookup_bits.to_string()); + let range = RangeChip::::default(params.lookup_bits); + let fp_chip = FpChip::::new(&range, params.limb_bits, params.num_limbs); + let ecc_chip = EccChip::new(&fp_chip); + + let ctx = builder.main(0); + let scalar_assigned = vec![ctx.load_witness(scalar)]; + + let _sm = scalar_multiply( + ecc_chip.field_chip(), + ctx, + &point, + scalar_assigned, + Fp::NUM_BITS as usize, + params.window_bits, + ); + + //println!("{:?}", sm.x().value()); +} + +#[test] +fn test_sm1() { + let path = "configs/secp256r1/scalar_multiplication.config"; + let params: SMCircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + let generator_point = Secp256r1::generator().to_affine(); + + let scalar = GROUP_ORDER; + + println!("the scalar is {scalar:?}"); + scalar_multiply_test(params, generator_point, scalar); +} + +#[test] +fn test_sm2() { + let path = "configs/secp256r1/scalar_multiplication.config"; + let params: SMCircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + let generator_point = Secp256r1::generator().to_affine(); + + let scalar = GROUP_ORDER - Fp::one(); + + println!("the scalar is {scalar:?}"); + scalar_multiply_test(params, generator_point, scalar); +} + +#[test] +fn test_sm3() { + let path = "configs/secp256r1/scalar_multiplication.config"; + let params: SMCircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + let generator_point = Secp256r1::generator().to_affine(); + + let scalar = GROUP_ORDER - Fp::one(); + + println!("the scalar is {scalar:?}"); + scalar_multiply_test(params, generator_point, scalar); +} + +#[test] +fn test_sm4() { + let path = "configs/secp256r1/scalar_multiplication.config"; + let params: SMCircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + let generator_point = Secp256r1::generator().to_affine(); + + let scalar = GROUP_ORDER - Fp::from(2); + + println!("the scalar is {scalar:?}"); + scalar_multiply_test(params, generator_point, scalar); +} + +#[test] +fn test_sm5() { + let path = "configs/secp256r1/scalar_multiplication.config"; + let params: SMCircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + let generator_point = Secp256r1::generator().to_affine(); + + let scalar = GROUP_ORDER + Fp::one(); + + println!("the scalar is {scalar:?}"); + scalar_multiply_test(params, generator_point, scalar); +} + +#[test] +fn test_sm6() { + let path = "configs/secp256r1/scalar_multiplication.config"; + let params: SMCircuitParams = serde_json::from_reader( + File::open(path).unwrap_or_else(|e| panic!("{path} does not exist: {e:?}")), + ) + .unwrap(); + let generator_point = Secp256r1::generator().to_affine(); + + let scalar = GROUP_ORDER + Fp::from(2); + + println!("the scalar is {scalar:?}"); + scalar_multiply_test(params, generator_point, scalar); +} From 307601d61c22747ba0253c03e7ef44f03cdbe463 Mon Sep 17 00:00:00 2001 From: DCMMC Date: Sun, 21 Jan 2024 15:10:56 +0800 Subject: [PATCH 2/2] enable these new operations only if C::a() != 0 --- halo2-ecc/src/ecc/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/halo2-ecc/src/ecc/mod.rs b/halo2-ecc/src/ecc/mod.rs index 5aa3debd..0bda6b0a 100644 --- a/halo2-ecc/src/ecc/mod.rs +++ b/halo2-ecc/src/ecc/mod.rs @@ -290,7 +290,7 @@ where // formula from https://crypto.stanford.edu/pbc/notes/elliptic/explicit.html // assume y != 0 (otherwise 2P = O) -// lamb = 3x^2 / (2 y) % p +// lamb = (3x^2 + a) / (2 y) % p // x_3 = out[0] = lambda^2 - 2 x % p // y_3 = out[1] = lambda (x - x_3) - y % p @@ -313,7 +313,11 @@ where let two_y = chip.scalar_mul_no_carry(ctx, &P.y, 2); let three_x = chip.scalar_mul_no_carry(ctx, &P.x, 3); let three_x_sq = chip.mul_no_carry(ctx, three_x, &P.x); - let lambda_numerator = chip.add_constant_no_carry(ctx, three_x_sq, C::a()); + let lambda_numerator = if C::a() != C::Base::ZERO { + chip.add_constant_no_carry(ctx, three_x_sq, C::a()) + } else { + three_x_sq + }; let lambda = chip.divide_unsafe(ctx, lambda_numerator, two_y); // x_3 = lambda^2 - 2 x % p @@ -600,7 +604,9 @@ where let lhs = chip.mul_no_carry(ctx, &P.y, &P.y); let mut rhs = chip.mul(ctx, &P.x, &P.x).into(); - rhs = chip.add_constant_no_carry(ctx, rhs, C::a()); + if C::a() != C::Base::ZERO { + rhs = chip.add_constant_no_carry(ctx, rhs, C::a()); + } rhs = chip.mul_no_carry(ctx, rhs, &P.x); rhs = chip.add_constant_no_carry(ctx, rhs, C::b());