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

feat: add secp256r1 support #1351

Draft
wants to merge 27 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2d238e3
update SignData with generic type
DreamWuGit Jun 27, 2024
f430f3d
update PrecompileEvent::SignData
DreamWuGit Jun 27, 2024
29f7c34
update some SignDatas
DreamWuGit Jun 27, 2024
1b7d0f2
add sig circuit signatures_r1
DreamWuGit Jun 27, 2024
9c59cf3
change tx circuit regarding signdata
DreamWuGit Jun 27, 2024
ca79ecc
sig table adds p256 signatures
DreamWuGit Jun 28, 2024
fb1d74a
add ecdsa_r1_config
DreamWuGit Jun 28, 2024
435c392
add FpChipR1 type
DreamWuGit Jul 1, 2024
bdff1f7
fix comment
DreamWuGit Jul 1, 2024
aad86aa
Merge branch 'develop' into feat/secp256r1
DreamWuGit Jul 1, 2024
ffd9263
rename FpChip
DreamWuGit Jul 2, 2024
7272aa1
add assign_ecdsa_generic method
DreamWuGit Jul 3, 2024
01e6f94
fix assign_ecdsa_generic issue
DreamWuGit Jul 3, 2024
71d64fe
update sign_data_decomposition_generic
DreamWuGit Jul 4, 2024
be10b33
make pk_bytes_le_generic helper
DreamWuGit Jul 4, 2024
0d72669
apply sign_data_decomposition_generic
DreamWuGit Jul 5, 2024
266fc75
add assign_sig_verify_generic
DreamWuGit Jul 5, 2024
9c35569
apply assign_sig_verify_generic
DreamWuGit Jul 7, 2024
65a80f3
ecdsa_r1_config.range load lookup
DreamWuGit Jul 9, 2024
1ebd7bf
update existing sig circuit tests
DreamWuGit Jul 10, 2024
cf4ccef
add helper sign_generic
DreamWuGit Jul 10, 2024
b469244
rename sign_generic to sign and remove old method
DreamWuGit Jul 10, 2024
e6d5784
add new test p256_sign_verify
DreamWuGit Jul 11, 2024
15476f3
add random msg_hash test
DreamWuGit Jul 11, 2024
116a56b
add sign_verify and rename old
DreamWuGit Jul 11, 2024
fea64c4
sign_verify fill with k1 & r1 test data
DreamWuGit Jul 11, 2024
29ea9b2
msg_hash == 0 case
DreamWuGit Jul 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions bus-mapping/src/circuit_input_builder/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ use halo2_proofs::{
plonk::Expression,
};

use halo2_proofs::halo2curves::{
// secp256k1 curve
secp256k1::{Fp as Fp_K1, Fq as Fq_K1, Secp256k1Affine},
// p256 curve
secp256r1::{Fp as Fp_R1, Fq as Fq_R1, Secp256r1Affine},
Coordinates,
//CurveAffine,
};

