Skip to content

Commit

Permalink
Add reduce_512 to offer efficient reduction of 64 bytes into a scalar
Browse files Browse the repository at this point in the history
  • Loading branch information
kayabaNerve committed Jan 6, 2025
1 parent cbb5ffa commit abca01f
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 54 deletions.
6 changes: 6 additions & 0 deletions crypto/ciphersuite/src/dalek.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ macro_rules! dalek_curve {
$Point::generator()
}

fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
let res = Scalar::from_bytes_mod_order_wide(&scalar);
scalar.zeroize();
res
}

fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
Scalar::from_hash(Sha512::new_with_prefix(&[dst, data].concat()))
}
Expand Down
6 changes: 6 additions & 0 deletions crypto/ciphersuite/src/ed448.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ impl Ciphersuite for Ed448 {
Point::generator()
}

fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
let res = Self::hash_to_F(b"Ciphersuite-reduce_512", &scalar);
scalar.zeroize();
res
}

fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
Scalar::wide_reduce(Self::H::digest([dst, data].concat()).as_ref().try_into().unwrap())
}
Expand Down
12 changes: 12 additions & 0 deletions crypto/ciphersuite/src/helioselene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ impl Ciphersuite for Helios {
<HeliosPoint as Group>::generator()
}

fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
let res = HelioseleneField::wide_reduce(scalar);
scalar.zeroize();
res
}

fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
let mut uniform = [0; 64];
let mut hash = Blake2b512::digest([dst, msg].concat());
Expand All @@ -46,6 +52,12 @@ impl Ciphersuite for Selene {
<SelenePoint as Group>::generator()
}

fn reduce_512(mut scalar: [u8; 64]) -> Self::F {
let res = Field25519::wide_reduce(scalar);
scalar.zeroize();
res
}

fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
let mut uniform = [0; 64];
let mut hash = Blake2b512::digest([dst, msg].concat());
Expand Down
18 changes: 17 additions & 1 deletion crypto/ciphersuite/src/kp256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use group::ff::PrimeField;

use elliptic_curve::{
generic_array::GenericArray,
bigint::{NonZero, CheckedAdd, Encoding, U384},
bigint::{NonZero, CheckedAdd, Encoding, U384, U512},
hash2curve::{Expander, ExpandMsg, ExpandMsgXmd},
};

Expand All @@ -31,6 +31,22 @@ macro_rules! kp_curve {
$lib::ProjectivePoint::GENERATOR
}

fn reduce_512(scalar: [u8; 64]) -> Self::F {
let mut modulus = [0; 64];
modulus[32 ..].copy_from_slice(&(Self::F::ZERO - Self::F::ONE).to_bytes());
let modulus = U512::from_be_slice(&modulus).checked_add(&U512::ONE).unwrap();

let mut wide =
U512::from_be_bytes(scalar).rem(&NonZero::new(modulus).unwrap()).to_be_bytes();

let mut array = *GenericArray::from_slice(&wide[32 ..]);
let res = $lib::Scalar::from_repr(array).unwrap();

wide.zeroize();
array.zeroize();
res
}

fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
// While one of these two libraries does support directly hashing to the Scalar field, the
// other doesn't. While that's probably an oversight, this is a universally working method
Expand Down
7 changes: 7 additions & 0 deletions crypto/ciphersuite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ pub trait Ciphersuite:
// While group does provide this in its API, privacy coins may want to use a custom basepoint
fn generator() -> Self::G;

/// Reduce 512 bits into a uniform scalar.
///
/// If 512 bits is insufficient to perform a reduction into a uniform scalar, the ciphersuite
/// will perform a hash to sample the necessary bits.
#[allow(non_snake_case)]
fn reduce_512(scalar: [u8; 64]) -> Self::F;

/// Hash the provided domain-separation tag and message to a scalar. Ciphersuites MAY naively
/// prefix the tag to the message, enabling transpotion between the two. Accordingly, this
/// function should NOT be used in any scheme where one tag is a valid substring of another
Expand Down
4 changes: 4 additions & 0 deletions crypto/dkg/src/tests/promote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ impl<C: Ciphersuite> Ciphersuite for AltGenerator<C> {
C::G::generator() * <C as Ciphersuite>::hash_to_F(b"DKG Promotion Test", b"generator")
}

