#![allow(non_snake_case)]
use crate::{
ec::{
commitments::{CommitmentWithOpening, PointCommitment, PointCommitmentWithOpening},
sw_point_addition::{PointAdditionProof, PointAdditionProtocol},
sw_scalar_mult::{ScalarMultiplicationProof, ScalarMultiplicationProtocol},
},
error::Error,
tom256::{Affine as Tom256Affine, Config as Tom256Config, Fr as Tom256Fr},
};
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::{BigInteger, Field, PrimeField};
use ark_secp256r1::{Affine, Config, Fr, G_GENERATOR_X, G_GENERATOR_Y};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{io::Write, ops::Neg, rand::RngCore, vec::Vec};
use dock_crypto_utils::{
commitment::PedersenCommitmentKey, randomized_mult_checker::RandomizedMultChecker,
};
use kvac::bbs_sharp::ecdsa;
const SECP_GEN: Affine = Affine::new_unchecked(G_GENERATOR_X, G_GENERATOR_Y);
pub struct TransformedEcdsaSig {
pub R: Affine,
pub z: Fr,
}
#[derive(Clone, PartialEq, Eq)]
pub struct PoKEcdsaSigCommittedPublicKeyProtocol<const NUM_REPS_SCALAR_MULT: usize = 128> {
pub R: Affine,
pub comm_z: Affine,
pub comm_minus_zR: PointCommitment<Tom256Config>,
pub comm_minus_g_t_r_inv_rand: (Tom256Fr, Tom256Fr),
pub protocol_minus_zR: ScalarMultiplicationProtocol<Config, Tom256Config, NUM_REPS_SCALAR_MULT>,
pub protocol_add: PointAdditionProtocol<Config, Tom256Config>,
}
#[derive(Clone, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)]
pub struct PoKEcdsaSigCommittedPublicKey<const NUM_REPS_SCALAR_MULT: usize = 128> {
pub R: Affine,
pub comm_z: Affine,
pub comm_minus_zR: PointCommitment<Tom256Config>,
pub comm_minus_g_t_r_inv_rand: (Tom256Fr, Tom256Fr),
pub proof_minus_zR: ScalarMultiplicationProof<Config, Tom256Config, NUM_REPS_SCALAR_MULT>,
pub proof_add: PointAdditionProof<Config, Tom256Config>,
}
impl TransformedEcdsaSig {
pub fn new(
sig: &ecdsa::Signature,
hashed_message: Fr,
public_key: Affine,
) -> Result<Self, Error> {
let s_inv = sig
.response
.inverse()
.ok_or(Error::EcdsaSigResponseNotInvertible)?;
let r_inv = sig
.rand_x_coord
.inverse()
.ok_or(Error::EcdsaSigResponseNotInvertible)?;
let u1 = hashed_message * s_inv;
let u2 = sig.rand_x_coord * s_inv;
let R = (SECP_GEN * u1 + public_key * u2).into_affine();
Ok(Self {
R,
z: sig.response * r_inv,
})
}
pub fn verify_prehashed(&self, hashed_message: Fr, public_key: Affine) -> Result<(), Error> {
let r_inv = Self::r_inv(&self.R)?;
let t_r_inv = hashed_message * r_inv;
let zR = self.R * self.z;
if (zR - (SECP_GEN * t_r_inv)) != public_key.into_group() {
return Err(Error::InvalidTransformedEcdsaSig);
}
Ok(())
}
pub fn r_inv(R: &Affine) -> Result<Fr, Error> {
Fr::from(R.x.into_bigint())
.inverse()
.ok_or(Error::EcdsaSigResponseNotInvertible)
}
}
impl<const NUM_REPS_SCALAR_MULT: usize>
PoKEcdsaSigCommittedPublicKeyProtocol<NUM_REPS_SCALAR_MULT>
{
pub fn init<R: RngCore>(
rng: &mut R,
sig: TransformedEcdsaSig,
hashed_message: Fr,
public_key: Affine,
comm_public_key: PointCommitmentWithOpening<Tom256Config>,
comm_key_secp: &PedersenCommitmentKey<Affine>,
comm_key_tom: &PedersenCommitmentKey<Tom256Affine>,
) -> Result<Self, Error> {
let minus_R = sig.R.neg();
let minus_zR = (minus_R * sig.z).into_affine();
let minus_g_t_r_inv = (SECP_GEN * (hashed_message * TransformedEcdsaSig::r_inv(&sig.R)?))
.neg()
.into_affine();
let comm_z = CommitmentWithOpening::new(rng, sig.z, comm_key_secp);
let comm_minus_zR = PointCommitmentWithOpening::new(rng, &minus_zR, comm_key_tom)?;
let comm_minus_g_t_r_inv =
PointCommitmentWithOpening::new(rng, &minus_g_t_r_inv, comm_key_tom)?;
let protocol_minus_zR =
ScalarMultiplicationProtocol::<Config, Tom256Config, NUM_REPS_SCALAR_MULT>::init(
rng,
comm_z.clone(),
comm_minus_zR.clone(),
minus_zR,
minus_R,
comm_key_secp,
comm_key_tom,
)?;
let protocol_add = PointAdditionProtocol::init(
rng,
comm_minus_zR.clone(),
comm_public_key.clone(),
comm_minus_g_t_r_inv.clone(),
minus_zR,
public_key,
minus_g_t_r_inv,
comm_key_tom,
)?;
Ok(Self {
R: sig.R,
comm_z: comm_z.comm,
comm_minus_zR: comm_minus_zR.comm,
comm_minus_g_t_r_inv_rand: (comm_minus_g_t_r_inv.r_x, comm_minus_g_t_r_inv.r_y),
protocol_minus_zR,
protocol_add,
})
}
pub fn challenge_contribution<W: Write>(&self, mut writer: W) -> Result<(), Error> {
self.R.serialize_compressed(&mut writer)?;
self.comm_z.serialize_compressed(&mut writer)?;
self.comm_minus_zR.serialize_compressed(&mut writer)?;
self.comm_minus_g_t_r_inv_rand
.0
.serialize_compressed(&mut writer)?;
self.comm_minus_g_t_r_inv_rand
.1
.serialize_compressed(&mut writer)?;
self.protocol_minus_zR.challenge_contribution(&mut writer)?;
self.protocol_add.challenge_contribution(&mut writer)?;
Ok(())
}
pub fn gen_proof(
self,
challenge: &Tom256Fr,
) -> PoKEcdsaSigCommittedPublicKey<NUM_REPS_SCALAR_MULT> {
let challenge_bytes = challenge.0.to_bytes_le();
let proof_minus_zR = self.protocol_minus_zR.gen_proof(&challenge_bytes);
let proof_add = self.protocol_add.gen_proof(challenge);
PoKEcdsaSigCommittedPublicKey {
R: self.R,
comm_z: self.comm_z,
comm_minus_zR: self.comm_minus_zR,
comm_minus_g_t_r_inv_rand: self.comm_minus_g_t_r_inv_rand,
proof_minus_zR,
proof_add,
}
}
}
impl<const NUM_REPS_SCALAR_MULT: usize> PoKEcdsaSigCommittedPublicKey<NUM_REPS_SCALAR_MULT> {
pub fn verify(
&self,
hashed_message: Fr,
comm_public_key: &PointCommitment<Tom256Config>,
challenge: &Tom256Fr,
comm_key_secp: &PedersenCommitmentKey<Affine>,
comm_key_tom: &PedersenCommitmentKey<Tom256Affine>,
) -> Result<(), Error> {
let minus_R = self.R.neg();
let minus_g_t_r_inv = (SECP_GEN * (hashed_message * TransformedEcdsaSig::r_inv(&self.R)?))
.neg()
.into_affine();
let comm_minus_g_t_r_inv = PointCommitmentWithOpening::new_given_randomness(
&minus_g_t_r_inv,
self.comm_minus_g_t_r_inv_rand.0,
self.comm_minus_g_t_r_inv_rand.1,
comm_key_tom,
)?;
let challenge_bytes = challenge.0.to_bytes_le();
self.proof_minus_zR.verify(
&self.comm_z,
&self.comm_minus_zR,
&minus_R,
&challenge_bytes,
comm_key_secp,
comm_key_tom,
)?;
self.proof_add.verify(
&self.comm_minus_zR,
comm_public_key,
&comm_minus_g_t_r_inv.comm,
challenge,
comm_key_tom,
)?;
Ok(())
}
pub fn verify_using_randomized_mult_checker(
&self,
hashed_message: Fr,
comm_public_key: PointCommitment<Tom256Config>,
challenge: &Tom256Fr,
comm_key_secp: PedersenCommitmentKey<Affine>,
comm_key_tom: PedersenCommitmentKey<Tom256Affine>,
rmc_1: &mut RandomizedMultChecker<Affine>,
rmc_2: &mut RandomizedMultChecker<Tom256Affine>,
) -> Result<(), Error> {
let minus_R = self.R.neg();
let minus_g_t_r_inv = (SECP_GEN * (hashed_message * TransformedEcdsaSig::r_inv(&self.R)?))
.neg()
.into_affine();
let comm_minus_g_t_r_inv = PointCommitmentWithOpening::new_given_randomness(
&minus_g_t_r_inv,
self.comm_minus_g_t_r_inv_rand.0,
self.comm_minus_g_t_r_inv_rand.1,
&comm_key_tom,
)?;
let challenge_bytes = challenge.0.to_bytes_le();
self.proof_minus_zR.verify_using_randomized_mult_checker(
self.comm_z,
self.comm_minus_zR,
minus_R,
&challenge_bytes,
comm_key_secp,
comm_key_tom,
rmc_1,
rmc_2,
)?;
self.proof_add.verify_using_randomized_mult_checker(
self.comm_minus_zR,
comm_public_key,
comm_minus_g_t_r_inv.comm,
challenge,
comm_key_tom,
rmc_2,
)?;
Ok(())
}
pub fn challenge_contribution<W: Write>(&self, mut writer: W) -> Result<(), Error> {
self.R.serialize_compressed(&mut writer)?;
self.comm_z.serialize_compressed(&mut writer)?;
self.comm_minus_zR.serialize_compressed(&mut writer)?;
self.comm_minus_g_t_r_inv_rand
.0
.serialize_compressed(&mut writer)?;
self.comm_minus_g_t_r_inv_rand
.1
.serialize_compressed(&mut writer)?;
self.proof_minus_zR.challenge_contribution(&mut writer)?;
self.proof_add.challenge_contribution(&mut writer)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
ec::commitments::from_base_field_to_scalar_field, eq_across_groups::ProofLargeWitness,
};
use ark_bls12_381::{Fr as BlsFr, G1Affine as BlsG1Affine};
use ark_secp256r1::Fq;
use ark_std::{
rand::{rngs::StdRng, SeedableRng},
UniformRand,
};
use blake2::Blake2b512;
use bulletproofs_plus_plus::prelude::SetupParams as BppSetupParams;
use dock_crypto_utils::transcript::{new_merlin_transcript, Transcript};
use rand_core::OsRng;
use std::time::Instant;
use test_utils::statistics::statistics;
#[test]
fn transformed_sig_verify() {
let mut rng = StdRng::seed_from_u64(0u64);
let sk = Fr::rand(&mut rng);
let pk = (SECP_GEN * sk).into_affine();
let message = Fr::rand(&mut rng);
let sig = ecdsa::Signature::new_prehashed(&mut rng, message, sk);
assert!(sig.verify_prehashed(message, pk));
let transformed_sig = TransformedEcdsaSig::new(&sig, message, pk).unwrap();
transformed_sig.verify_prehashed(message, pk).unwrap();
}
#[test]
fn pok_ecdsa_sig_comm_pubkey() {
let mut rng = OsRng::default();
let comm_key_secp = PedersenCommitmentKey::<Affine>::new::<Blake2b512>(b"test1");
let comm_key_tom = PedersenCommitmentKey::<Tom256Affine>::new::<Blake2b512>(b"test2");
let sk = Fr::rand(&mut rng);
let pk = (SECP_GEN * sk).into_affine();
let mut prov_time = vec![];
let mut ver_time = vec![];
let mut ver_rmc_time = vec![];
let num_iters = 10;
for i in 0..num_iters {
let message = Fr::rand(&mut rng);
let sig = ecdsa::Signature::new_prehashed(&mut rng, message, sk);
let transformed_sig = TransformedEcdsaSig::new(&sig, message, pk).unwrap();
transformed_sig.verify_prehashed(message, pk).unwrap();
let comm_pk = PointCommitmentWithOpening::new(&mut rng, &pk, &comm_key_tom).unwrap();
let start = Instant::now();
let mut prover_transcript = new_merlin_transcript(b"test");
prover_transcript.append(b"comm_key_secp", &comm_key_secp);
prover_transcript.append(b"comm_key_tom", &comm_key_tom);
prover_transcript.append(b"comm_pk", &comm_pk.comm);
prover_transcript.append(b"message", &message);
let protocol = PoKEcdsaSigCommittedPublicKeyProtocol::<128>::init(
&mut rng,
transformed_sig,
message,
pk,
comm_pk.clone(),
&comm_key_secp,
&comm_key_tom,
)
.unwrap();
protocol
.challenge_contribution(&mut prover_transcript)
.unwrap();
let challenge_prover = prover_transcript.challenge_scalar(b"challenge");
let proof = protocol.gen_proof(&challenge_prover);
prov_time.push(start.elapsed());
let start = Instant::now();
let mut verifier_transcript = new_merlin_transcript(b"test");
verifier_transcript.append(b"comm_key_secp", &comm_key_secp);
verifier_transcript.append(b"comm_key_tom", &comm_key_tom);
verifier_transcript.append(b"comm_pk", &comm_pk.comm);
verifier_transcript.append(b"message", &message);
proof
.challenge_contribution(&mut verifier_transcript)
.unwrap();
let challenge_verifier = verifier_transcript.challenge_scalar(b"challenge");
assert_eq!(challenge_prover, challenge_verifier);
proof
.verify(
message,
&comm_pk.comm,
&challenge_verifier,
&comm_key_secp,
&comm_key_tom,
)
.unwrap();
ver_time.push(start.elapsed());
let start = Instant::now();
let mut verifier_transcript = new_merlin_transcript(b"test");
verifier_transcript.append(b"comm_key_secp", &comm_key_secp);
verifier_transcript.append(b"comm_key_tom", &comm_key_tom);
verifier_transcript.append(b"comm_pk", &comm_pk.comm);
verifier_transcript.append(b"message", &message);
proof
.challenge_contribution(&mut verifier_transcript)
.unwrap();
let challenge_verifier = verifier_transcript.challenge_scalar(b"challenge");
assert_eq!(challenge_prover, challenge_verifier);
let mut checker_1 = RandomizedMultChecker::<Affine>::new_using_rng(&mut rng);
let mut checker_2 = RandomizedMultChecker::<Tom256Affine>::new_using_rng(&mut rng);
proof
.verify_using_randomized_mult_checker(
message,
comm_pk.comm,
&challenge_verifier,
comm_key_secp,
comm_key_tom,
&mut checker_1,
&mut checker_2,
)
.unwrap();
ver_rmc_time.push(start.elapsed());
if i == 0 {
println!("Proof size = {} bytes", proof.compressed_size());
}
}
println!("For {num_iters} iterations");
println!("Proving time: {:?}", statistics(prov_time));
println!("Verifying time: {:?}", statistics(ver_time));
println!(
"Verifying time with randomized multiplication check: {:?}",
statistics(ver_rmc_time)
);
}
#[test]
fn pok_ecdsa_pubkey_committed_in_bls12_381_commitment() {
let mut rng = OsRng::default();
const WITNESS_BIT_SIZE: usize = 64;
const CHALLENGE_BIT_SIZE: usize = 180;
const ABORT_PARAM: usize = 8;
const RESPONSE_BYTE_SIZE: usize = 32;
const NUM_REPS: usize = 1;
const NUM_CHUNKS: usize = 4;
let comm_key_secp = PedersenCommitmentKey::<Affine>::new::<Blake2b512>(b"test1");
let comm_key_tom = PedersenCommitmentKey::<Tom256Affine>::new::<Blake2b512>(b"test2");
let comm_key_bls = PedersenCommitmentKey::<BlsG1Affine>::new::<Blake2b512>(b"test3");
let base = 2;
let mut bpp_setup_params = BppSetupParams::<Tom256Affine>::new_for_perfect_range_proof::<
Blake2b512,
>(
b"test", base, WITNESS_BIT_SIZE as u16, NUM_CHUNKS as u32
);
bpp_setup_params.G = comm_key_tom.g;
bpp_setup_params.H_vec[0] = comm_key_tom.h;
let sk = Fr::rand(&mut rng);
let pk = (SECP_GEN * sk).into_affine();
let comm_pk = PointCommitmentWithOpening::new(&mut rng, &pk, &comm_key_tom).unwrap();
let pk_x = from_base_field_to_scalar_field::<Fq, BlsFr>(pk.x().unwrap());
let pk_y = from_base_field_to_scalar_field::<Fq, BlsFr>(pk.y().unwrap());
let bls_comm_pk_rx = BlsFr::rand(&mut rng);
let bls_comm_pk_ry = BlsFr::rand(&mut rng);
let bls_comm_pk_x = comm_key_bls.commit(&pk_x, &bls_comm_pk_rx);
let bls_comm_pk_y = comm_key_bls.commit(&pk_y, &bls_comm_pk_ry);
let mut prov_time = vec![];
let mut ver_time = vec![];
let num_iters = 10;
for i in 0..num_iters {
let message = Fr::rand(&mut rng);
let sig = ecdsa::Signature::new_prehashed(&mut rng, message, sk);
let start = Instant::now();
let transformed_sig = TransformedEcdsaSig::new(&sig, message, pk).unwrap();
transformed_sig.verify_prehashed(message, pk).unwrap();
let mut prover_transcript = new_merlin_transcript(b"test");
prover_transcript.append(b"comm_key_secp", &comm_key_secp);
prover_transcript.append(b"comm_key_tom", &comm_key_tom);
prover_transcript.append(b"comm_key_bls", &comm_key_bls);
prover_transcript.append(b"bpp_setup_params", &bpp_setup_params);
prover_transcript.append(b"comm_pk", &comm_pk.comm);
prover_transcript.append(b"bls_comm_pk_x", &bls_comm_pk_x);
prover_transcript.append(b"bls_comm_pk_y", &bls_comm_pk_y);
prover_transcript.append(b"message", &message);
let protocol = PoKEcdsaSigCommittedPublicKeyProtocol::<128>::init(
&mut rng,
transformed_sig,
message,
pk,
comm_pk.clone(),
&comm_key_secp,
&comm_key_tom,
)
.unwrap();
protocol
.challenge_contribution(&mut prover_transcript)
.unwrap();
let challenge_prover = prover_transcript.challenge_scalar(b"challenge");
let proof = protocol.gen_proof(&challenge_prover);
let proof_eq_pk_x = ProofLargeWitness::<
Tom256Affine,
BlsG1Affine,
NUM_CHUNKS,
WITNESS_BIT_SIZE,
CHALLENGE_BIT_SIZE,
ABORT_PARAM,
RESPONSE_BYTE_SIZE,
NUM_REPS,
>::new(
&mut rng,
&comm_pk.x,
comm_pk.r_x,
bls_comm_pk_rx,
&comm_key_tom,
&comm_key_bls,
base,
bpp_setup_params.clone(),
&mut prover_transcript,
)
.unwrap();
let proof_eq_pk_y = ProofLargeWitness::<
Tom256Affine,
BlsG1Affine,
NUM_CHUNKS,
WITNESS_BIT_SIZE,
CHALLENGE_BIT_SIZE,
ABORT_PARAM,
RESPONSE_BYTE_SIZE,
NUM_REPS,
>::new(
&mut rng,
&comm_pk.y,
comm_pk.r_y,
bls_comm_pk_ry,
&comm_key_tom,
&comm_key_bls,
base,
bpp_setup_params.clone(),
&mut prover_transcript,
)
.unwrap();
prov_time.push(start.elapsed());
let start = Instant::now();
let mut verifier_transcript = new_merlin_transcript(b"test");
verifier_transcript.append(b"comm_key_secp", &comm_key_secp);
verifier_transcript.append(b"comm_key_tom", &comm_key_tom);
verifier_transcript.append(b"comm_key_bls", &comm_key_bls);
verifier_transcript.append(b"bpp_setup_params", &bpp_setup_params);
verifier_transcript.append(b"comm_pk", &comm_pk.comm);
verifier_transcript.append(b"bls_comm_pk_x", &bls_comm_pk_x);
verifier_transcript.append(b"bls_comm_pk_y", &bls_comm_pk_y);
verifier_transcript.append(b"message", &message);
proof
.challenge_contribution(&mut verifier_transcript)
.unwrap();
let challenge_verifier = verifier_transcript.challenge_scalar(b"challenge");
assert_eq!(challenge_prover, challenge_verifier);
proof
.verify(
message,
&comm_pk.comm,
&challenge_verifier,
&comm_key_secp,
&comm_key_tom,
)
.unwrap();
proof_eq_pk_x
.verify(
&comm_pk.comm.x,
&bls_comm_pk_x,
&comm_key_tom,
&comm_key_bls,
&bpp_setup_params,
&mut verifier_transcript,
)
.unwrap();
proof_eq_pk_y
.verify(
&comm_pk.comm.y,
&bls_comm_pk_y,
&comm_key_tom,
&comm_key_bls,
&bpp_setup_params,
&mut verifier_transcript,
)
.unwrap();
ver_time.push(start.elapsed());
if i == 0 {
println!(
"Total proof size = {} bytes",
proof.compressed_size()
+ proof_eq_pk_x.compressed_size()
+ proof_eq_pk_y.compressed_size()
);
println!(
"Proof size for equality of committed x and y coordinates = {} bytes",
proof_eq_pk_x.compressed_size() + proof_eq_pk_y.compressed_size()
);
}
}
println!("For {} iterations", num_iters);
println!("Proving time: {:?}", statistics(prov_time));
println!("Verifying time: {:?}", statistics(ver_time));
}
}