Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement schnorr siganture #116

Merged
9 changes: 9 additions & 0 deletions halo2-ecc/configs/secp256k1/bench_schnorr.config
Original file line number Diff line number Diff line change
@@ -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}
1 change: 1 addition & 0 deletions halo2-ecc/configs/secp256k1/schnorr_circuit.config
Original file line number Diff line number Diff line change
@@ -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}
21 changes: 21 additions & 0 deletions halo2-ecc/src/bigint/big_is_even.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use super::OverflowInteger;
use halo2_base::gates::GateInstructions;
use halo2_base::gates::RangeChip;
use halo2_base::QuantumCell::Constant;
use halo2_base::{safe_types::RangeInstructions, utils::ScalarField, AssignedValue, Context};

/// # Assumptions
/// * `a` has nonzero number of limbs
pub fn positive<F: ScalarField>(
gate: &RangeChip<F>,
odyssey2077 marked this conversation as resolved.
Show resolved Hide resolved
odyssey2077 marked this conversation as resolved.
Show resolved Hide resolved
ctx: &mut Context<F>,
a: OverflowInteger<F>,
limb_bits: usize,
) -> AssignedValue<F> {
let k = a.limbs.len();
assert_ne!(k, 0);
let first_cell: AssignedValue<F> = a.limbs[0];

let last_bit = gate.get_last_bit(ctx, first_cell, limb_bits);
gate.gate.sub(ctx, Constant(F::one()), last_bit)
odyssey2077 marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions halo2-ecc/src/bigint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use num_traits::Zero;

pub mod add_no_carry;
pub mod big_is_equal;
pub mod big_is_even;
pub mod big_is_zero;
pub mod big_less_than;
pub mod carry_mod;
Expand Down
1 change: 1 addition & 0 deletions halo2-ecc/src/ecc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use std::marker::PhantomData;

pub mod ecdsa;
pub mod fixed_base;
pub mod schnorr_signature;
// pub mod fixed_base_pippenger;
pub mod pippenger;

Expand Down
78 changes: 78 additions & 0 deletions halo2-ecc/src/ecc/schnorr_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::bigint::{big_is_equal, ProperCrtUint};
use crate::fields::{fp::FpChip, FieldChip, PrimeField};
use halo2_base::{gates::GateInstructions, utils::CurveAffineExt, AssignedValue, Context};

use super::{fixed_base, scalar_multiply, EcPoint, EccChip};

// CF is the coordinate field of GA
// SF is the scalar field of GA
// p = base field modulus
// n = scalar field modulus
/// `pubkey` should not be the identity point
/// follow spec in https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
odyssey2077 marked this conversation as resolved.
Show resolved Hide resolved
pub fn schnorr_verify_no_pubkey_check<F: PrimeField, CF: PrimeField, SF: PrimeField, GA>(
chip: &EccChip<F, FpChip<F, CF>>,
ctx: &mut Context<F>,
pubkey: EcPoint<F, <FpChip<F, CF> as FieldChip<F>>::FieldPoint>,
r: ProperCrtUint<F>, // int(sig[0:32]); fail if r ≥ p.
s: ProperCrtUint<F>, // int(sig[32:64]); fail if s ≥ n
msgHash: ProperCrtUint<F>, // int(hashBIP0340/challenge(bytes(r) || bytes(P) || m)) mod n
var_window_bits: usize,
fixed_window_bits: usize,
) -> AssignedValue<F>
where
GA: CurveAffineExt<Base = CF, ScalarExt = SF>,
{
let base_chip = chip.field_chip;
let scalar_chip =
FpChip::<F, SF>::new(base_chip.range, base_chip.limb_bits, base_chip.num_limbs);

// check r < p
let r_valid = base_chip.is_less_than_p(ctx, &r);
// check s < n
let s_valid = scalar_chip.is_soft_nonzero(ctx, &s);
// check e < n
let e_valid = scalar_chip.is_soft_nonzero(ctx, &msgHash);

// compute s * G and msgHash * pubkey
let s_G = fixed_base::scalar_multiply(
base_chip,
ctx,
&GA::generator(),
s.limbs().to_vec(),
base_chip.limb_bits,
fixed_window_bits,
);
let e_P = scalar_multiply::<_, _, GA>(
base_chip,
ctx,
pubkey,
msgHash.limbs().to_vec(),
base_chip.limb_bits,
var_window_bits,
);

// check s_G.x != e_P.x, which is a requirement for sub_unequal
let x_eq = base_chip.is_equal(ctx, &s_G.x, &e_P.x);
let x_neq = base_chip.gate().not(ctx, x_eq);

// R = s⋅G - e⋅P
// R is not infinity point implicitly constrainted by is_strict = true
let R = chip.sub_unequal(ctx, s_G, e_P, true);

// check R.y is even
let R_y = R.y;
let R_y_is_even: AssignedValue<F> = base_chip.is_even(ctx, &R_y);

// check R.x == r
let R_x = scalar_chip.enforce_less_than(ctx, R.x);
let equal_check = big_is_equal::assign(base_chip.gate(), ctx, R_x.0, r);

let res1 = base_chip.gate().and(ctx, r_valid, s_valid);
let res2: AssignedValue<F> = base_chip.gate().and(ctx, res1, e_valid);
let res3 = base_chip.gate().and(ctx, res2, x_neq);
let res4: AssignedValue<F> = base_chip.gate().and(ctx, res3, R_y_is_even);
let res5 = base_chip.gate().and(ctx, res4, equal_check);

res5
}
39 changes: 36 additions & 3 deletions halo2-ecc/src/fields/fp.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::{FieldChip, PrimeField, PrimeFieldChip, Selectable};
use crate::bigint::{
add_no_carry, big_is_equal, big_is_zero, carry_mod, check_carry_mod_to_zero, mul_no_carry,
scalar_mul_and_add_no_carry, scalar_mul_no_carry, select, select_by_indicator, sub,
sub_no_carry, CRTInteger, FixedCRTInteger, OverflowInteger, ProperCrtUint, ProperUint,
add_no_carry, big_is_equal, big_is_even, big_is_zero, carry_mod, check_carry_mod_to_zero,
mul_no_carry, scalar_mul_and_add_no_carry, scalar_mul_no_carry, select, select_by_indicator,
sub, sub_no_carry, CRTInteger, FixedCRTInteger, OverflowInteger, ProperCrtUint, ProperUint,
};
use crate::halo2_proofs::halo2curves::CurveAffine;
use halo2_base::gates::RangeChip;
Expand Down Expand Up @@ -124,13 +124,46 @@ impl<'range, F: PrimeField, Fp: PrimeField> FpChip<'range, F, Fp> {
self.gate().assert_is_const(ctx, &borrow.unwrap(), &F::one());
}

/// Given proper CRT integer `a`, returns 1 iff `a < modulus::<F>()`
/// # Assumptions
/// * `a` is proper representation of BigUint
pub fn is_less_than_p(
&self,
ctx: &mut Context<F>,
a: impl Into<ProperCrtUint<F>>,
) -> AssignedValue<F> {
let a = a.into();

// underflow != 0 iff carry < p
let p = self.load_constant_uint(ctx, self.p.to_biguint().unwrap());
odyssey2077 marked this conversation as resolved.
Show resolved Hide resolved
let (_, underflow) =
sub::crt::<F>(self.range(), ctx, a, p, self.limb_bits, self.limb_bases[1]);
let is_underflow_zero = self.gate().is_zero(ctx, underflow);
let no_underflow = self.gate().not(ctx, is_underflow_zero);

no_underflow
}

pub fn load_constant_uint(&self, ctx: &mut Context<F>, a: BigUint) -> ProperCrtUint<F> {
FixedCRTInteger::from_native(a, self.num_limbs, self.limb_bits).assign(
ctx,
self.limb_bits,
self.native_modulus(),
)
}

// assuming `a` has been range checked to be a proper BigInt
// constrain the witness `a` to be `< p`
// then check if `a[0]` is even
pub fn is_even(
&self,
ctx: &mut Context<F>,
a: impl Into<ProperCrtUint<F>>,
) -> AssignedValue<F> {
let a = a.into();
self.enforce_less_than_p(ctx, a.clone());
big_is_even::positive(self.range(), ctx, a.0.truncation, self.limb_bits)
}
}

impl<'range, F: PrimeField, Fp: PrimeField> PrimeFieldChip<F> for FpChip<'range, F, Fp> {
Expand Down
2 changes: 2 additions & 0 deletions halo2-ecc/src/secp256k1/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ use crate::{
pub mod ecdsa;
pub mod ecdsa_tests;

pub mod schnorr_signature_tests;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
struct CircuitParams {
strategy: FpStrategy,
Expand Down
Loading