fn reduce_512(scalar: [u8; 64]) -> Self::F {
<C as Ciphersuite>::reduce_512(scalar)
}

fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
<C as Ciphersuite>::hash_to_F(dst, data)
}
Expand Down
15 changes: 6 additions & 9 deletions crypto/fcmps/circuit-abstraction/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ use std_shims::{vec, vec::Vec};

use zeroize::{Zeroize, ZeroizeOnDrop};

use ciphersuite::{
group::ff::{Field, PrimeField},
Ciphersuite,
};
use ciphersuite::{group::ff::Field, Ciphersuite};

use generalized_bulletproofs::{
ScalarVector, PedersenCommitment, PedersenVectorCommitment, ProofGenerators,
Expand All @@ -29,7 +26,7 @@ pub trait Transcript {
///
/// It is the caller's responsibility to have properly transcripted all variables prior to
/// sampling this challenge.
fn challenge<F: PrimeField>(&mut self) -> F;
fn challenge<C: Ciphersuite>(&mut self) -> C::F;

/// Sample a challenge as a byte array.
///
Expand All @@ -38,16 +35,16 @@ pub trait Transcript {
fn challenge_bytes(&mut self) -> [u8; 64];
}
impl Transcript for ProverTranscript {
fn challenge<F: PrimeField>(&mut self) -> F {
self.challenge()
fn challenge<C: Ciphersuite>(&mut self) -> C::F {
self.challenge::<C>()
}
fn challenge_bytes(&mut self) -> [u8; 64] {
self.challenge_bytes()
}
}
impl Transcript for VerifierTranscript<'_> {
fn challenge<F: PrimeField>(&mut self) -> F {
self.challenge()
fn challenge<C: Ciphersuite>(&mut self) -> C::F {
self.challenge::<C>()
}
fn challenge_bytes(&mut self) -> [u8; 64] {
self.challenge_bytes()
Expand Down
4 changes: 2 additions & 2 deletions crypto/fcmps/ec-gadgets/src/dlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ impl<C: Ciphersuite> EcDlogGadgets<C> for Circuit<C> {
let sign_of_point_0 = (sign_of_points[0] & 1) == 1;
let sign_of_point_1 = ((sign_of_points[0] >> 1) & 1) == 1;
let (c0_x, c0_y) = loop {
let c0_x: C::F = transcript.challenge();
let c0_x = transcript.challenge::<C>();
let Some(c0_y) =
Option::<C::F>::from(((c0_x.square() * c0_x) + (curve.a * c0_x) + curve.b).sqrt())
else {
Expand All @@ -367,7 +367,7 @@ impl<C: Ciphersuite> EcDlogGadgets<C> for Circuit<C> {
break (c0_x, if bool::from(c0_y.is_odd()) != sign_of_point_0 { -c0_y } else { c0_y });
};
let (c1_x, c1_y) = loop {
let c1_x: C::F = transcript.challenge();
let c1_x = transcript.challenge::<C>();
let Some(c1_y) =
Option::<C::F>::from(((c1_x.square() * c1_x) + (curve.a * c1_x) + curve.b).sqrt())
else {
Expand Down
4 changes: 2 additions & 2 deletions crypto/fcmps/src/gadgets/interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ impl<C: Ciphersuite> Circuit<C> {
}

// Create challenges which we use to aggregate tuples into LinCombs
let mut challenges: Vec<C::F> = vec![];
let mut challenges = vec![];
for _ in 0 .. member.len() {
challenges.push(transcript.challenge());
challenges.push(transcript.challenge::<C>());
}

// Aggregate the claimed member
Expand Down
12 changes: 6 additions & 6 deletions crypto/fcmps/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,13 +644,13 @@ where

let mut root_blind_pok = [0; 64];
if matches!(tree, TreeRoot::C1(_)) {
let s = *root_blind_r_C1.unwrap() +
(transcript.challenge::<<C::C1 as Ciphersuite>::F>() * root_blind_C1.unwrap());
let s =
*root_blind_r_C1.unwrap() + (transcript.challenge::<C::C1>() * root_blind_C1.unwrap());
root_blind_pok[.. 32].copy_from_slice(&root_blind_R);
root_blind_pok[32 ..].copy_from_slice(s.to_repr().as_ref());
} else {
let s = *root_blind_r_C2.unwrap() +
(transcript.challenge::<<C::C2 as Ciphersuite>::F>() * root_blind_C2.unwrap());
let s =
*root_blind_r_C2.unwrap() + (transcript.challenge::<C::C2>() * root_blind_C2.unwrap());
root_blind_pok[.. 32].copy_from_slice(&root_blind_R);
root_blind_pok[32 ..].copy_from_slice(s.to_repr().as_ref());
}
Expand Down Expand Up @@ -912,7 +912,7 @@ where
let R = <C::C1 as Ciphersuite>::read_G(&mut self.root_blind_pok[.. 32].as_ref())?;
let s = <C::C1 as Ciphersuite>::read_F(&mut self.root_blind_pok[32 ..].as_ref())?;

let c: <C::C1 as Ciphersuite>::F = transcript.challenge();
let c = transcript.challenge::<C::C1>();

// R + cX == sH, where X is the difference in the roots
// (which should only be the randomness, and H is the generator for the randomness)
Expand All @@ -925,7 +925,7 @@ where
let R = <C::C2 as Ciphersuite>::read_G(&mut self.root_blind_pok[.. 32].as_ref())?;
let s = <C::C2 as Ciphersuite>::read_F(&mut self.root_blind_pok[32 ..].as_ref())?;

let c: <C::C2 as Ciphersuite>::F = transcript.challenge();
let c = transcript.challenge::<C::C2>();

// R + cX == sH, where X is the difference in the roots
// (which should only be the randomness, and H is the generator for the randomness)
Expand Down
16 changes: 8 additions & 8 deletions crypto/generalized-bulletproofs/src/arithmetic_circuit_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,8 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
transcript.push_point(AI);
transcript.push_point(AO);
transcript.push_point(S);
let y = transcript.challenge();
let z = transcript.challenge();
let y = transcript.challenge::<C>();
let z = transcript.challenge::<C>();
let YzChallenges { y_inv, z } = self.yz_challenges(y, z);
let y = ScalarVector::powers(y, n);

Expand Down Expand Up @@ -416,7 +416,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
transcript.push_point(multiexp(&[(*t, self.generators.g()), (*tau, self.generators.h())]));
}

let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge(), t.len());
let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge::<C>(), t.len());

let poly_eval = |poly: &[ScalarVector<C::F>], x: &ScalarVector<_>| -> ScalarVector<_> {
let mut res = ScalarVector::<C::F>::new(poly[0].0.len());
Expand Down Expand Up @@ -480,7 +480,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
transcript.push_scalar(tau_x);
transcript.push_scalar(u);
transcript.push_scalar(t_caret);
let ip_x = transcript.challenge();
let ip_x = transcript.challenge::<C>();
P_terms.push((ip_x * t_caret, self.generators.g()));
IpStatement::new(
self.generators,
Expand Down Expand Up @@ -523,8 +523,8 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
let AI = transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?;
let AO = transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?;
let S = transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?;
let y = transcript.challenge();
let z = transcript.challenge();
let y = transcript.challenge::<C>();
let z = transcript.challenge::<C>();
let YzChallenges { y_inv, z } = self.yz_challenges(y, z);

let mut l_weights = ScalarVector::new(n);
Expand All @@ -547,7 +547,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
for _ in 0 .. (t_poly_len - ni - 1) {
T_after_ni.push(transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?);
}
let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge(), t_poly_len);
let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge::<C>(), t_poly_len);

let tau_x = transcript.read_scalar::<C>().map_err(|_| AcError::IncompleteProof)?;
let u = transcript.read_scalar::<C>().map_err(|_| AcError::IncompleteProof)?;
Expand Down Expand Up @@ -645,7 +645,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {

// Prove for lines 88, 92 with an Inner-Product statement
// This inlines Protocol 1, as our IpStatement implements Protocol 2
let ip_x = transcript.challenge();
let ip_x = transcript.challenge::<C>();
// P is amended with this additional term
verifier.g += verifier_weight * ip_x * t_caret;
IpStatement::new(self.generators, y_inv, ip_x, P::Verifier { verifier_weight })
Expand Down
4 changes: 2 additions & 2 deletions crypto/generalized-bulletproofs/src/inner_product.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl<'a, C: Ciphersuite> IpStatement<'a, C> {
// Now that we've calculate L, R, transcript them to receive x (26-27)
transcript.push_point(L);
transcript.push_point(R);
let x: C::F = transcript.challenge();
let x: C::F = transcript.challenge::<C>();
let x_inv = x.invert().unwrap();

// The prover and verifier now calculate the following (28-31)
Expand Down Expand Up @@ -304,7 +304,7 @@ impl<'a, C: Ciphersuite> IpStatement<'a, C> {
for _ in 0 .. lr_len {
L.push(transcript.read_point::<C>().map_err(|_| IpError::IncompleteProof)?);
R.push(transcript.read_point::<C>().map_err(|_| IpError::IncompleteProof)?);
xs.push(transcript.challenge());
xs.push(transcript.challenge::<C>());
}

// We calculate their inverse in batch
Expand Down
36 changes: 12 additions & 24 deletions crypto/generalized-bulletproofs/src/transcript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use std_shims::{vec::Vec, io};
use blake2::{Digest, Blake2b512};

use ciphersuite::{
group::{ff::PrimeField, GroupEncoding},
group::{
ff::{Field, PrimeField},
GroupEncoding,
},
Ciphersuite,
};

Expand All @@ -13,26 +16,11 @@ const SCALAR: u8 = 0;
const POINT: u8 = 1;
const CHALLENGE: u8 = 2;

fn challenge<F: PrimeField>(digest: &mut Blake2b512) -> F {
// Ensure this field is small enough this is a successful wide reduction
assert!(F::NUM_BITS <= (512 - 128));

fn challenge<C: Ciphersuite>(digest: &mut Blake2b512) -> C::F {
digest.update([CHALLENGE]);
let chl = digest.clone().finalize();

let mut res = F::ZERO;
for (i, mut byte) in chl.iter().cloned().enumerate() {
for j in 0 .. 8 {
let lsb = byte & 1;
let mut bit = F::from(u64::from(lsb));
for _ in 0 .. ((i * 8) + j) {
bit = bit.double();
}
res += bit;

byte >>= 1;
}
}
let chl = digest.clone().finalize().into();

let res = C::reduce_512(chl);

// Negligible probability
if bool::from(res.is_zero()) {
Expand Down Expand Up @@ -113,8 +101,8 @@ impl Transcript {
}

/// Sample a challenge.
pub fn challenge<F: PrimeField>(&mut self) -> F {
challenge(&mut self.digest)
pub fn challenge<C: Ciphersuite>(&mut self) -> C::F {
challenge::<C>(&mut self.digest)
}

/// Sample a challenge as a byte array.
Expand Down Expand Up @@ -182,8 +170,8 @@ impl<'a> VerifierTranscript<'a> {
}

/// Sample a challenge.
pub fn challenge<F: PrimeField>(&mut self) -> F {
challenge(&mut self.digest)
pub fn challenge<C: Ciphersuite>(&mut self) -> C::F {
challenge::<C>(&mut self.digest)
}

/// Sample a challenge as a byte array.
Expand Down
4 changes: 4 additions & 0 deletions networks/monero/ringct/fcmp++/src/sal/multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ impl Ciphersuite for Ed25519T {
EdwardsPoint(T())
}

fn reduce_512(scalar: [u8; 64]) -> Self::F {
<Ed25519 as Ciphersuite>::reduce_512(scalar)
}

fn hash_to_F(dst: &[u8], data: &[u8]) -> Self::F {
<Ed25519 as Ciphersuite>::hash_to_F(dst, data)
}
Expand Down

0 comments on commit abca01f

Please sign in to comment.