/// An execution step of the EVM.
#[derive(Clone, Debug)]
pub struct ExecStep {
Expand Down Expand Up @@ -905,7 +914,7 @@ pub struct PrecompileEvents {

impl PrecompileEvents {
/// Get all ecrecover events.
pub fn get_ecrecover_events(&self) -> Vec<SignData> {
pub fn get_ecrecover_events(&self) -> Vec<SignData<Fq_K1, Secp256k1Affine>> {
self.events
.iter()
.filter_map(|e| {
Expand Down Expand Up @@ -988,13 +997,28 @@ impl PrecompileEvents {
.cloned()
.collect()
}

/// Get all p256 verify events.
pub fn get_p256_verify_events(&self) -> Vec<SignData<Fq_R1, Secp256r1Affine>> {
self.events
.iter()
.filter_map(|e: &PrecompileEvent| {
if let PrecompileEvent::P256Verify(sign_data) = e {
Some(sign_data)
} else {
None
}
})
.cloned()
.collect()
}
}

/// I/O from a precompiled contract call.
#[derive(Clone, Debug)]
pub enum PrecompileEvent {
/// Represents the I/O from Ecrecover call.
Ecrecover(SignData),
Ecrecover(SignData<Fq_K1, Secp256k1Affine>),
/// Represents the I/O from EcAdd call.
EcAdd(EcAddOp),
/// Represents the I/O from EcMul call.
Expand All @@ -1005,6 +1029,8 @@ pub enum PrecompileEvent {
ModExp(BigModExp),
/// Represents the I/O from SHA256 call.
SHA256(SHA256),
/// Represents the I/O from P256Verify call.
P256Verify(SignData<Fq_R1, Secp256r1Affine>),
}

impl Default for PrecompileEvent {
Expand Down
14 changes: 9 additions & 5 deletions eth-types/src/geth_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use ethers_core::types::{
transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, Eip2930TransactionRequest,
NameOrAddress, TransactionRequest, H256,
};
use halo2curves::{group::ff::PrimeField, secp256k1::Fq};
use halo2curves::{
group::ff::PrimeField,
secp256k1::{Fq as Fq_K1, Secp256k1Affine},
};
use num::Integer;
use num_bigint::BigUint;
use serde::{Serialize, Serializer};
Expand Down Expand Up @@ -356,12 +359,13 @@ impl From<&Transaction> for TransactionRequest {
}

impl Transaction {
/// secp256k1 method:
/// Return the SignData associated with this Transaction.
pub fn sign_data(&self) -> Result<SignData, Error> {
pub fn sign_data(&self) -> Result<SignData<Fq_K1, Secp256k1Affine>, Error> {
let sig_r_le = self.r.to_le_bytes();
let sig_s_le = self.s.to_le_bytes();
let sig_r = ct_option_ok_or(Fq::from_repr(sig_r_le), Error::Signature)?;
let sig_s = ct_option_ok_or(Fq::from_repr(sig_s_le), Error::Signature)?;
let sig_r = ct_option_ok_or(Fq_K1::from_repr(sig_r_le), Error::Signature)?;
let sig_s = ct_option_ok_or(Fq_K1::from_repr(sig_s_le), Error::Signature)?;
let msg = self.rlp_unsigned_bytes.clone().into();
let msg_hash: [u8; 32] = Keccak256::digest(&msg)
.as_slice()
Expand All @@ -374,7 +378,7 @@ impl Transaction {
let msg_hash = BigUint::from_bytes_be(msg_hash.as_slice());
let msg_hash = msg_hash.mod_floor(&*SECP256K1_Q);
let msg_hash_le = biguint_to_32bytes_le(msg_hash);
let msg_hash = ct_option_ok_or(Fq::from_repr(msg_hash_le), Error::Signature)?;
let msg_hash = ct_option_ok_or(Fq_K1::from_repr(msg_hash_le), Error::Signature)?;
Ok(SignData {
signature: (sig_r, sig_s, v),
pk,
Expand Down
89 changes: 72 additions & 17 deletions eth-types/src/sign_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,45 @@ use halo2curves::{
group::{
ff::{Field as GroupField, PrimeField},
prime::PrimeCurveAffine,
Curve,
Curve, GroupEncoding,
},
secp256k1::{Fp, Fq, Secp256k1Affine},
Coordinates, CurveAffine,

// secp256k1 curve
secp256k1::{Fp as Fp_K1, Fq as Fq_K1, Secp256k1Affine},
// p256 curve
secp256r1::{Fp as Fp_R1, Fq as Fq_R1, Secp256r1Affine},
Coordinates,
CurveAffine,
CurveAffineExt,
};
use num_bigint::BigUint;
use sha3::digest::generic_array::GenericArray;
use std::sync::LazyLock;
use subtle::CtOption;

/// Do a secp256k1 signature with a given randomness value.
pub fn sign(randomness: Fq, sk: Fq, msg_hash: Fq) -> (Fq, Fq, u8) {
/// Do a secp256k1 or secp256r1 signature with a given randomness value.
/// FromUniformBytes<64> refers to https://github.com/scroll-tech/halo2curves/blob/v0.1.0/src/secp256k1/fq.rs#L287
/// and https://github.com/scroll-tech/halo2curves/blob/v0.1.0/src/secp256r1/fq.rs#L283
pub fn sign<
Fp: PrimeField<Repr = [u8; 32]>,
Fq: PrimeField + FromUniformBytes<64>,
Affine: CurveAffine<ScalarExt = Fq, Base = Fp> + std::ops::Mul<Fq> + CurveAffineExt,
>(
randomness: Fq,
sk: Fq,
msg_hash: Fq,
) -> (Fq, Fq, u8) {
let randomness_inv = Option::<Fq>::from(randomness.invert()).expect("cannot invert randomness");
let generator = Secp256k1Affine::generator();
let generator = Affine::generator();
let sig_point = generator * randomness;
let sig_v: bool = sig_point.to_affine().y.is_odd().into();
let sig_v: bool = sig_point.to_affine().into_coordinates().1.is_odd().into();

let x = *Option::<Coordinates<_>>::from(sig_point.to_affine().coordinates())
.expect("point is the identity")
.x();

let mut x_bytes = [0u8; 64];
x_bytes[..32].copy_from_slice(&x.to_bytes());
x_bytes[..32].copy_from_slice(&x.to_repr());

let sig_r = Fq::from_uniform_bytes(&x_bytes); // get x cordinate (E::Base) on E::Scalar

Expand All @@ -52,12 +68,13 @@ pub fn sign(randomness: Fq, sk: Fq, msg_hash: Fq) -> (Fq, Fq, u8) {
/// Signature data required by the SignVerify Chip as input to verify a
/// signature.
#[derive(Clone, Debug)]
pub struct SignData {
pub struct SignData<Fq: PrimeField, Affine: CurveAffine> {
/// Secp256k1 signature point (r, s, v)
/// v must be 0 or 1
pub signature: (Fq, Fq, u8),
/// Secp256k1 public key
pub pk: Secp256k1Affine,
/// Secp256k1 or Secp256r1 public key
///
pub pk: Affine,
/// Message being hashed before signing.
pub msg: Bytes,
/// Hash of the message that is being signed
Expand Down Expand Up @@ -99,7 +116,7 @@ pub fn get_dummy_tx() -> (TransactionRequest, Signature) {
(tx, sig)
}

impl SignData {
impl SignData<Fq_K1, Secp256k1Affine> {
/// Recover address of the signature
pub fn get_addr(&self) -> Address {
if self.pk.is_identity().into() {
Expand All @@ -110,7 +127,18 @@ impl SignData {
}
}

static SIGN_DATA_DEFAULT: LazyLock<SignData> = LazyLock::new(|| {
impl SignData<Fq_R1, Secp256r1Affine> {
/// Recover address of the signature
pub fn get_addr(&self) -> Address {
if self.pk.is_identity().into() {
return Address::zero();
}
let pk_hash = keccak256(pk_bytes_swap_endianness(&pk_bytes_le_p256(&self.pk)));
Address::from_slice(&pk_hash[12..])
}
}

static SIGN_DATA_DEFAULT: LazyLock<SignData<Fq_K1, Secp256k1Affine>> = LazyLock::new(|| {
let (tx_req, sig) = get_dummy_tx();
let tx = Transaction {
tx_type: TxType::PreEip155,
Expand All @@ -132,7 +160,8 @@ static SIGN_DATA_DEFAULT: LazyLock<SignData> = LazyLock::new(|| {
sign_data
});

impl Default for SignData {
// Default for secp256k1
impl Default for SignData<Fq_K1, Secp256k1Affine> {
// Hardcoded valid signature corresponding to a hardcoded private key and
// message hash generated from "nothing up my sleeve" values to make the
// ECDSA chip pass the constraints, to be use for padding signature
Expand Down Expand Up @@ -183,11 +212,11 @@ pub fn recover_pk2(
debug_assert_eq!(public_key[0], 0x04);
let pk_le = pk_bytes_swap_endianness(&public_key[1..]);
let x = ct_option_ok_or(
Fp::from_bytes(pk_le[..32].try_into().unwrap()),
Fp_K1::from_bytes(pk_le[..32].try_into().unwrap()),
Error::Signature,
)?;
let y = ct_option_ok_or(
Fp::from_bytes(pk_le[32..].try_into().unwrap()),
Fp_K1::from_bytes(pk_le[32..].try_into().unwrap()),
Error::Signature,
)?;
ct_option_ok_or(Secp256k1Affine::from_xy(x, y), Error::Signature)
Expand All @@ -196,7 +225,7 @@ pub fn recover_pk2(
/// Secp256k1 Curve Scalar. Reference: Section 2.4.1 (parameter `n`) in "SEC 2: Recommended
/// Elliptic Curve Domain Parameters" document at http://www.secg.org/sec2-v2.pdf
pub static SECP256K1_Q: LazyLock<BigUint> =
LazyLock::new(|| BigUint::from_bytes_le(&(Fq::zero() - Fq::one()).to_repr()) + 1u64);
LazyLock::new(|| BigUint::from_bytes_le(&(Fq_K1::zero() - Fq_K1::one()).to_repr()) + 1u64);

/// Helper function to convert a `CtOption` into an `Result`. Similar to
/// `Option::ok_or`.
Expand All @@ -223,3 +252,29 @@ pub fn pk_bytes_le(pk: &Secp256k1Affine) -> [u8; 64] {
pk_le[32..].copy_from_slice(&pk_coord.y().to_bytes());
pk_le
}

/// Return both secp256k1 and secp256r1 public key (x, y) coordinates in little endian bytes.
pub fn pk_bytes_le_generic<
Fp: PrimeField<Repr = [u8; 32]>, // + GroupEncoding<Repr = [u8; 32]>, // 32 bytes for secp256k1 and secp256r1 curve
Affine: CurveAffine<Base = Fp>,
>(
pk: &Affine,
) -> [u8; 64] {
let pk_coord = Option::<Coordinates<_>>::from(pk.coordinates()).expect("point is the identity");
let mut pk_le = [0u8; 64];
//pk_le[..32].copy_from_slice(&pk_coord.x().to_bytes());
//pk_le[32..].copy_from_slice(&pk_coord.y().to_bytes());
pk_le[..32].copy_from_slice(&pk_coord.x().to_repr());
pk_le[32..].copy_from_slice(&pk_coord.y().to_repr());
pk_le
}

// TODO: refactor to generic type: `pk_bytes_le_<Affine: CurveAffineExt>(pk: &Affine)`
/// Return the secp256k1 public key (x, y) coordinates in little endian bytes.
pub fn pk_bytes_le_p256(pk: &Secp256r1Affine) -> [u8; 64] {
let pk_coord = Option::<Coordinates<_>>::from(pk.coordinates()).expect("point is the identity");
let mut pk_le = [0u8; 64];
pk_le[..32].copy_from_slice(&pk_coord.x().to_bytes());
pk_le[32..].copy_from_slice(&pk_coord.y().to_bytes());
pk_le
}
Loading
Loading