From 740a1620a435cac9ed0f91cf69f6ee19c19ddc88 Mon Sep 17 00:00:00 2001 From: iquerejeta <31273774+iquerejeta@users.noreply.github.com> Date: Thu, 1 Jul 2021 16:59:53 +0100 Subject: [PATCH] Removed EC features from chain-vote (#591) * removed backend feature and included multiscalar mult for sec2 --- chain-crypto/src/ec/p256k1.rs | 23 ++++ chain-crypto/src/ec/ristretto255.rs | 17 ++- chain-vote/src/cryptography/mod.rs | 2 +- .../zkps/unit_vector/unit_vector_zkp.rs | 101 ++++-------------- 4 files changed, 58 insertions(+), 85 deletions(-) diff --git a/chain-crypto/src/ec/p256k1.rs b/chain-crypto/src/ec/p256k1.rs index d1d9230ee..da36c335a 100644 --- a/chain-crypto/src/ec/p256k1.rs +++ b/chain-crypto/src/ec/p256k1.rs @@ -147,6 +147,29 @@ impl GroupElement { } sum } + + /// Non-optimised multiscalar multiplication. If we end up using sec2 backend, this function + /// could be optimised. + pub fn multiscalar_multiplication(scalars: I, points: J) -> Self + where + I: IntoIterator, + J: IntoIterator, + { + let mut sum = GroupElement::zero(); + for (scalar, point) in scalars.into_iter().zip(points.into_iter()) { + sum = sum + scalar * point; + } + sum + } + + /// Exposing this function in the API, even though it does not perform vartime operations. + pub fn vartime_multiscalar_multiplication(scalars: I, points: J) -> Self + where + I: IntoIterator, + J: IntoIterator, + { + multiscalar_multiplication(scalar, points) + } } impl Scalar { diff --git a/chain-crypto/src/ec/ristretto255.rs b/chain-crypto/src/ec/ristretto255.rs index 8d973cdde..b697d7ad1 100644 --- a/chain-crypto/src/ec/ristretto255.rs +++ b/chain-crypto/src/ec/ristretto255.rs @@ -12,7 +12,7 @@ use rand_core::{CryptoRng, RngCore}; use std::hash::{Hash, Hasher}; use std::ops::{Add, Mul, Sub}; -use curve25519_dalek_ng::traits::VartimeMultiscalarMul; +use curve25519_dalek_ng::traits::{MultiscalarMul, VartimeMultiscalarMul}; use std::array::TryFromSliceError; use std::convert::TryInto; @@ -81,7 +81,22 @@ impl GroupElement { } sum } + + /// Constant-time multiscalar multiplication using Straus algorithm pub fn multiscalar_multiplication(scalars: I, points: J) -> Self + where + I: IntoIterator, + J: IntoIterator, + { + GroupElement(Point::multiscalar_mul( + scalars.into_iter().map(|s| s.0), + points.into_iter().map(|p| p.0), + )) + } + + /// Variable multiscalar multiplication. This function is vulnerable to side-channel attacks and + /// should only be used when the scalars are not secret. + pub fn vartime_multiscalar_multiplication(scalars: I, points: J) -> Self where I: IntoIterator, J: IntoIterator, diff --git a/chain-vote/src/cryptography/mod.rs b/chain-vote/src/cryptography/mod.rs index 0f7412150..755039fcc 100644 --- a/chain-vote/src/cryptography/mod.rs +++ b/chain-vote/src/cryptography/mod.rs @@ -3,7 +3,7 @@ mod elgamal; mod zkps; pub(crate) use self::{ - commitment::{CommitmentKey, Open}, + commitment::CommitmentKey, elgamal::{HybridCiphertext, PublicKey, SecretKey}, zkps::{CorrectElGamalDecrZkp, UnitVectorZkp}, }; diff --git a/chain-vote/src/cryptography/zkps/unit_vector/unit_vector_zkp.rs b/chain-vote/src/cryptography/zkps/unit_vector/unit_vector_zkp.rs index 20e583102..cdc09bd78 100644 --- a/chain-vote/src/cryptography/zkps/unit_vector/unit_vector_zkp.rs +++ b/chain-vote/src/cryptography/zkps/unit_vector/unit_vector_zkp.rs @@ -8,14 +8,11 @@ use chain_core::mempack::{ReadBuf, ReadError}; use chain_crypto::ec::{GroupElement, Scalar}; use rand_core::{CryptoRng, RngCore}; -#[cfg(feature = "ristretto255")] use {rand::thread_rng, std::iter}; use super::challenge_context::ChallengeContext; use super::messages::{generate_polys, Announcement, BlindingRandomness, ResponseRandomness}; use crate::cryptography::CommitmentKey; -#[cfg(not(feature = "ristretto255"))] -use crate::cryptography::Open; use crate::cryptography::{Ciphertext, PublicKey}; use crate::encrypted_vote::{binrep, Ptp, UnitVector}; use crate::tally::Crs; @@ -169,7 +166,6 @@ impl Zkp { /// Final verification of the proof, that we compute in a single vartime multiscalar /// multiplication. - #[cfg(feature = "ristretto255")] fn verify_statements( &self, public_key: &PublicKey, @@ -193,111 +189,50 @@ impl Zkp { let batch_challenge = Scalar::random(&mut thread_rng()); for (zwv, iba) in self.zwvs.iter().zip(self.ibas.iter()) { - if GroupElement::multiscalar_multiplication( - iter::once(zwv.z) - .chain(iter::once(zwv.w + batch_challenge * zwv.v)) + if GroupElement::vartime_multiscalar_multiplication( + iter::once(zwv.z.clone()) + .chain(iter::once(&zwv.w + &batch_challenge * &zwv.v)) .chain(iter::once( - batch_challenge * (zwv.z - challenge_x) - challenge_x, + &batch_challenge * (&zwv.z - challenge_x) - challenge_x, )) .chain(iter::once(Scalar::one().negate())) .chain(iter::once(batch_challenge.negate())), iter::once(GroupElement::generator()) - .chain(iter::once(commitment_key.h)) - .chain(iter::once(iba.i)) - .chain(iter::once(iba.b)) - .chain(iter::once(iba.a)), + .chain(iter::once(commitment_key.h.clone())) + .chain(iter::once(iba.i.clone())) + .chain(iter::once(iba.b.clone())) + .chain(iter::once(iba.a.clone())), ) != GroupElement::zero() { return false; } } - let mega_check = GroupElement::multiscalar_multiplication( + let mega_check = GroupElement::vartime_multiscalar_multiplication( powers_cy + .clone() .take(length) - .map(|s| s * cx_pow) - .chain(powers_cy.take(length).map(|s| s * cx_pow)) + .map(|s| s * &cx_pow) + .chain(powers_cy.clone().take(length).map(|s| s * &cx_pow)) .chain(powers_cy.take(length)) - .chain(powers_cx.take(bits)) + .chain(powers_cx.clone().take(bits)) .chain(powers_cx.take(bits)) .chain(iter::once(Scalar::one().negate())) .chain(iter::once(Scalar::one().negate())), ciphertexts .iter() - .map(|ctxt| ctxt.e2) - .chain(ciphertexts.iter().map(|ctxt| ctxt.e1)) + .map(|ctxt| ctxt.e2.clone()) + .chain(ciphertexts.iter().map(|ctxt| ctxt.e1.clone())) .chain(powers_z_iterator.take(length)) - .chain(self.ds.iter().map(|ctxt| ctxt.e1)) - .chain(self.ds.iter().map(|ctxt| ctxt.e2)) - .chain(iter::once(zero.e1)) + .chain(self.ds.iter().map(|ctxt| ctxt.e1.clone())) + .chain(self.ds.iter().map(|ctxt| ctxt.e2.clone())) + .chain(iter::once(zero.e1.clone())) .chain(iter::once(zero.e2)), ); mega_check == GroupElement::zero() } - // Final verification of the proof. We do not use the multiscalar optimisation when using sec2 curves. - #[cfg(not(feature = "ristretto255"))] - fn verify_statements( - &self, - public_key: &PublicKey, - commitment_key: &CommitmentKey, - ciphertexts: &Ptp, - challenge_x: &Scalar, - challenge_y: &Scalar, - ) -> bool { - // check commitments are 0 / 1 - for (iba, zwv) in self.ibas.iter().zip(self.zwvs.iter()) { - if !commitment_key.verify( - &(&iba.i * challenge_x + &iba.b), - &Open { - m: zwv.z.clone(), - r: zwv.w.clone(), - }, - ) { - return false; - } - - if !commitment_key.verify( - &(&iba.i * (challenge_x - &zwv.z) + &iba.a), - &Open { - m: Scalar::zero(), - r: zwv.v.clone(), - }, - ) { - return false; - } - } - - let bits = ciphertexts.bits(); - let cx_pow = challenge_x.power(bits); - - let p1 = ciphertexts - .as_ref() - .iter() - .zip(challenge_y.exp_iter()) - .enumerate() - .fold(Ciphertext::zero(), |acc, (i, (c, cy_pows))| { - let multz = powers_z_encs(&self.zwvs, challenge_x.clone(), i, bits as u32); - let enc = public_key.encrypt_with_r(&multz.negate(), &Scalar::zero()); - let mult_c = c * &cx_pow; - let t = (&mult_c + &enc) * cy_pows; - &acc + &t - }); - - let dsum = self - .ds - .iter() - .zip(challenge_x.exp_iter()) - .fold(Ciphertext::zero(), |acc, (d, cx_pows)| { - &acc + &(d * cx_pows) - }); - - let zero = public_key.encrypt_with_r(&Scalar::zero(), self.r()); - - &p1 + &dsum - zero == Ciphertext::zero() - } - /// Try to generate a `Proof` from a buffer pub fn from_buffer(buf: &mut ReadBuf) -> Result { let bits = buf.get_u8()? as usize;