Skip to content

Commit

Permalink
More ecdsa tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yulliakot committed May 20, 2023
1 parent f566451 commit da7d5b1
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 1 deletion.
1 change: 1 addition & 0 deletions halo2-ecc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rand_chacha = "0.3.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rayon = "1.6.1"
test-case = "3.1.0"

# arithmetic
ff = "0.12"
Expand Down
1 change: 1 addition & 0 deletions halo2-ecc/src/ecc/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,4 @@ fn plot_ecc() {

halo2_proofs::dev::CircuitLayout::default().render(k, &circuit, &root).unwrap();
}

7 changes: 6 additions & 1 deletion halo2-ecc/src/secp256k1/tests/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use std::io::BufReader;
use std::io::Write;
use std::{fs, io::BufRead};


#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
struct CircuitParams {
strategy: FpStrategy,
Expand Down Expand Up @@ -72,6 +73,7 @@ fn ecdsa_test<F: PrimeField>(
&fp_chip, ctx, &pk, &r, &s, &m, 4, 4,
);
assert_eq!(res.value(), &F::one());

}

fn random_ecdsa_circuit(
Expand All @@ -94,11 +96,13 @@ fn random_ecdsa_circuit(
let r_point = Secp256k1Affine::from(Secp256k1Affine::generator() * k).coordinates().unwrap();
let x = r_point.x();
let x_bigint = fe_to_biguint(x);

let r = biguint_to_fe::<Fq>(&(x_bigint % modulus::<Fq>()));
let s = k_inv * (msg_hash + (r * sk));

let start0 = start_timer!(|| format!("Witness generation for circuit in {stage:?} stage"));
ecdsa_test(builder.main(0), params, r, s, msg_hash, pubkey);


let circuit = match stage {
CircuitBuilderStage::Mock => {
Expand Down Expand Up @@ -127,6 +131,7 @@ fn test_secp256k1_ecdsa() {
MockProver::run(params.degree, &circuit, vec![]).unwrap().assert_satisfied();
}


#[test]
fn bench_secp256k1_ecdsa() -> Result<(), Box<dyn std::error::Error>> {
let mut rng = OsRng;
Expand Down
240 changes: 240 additions & 0 deletions halo2-ecc/src/secp256k1/tests/ecdsa_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#![allow(non_snake_case)]
use crate::fields::FpStrategy;
use crate::halo2_proofs::{
arithmetic::CurveAffine,
dev::MockProver,
halo2curves::bn256::Fr,
halo2curves::secp256k1::{Fp, Fq, Secp256k1Affine},
};
use crate::secp256k1::{FpChip, FqChip};
use crate::{
ecc::{ecdsa::ecdsa_verify_no_pubkey_check, EccChip},
fields::{FieldChip, PrimeField},
};
use ark_std::{end_timer, start_timer};
use halo2_base::gates::builder::{
CircuitBuilderStage, GateThreadBuilder, MultiPhaseThreadBreakPoints, RangeCircuitBuilder,
};

use halo2_base::gates::RangeChip;
use halo2_base::utils::{biguint_to_fe, fe_to_biguint, modulus};
use halo2_base::Context;
use rand_core::OsRng;
use serde::{Deserialize, Serialize};
use std::fs::File;


#[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 ecdsa_test<F: PrimeField>(
ctx: &mut Context<F>,
params: CircuitParams,
r: Fq,
s: Fq,
msghash: Fq,
pk: Secp256k1Affine,
) {
std::env::set_var("LOOKUP_BITS", params.lookup_bits.to_string());
let range = RangeChip::<F>::default(params.lookup_bits);
let fp_chip = FpChip::<F>::new(&range, params.limb_bits, params.num_limbs);
let fq_chip = FqChip::<F>::new(&range, params.limb_bits, params.num_limbs);

let [m, r, s] =
[msghash, r, s].map(|x| fq_chip.load_private(ctx, FqChip::<F>::fe_to_witness(&x)));

let ecc_chip = EccChip::<F, FpChip<F>>::new(&fp_chip);
let pk = ecc_chip.load_private(ctx, (pk.x, pk.y));
// test ECDSA
let res = ecdsa_verify_no_pubkey_check::<F, Fp, Fq, Secp256k1Affine>(
&fp_chip, ctx, &pk, &r, &s, &m, 4, 4,
);
assert_eq!(res.value(), &F::one());
}


fn random_parameters_ecdsa() -> (
Fq,
Fq,
Fq,
Secp256k1Affine,
) {
let sk = <Secp256k1Affine as CurveAffine>::ScalarExt::random(OsRng);
let pubkey = Secp256k1Affine::from(Secp256k1Affine::generator() * sk);
let msg_hash = <Secp256k1Affine as CurveAffine>::ScalarExt::random(OsRng);

let k = <Secp256k1Affine as CurveAffine>::ScalarExt::random(OsRng);
let k_inv = k.invert().unwrap();

let r_point = Secp256k1Affine::from(Secp256k1Affine::generator() * k).coordinates().unwrap();
let x = r_point.x();
let x_bigint = fe_to_biguint(x);


let r = biguint_to_fe::<Fq>(&(x_bigint % modulus::<Fq>()));
let s = k_inv * (msg_hash + (r * sk));

return ( r, s, msg_hash, pubkey)
}

fn custom_parameters_ecdsa(
sk: u64,
msg_hash: u64,
k: u64,
) -> (
Fq,
Fq,
Fq,
Secp256k1Affine,
){
let sk = <Secp256k1Affine as CurveAffine>::ScalarExt::from(sk);
let pubkey = Secp256k1Affine::from(Secp256k1Affine::generator() * sk);
let msg_hash = <Secp256k1Affine as CurveAffine>::ScalarExt::from(msg_hash);

let k = <Secp256k1Affine as CurveAffine>::ScalarExt::from(k);
let k_inv = k.invert().unwrap();

let r_point = Secp256k1Affine::from(Secp256k1Affine::generator() * k).coordinates().unwrap();
let x = r_point.x();
let x_bigint = fe_to_biguint(x);


let r = biguint_to_fe::<Fq>(&(x_bigint % modulus::<Fq>()));
let s = k_inv * (msg_hash + (r * sk));

return (r, s, msg_hash, pubkey)
}


fn ecdsa_circuit(r: Fq, s: Fq, msg_hash: Fq, pubkey: Secp256k1Affine,
params: CircuitParams,
stage: CircuitBuilderStage,
break_points: Option<MultiPhaseThreadBreakPoints>,
) -> RangeCircuitBuilder<Fr> {

let mut builder = match stage {
CircuitBuilderStage::Mock => GateThreadBuilder::mock(),
CircuitBuilderStage::Prover => GateThreadBuilder::prover(),
CircuitBuilderStage::Keygen => GateThreadBuilder::keygen(),
};
let start0 = start_timer!(|| format!("Witness generation for circuit in {stage:?} stage"));
ecdsa_test(builder.main(0), params, r, s, msg_hash, pubkey);


let circuit = match stage {
CircuitBuilderStage::Mock => {
builder.config(params.degree as usize, Some(20));
RangeCircuitBuilder::mock(builder)
}
CircuitBuilderStage::Keygen => {
builder.config(params.degree as usize, Some(20));
RangeCircuitBuilder::keygen(builder)
}
CircuitBuilderStage::Prover => RangeCircuitBuilder::prover(builder, break_points.unwrap()),
};
end_timer!(start0);
circuit
}



use rand::random;
#[cfg(test)]
#[test]
#[should_panic(expected = "assertion failed: `(left == right)`")]
fn test_ecdsa_msg_hash_zero()
{
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();

let (r, s, msg_hash, pubkey) = custom_parameters_ecdsa(random::<u64>(),0, random::<u64>());

let circuit = ecdsa_circuit(r, s, msg_hash, pubkey, params, CircuitBuilderStage::Mock, None,);
MockProver::run(params.degree, &circuit, vec![]).unwrap().assert_satisfied();
}

#[cfg(test)]
#[test]
#[should_panic(expected = "assertion failed: `(left == right)`")]
fn test_ecdsa_private_key_zero()
{
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();

let (r, s, msg_hash, pubkey) = custom_parameters_ecdsa(0, random::<u64>(), random::<u64>());

let circuit = ecdsa_circuit(r, s, msg_hash, pubkey, params, CircuitBuilderStage::Mock, None,);
MockProver::run(params.degree, &circuit, vec![]).unwrap().assert_satisfied();
}



#[cfg(test)]
#[test]
fn test_ecdsa_random_valid_inputs()
{
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();

let (r, s, msg_hash, pubkey) = random_parameters_ecdsa();

let circuit = ecdsa_circuit(r, s, msg_hash, pubkey, params, CircuitBuilderStage::Mock, None,);
MockProver::run(params.degree, &circuit, vec![]).unwrap().assert_satisfied();
}




use test_case::test_case;
#[cfg(test)]
#[test_case(1, 1, 1; "")]
fn test_ecdsa_custom_valid_inputs(sk: u64,msg_hash: u64, k: u64,)
{
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();

let (r, s, msg_hash, pubkey) = custom_parameters_ecdsa(sk, msg_hash, k);

let circuit = ecdsa_circuit(r, s, msg_hash, pubkey, params, CircuitBuilderStage::Mock, None,);
MockProver::run(params.degree, &circuit, vec![]).unwrap().assert_satisfied();
}



#[cfg(test)]
#[test_case(1, 1, 1; "")]
fn test_ecdsa_custom_valid_inputs_negative_s(sk: u64,msg_hash: u64, k: u64,)
{
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();

let (r, s, msg_hash, pubkey) = custom_parameters_ecdsa(sk, msg_hash, k);
let s = -s;

let circuit = ecdsa_circuit(r, s, msg_hash, pubkey, params, CircuitBuilderStage::Mock, None,);
MockProver::run(params.degree, &circuit, vec![]).unwrap().assert_satisfied();
}
1 change: 1 addition & 0 deletions halo2-ecc/src/secp256k1/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod ecdsa;
pub mod unit_tests;

0 comments on commit da7d5b1

Please sign in to comment.