Move constant-time scalar mul gadget divisor creation from dkg to ec-divisors

Anyone creating a divisor for the scalar mul gadget should use constant time
code, so this code should at least be in the EC gadgets crate It's of
non-trivial complexity to deal with otherwise.
This commit is contained in:
Luke Parker
2024-09-24 03:30:54 -04:00
parent 1ea7cb8b5b
commit 0fbc8e7f9c
7 changed files with 232 additions and 213 deletions

1
Cargo.lock generated
View File

@@ -2293,6 +2293,7 @@ name = "ec-divisors"
version = "0.1.0"
dependencies = [
"dalek-ff-group",
"ff",
"group",
"hex",
"pasta_curves",

View File

@@ -35,7 +35,7 @@ impl_modulus!(
type ResidueType = Residue<FieldModulus, { FieldModulus::LIMBS }>;
/// A constant-time implementation of the Ed25519 field.
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
pub struct FieldElement(ResidueType);
// Square root of -1.

View File

@@ -37,7 +37,6 @@ schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5
dleq = { path = "../dleq", version = "^0.4.1", default-features = false }
# eVRF DKG dependencies
subtle = { version = "2", default-features = false, features = ["std"], optional = true }
generic-array = { version = "1", default-features = false, features = ["alloc"], optional = true }
blake2 = { version = "0.10", default-features = false, features = ["std"], optional = true }
rand_chacha = { version = "0.3", default-features = false, features = ["std"], optional = true }
@@ -82,7 +81,6 @@ borsh = ["dep:borsh"]
evrf = [
"std",
"dep:subtle",
"dep:generic-array",
"dep:blake2",

View File

@@ -1,6 +1,5 @@
use core::{marker::PhantomData, ops::Deref, fmt};
use subtle::*;
use zeroize::{Zeroize, Zeroizing};
use rand_core::{RngCore, CryptoRng, SeedableRng};
@@ -10,10 +9,7 @@ use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
use blake2::{Digest, Blake2s256};
use ciphersuite::{
group::{
ff::{Field, PrimeField, PrimeFieldBits},
Group, GroupEncoding,
},
group::{ff::Field, Group, GroupEncoding},
Ciphersuite,
};
@@ -24,7 +20,7 @@ use generalized_bulletproofs::{
};
use generalized_bulletproofs_circuit_abstraction::*;
use ec_divisors::{DivisorCurve, new_divisor};
use ec_divisors::{DivisorCurve, ScalarDecomposition};
use generalized_bulletproofs_ec_gadgets::*;
/// A pair of curves to perform the eVRF with.
@@ -309,147 +305,6 @@ impl<C: EvrfCurve> Evrf<C> {
debug_assert!(challenged_generators.next().is_none());
}
/// Convert a scalar to a sequence of coefficients for the polynomial 2**i, where the sum of the
/// coefficients is F::NUM_BITS.
///
/// Despite the name, the returned coefficients are not guaranteed to be bits (0 or 1).
///
/// This scalar will presumably be used in a discrete log proof. That requires calculating a
/// divisor which is variable time to the amount of points interpolated. Since the amount of
/// points interpolated is equal to the sum of the coefficients in the polynomial, we need all
/// scalars to have a constant sum of their coefficients (instead of one variable to its bits).
///
/// We achieve this by finding the highest non-0 coefficient, decrementing it, and increasing the
/// immediately less significant coefficient by 2. This increases the sum of the coefficients by
/// 1 (-1+2=1).
fn scalar_to_bits(scalar: &<C::EmbeddedCurve as Ciphersuite>::F) -> Vec<u64> {
let num_bits = u64::from(<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F::NUM_BITS);
// Obtain the bits of the private key
let num_bits_usize = usize::try_from(num_bits).unwrap();
let mut decomposition = vec![0; num_bits_usize];
for (i, bit) in scalar.to_le_bits().into_iter().take(num_bits_usize).enumerate() {
let bit = u64::from(u8::from(bit));
decomposition[i] = bit;
}
// The following algorithm only works if the value of the scalar exceeds num_bits
// If it isn't, we increase it by the modulus such that it does exceed num_bits
{
let mut less_than_num_bits = Choice::from(0);
for i in 0 .. num_bits {
less_than_num_bits |= scalar.ct_eq(&<C::EmbeddedCurve as Ciphersuite>::F::from(i));
}
let mut decomposition_of_modulus = vec![0; num_bits_usize];
// Decompose negative one
for (i, bit) in (-<C::EmbeddedCurve as Ciphersuite>::F::ONE)
.to_le_bits()
.into_iter()
.take(num_bits_usize)
.enumerate()
{
let bit = u64::from(u8::from(bit));
decomposition_of_modulus[i] = bit;
}
// Increment it by one
decomposition_of_modulus[0] += 1;
// Add the decomposition onto the decomposition of the modulus
for i in 0 .. num_bits_usize {
let new_decomposition = <_>::conditional_select(
&decomposition[i],
&(decomposition[i] + decomposition_of_modulus[i]),
less_than_num_bits,
);
decomposition[i] = new_decomposition;
}
}
// Calculcate the sum of the coefficients
let mut sum_of_coefficients: u64 = 0;
for decomposition in &decomposition {
sum_of_coefficients += *decomposition;
}
/*
Now, because we added a log2(k)-bit number to a k-bit number, we may have our sum of
coefficients be *too high*. We attempt to reduce the sum of the coefficients accordingly.
This algorithm is guaranteed to complete as expected. Take the sequence `222`. `222` becomes
`032` becomes `013`. Even if the next coefficient in the sequence is `2`, the third
coefficient will be reduced once and the next coefficient (`2`, increased to `3`) will only
be eligible for reduction once. This demonstrates, even for a worst case of log2(k) `2`s
followed by `1`s (as possible if the modulus is a Mersenne prime), the log2(k) `2`s can be
reduced as necessary so long as there is a single coefficient after (requiring the entire
sequence be at least of length log2(k) + 1). For a 2-bit number, log2(k) + 1 == 2, so this
holds for any odd prime field.
To fully type out the demonstration for the Mersenne prime 3, with scalar to encode 1 (the
highest value less than the number of bits):
10 - Little-endian bits of 1
21 - Little-endian bits of 1, plus the modulus
02 - After one reduction, where the sum of the coefficients does in fact equal 2 (the target)
*/
{
let mut log2_num_bits = 0;
while (1 << log2_num_bits) < num_bits {
log2_num_bits += 1;
}
for _ in 0 .. log2_num_bits {
// If the sum of coefficients is the amount of bits, we're done
let mut done = sum_of_coefficients.ct_eq(&num_bits);
for i in 0 .. (num_bits_usize - 1) {
let should_act = (!done) & decomposition[i].ct_gt(&1);
// Subtract 2 from this coefficient
let amount_to_sub = <_>::conditional_select(&0, &2, should_act);
decomposition[i] -= amount_to_sub;
// Add 1 to the next coefficient
let amount_to_add = <_>::conditional_select(&0, &1, should_act);
decomposition[i + 1] += amount_to_add;
// Also update the sum of coefficients
sum_of_coefficients -= <_>::conditional_select(&0, &1, should_act);
// If we updated the coefficients this loop iter, we're done for this loop iter
done |= should_act;
}
}
}
for _ in 0 .. num_bits {
// If the sum of coefficients is the amount of bits, we're done
let mut done = sum_of_coefficients.ct_eq(&num_bits);
// Find the highest coefficient currently non-zero
for i in (1 .. decomposition.len()).rev() {
// If this is non-zero, we should decrement this coefficient if we haven't already
// decremented a coefficient this round
let is_non_zero = !(0.ct_eq(&decomposition[i]));
let should_act = (!done) & is_non_zero;
// Update this coefficient and the prior coefficient
let amount_to_sub = <_>::conditional_select(&0, &1, should_act);
decomposition[i] -= amount_to_sub;
let amount_to_add = <_>::conditional_select(&0, &2, should_act);
// i must be at least 1, so i - 1 will be at least 0 (meaning it's safe to index with)
decomposition[i - 1] += amount_to_add;
// Also update the sum of coefficients
sum_of_coefficients += <_>::conditional_select(&0, &1, should_act);
// If we updated the coefficients this loop iter, we're done for this loop iter
done |= should_act;
}
}
debug_assert!(bool::from(decomposition.iter().sum::<u64>().ct_eq(&num_bits)));
decomposition
}
/// Prove a point on an elliptic curve had its discrete logarithm generated via an eVRF.
pub(crate) fn prove(
rng: &mut (impl RngCore + CryptoRng),
@@ -471,11 +326,9 @@ impl<C: EvrfCurve> Evrf<C> {
// A function to calculate a divisor and push it onto the tape
// This defines a vec, divisor_points, outside of the fn to reuse its allocation
let mut divisor_points =
Vec::with_capacity((<C::EmbeddedCurve as Ciphersuite>::F::NUM_BITS as usize) + 1);
let mut divisor =
|vector_commitment_tape: &mut Vec<_>,
dlog: &[u64],
dlog: &ScalarDecomposition<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
push_generator: bool,
generator: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G,
dh: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G| {
@@ -484,24 +337,7 @@ impl<C: EvrfCurve> Evrf<C> {
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
}
{
let mut generator = generator;
for coefficient in dlog {
let mut coefficient = *coefficient;
while coefficient != 0 {
coefficient -= 1;
divisor_points.push(generator);
}
generator = generator.double();
}
debug_assert_eq!(
dlog.iter().sum::<u64>(),
u64::from(<C::EmbeddedCurve as Ciphersuite>::F::NUM_BITS)
);
}
divisor_points.push(-dh);
let mut divisor = new_divisor(&divisor_points).unwrap().normalize_x_coefficient();
divisor_points.zeroize();
let mut divisor = dlog.scalar_mul_divisor(generator).normalize_x_coefficient();
vector_commitment_tape.push(divisor.zero_coefficient);
@@ -540,11 +376,12 @@ impl<C: EvrfCurve> Evrf<C> {
let evrf_public_key;
let mut actual_coefficients = Vec::with_capacity(coefficients);
{
let mut dlog = Self::scalar_to_bits(evrf_private_key);
let dlog =
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(**evrf_private_key);
let points = Self::transcript_to_points(transcript, coefficients);
// Start by pushing the discrete logarithm onto the tape
for coefficient in &dlog {
for coefficient in dlog.decomposition() {
vector_commitment_tape.push(<_>::from(*coefficient));
}
@@ -573,8 +410,6 @@ impl<C: EvrfCurve> Evrf<C> {
actual_coefficients.push(res);
}
debug_assert_eq!(actual_coefficients.len(), coefficients);
dlog.zeroize();
}
// Now do the ECDHs for the encryption
@@ -595,14 +430,15 @@ impl<C: EvrfCurve> Evrf<C> {
break;
}
}
let mut dlog = Self::scalar_to_bits(&ecdh_private_key);
let dlog =
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(ecdh_private_key);
let ecdh_commitment = <C::EmbeddedCurve as Ciphersuite>::generator() * ecdh_private_key;
ecdh_commitments.push(ecdh_commitment);
ecdh_commitments_xy.last_mut().unwrap()[j] =
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_commitment).unwrap();
// Start by pushing the discrete logarithm onto the tape
for coefficient in &dlog {
for coefficient in dlog.decomposition() {
vector_commitment_tape.push(<_>::from(*coefficient));
}
@@ -625,7 +461,6 @@ impl<C: EvrfCurve> Evrf<C> {
*res += dh_x;
ecdh_private_key.zeroize();
dlog.zeroize();
}
encryption_masks.push(res);
}

View File

@@ -14,10 +14,11 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
rand_core = { version = "0.6", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
zeroize = { version = "^1.5", default-features = false, features = ["std", "zeroize_derive"] }
subtle = { version = "2", default-features = false, features = ["std"] }
group = "0.13"
ff = { version = "0.13", default-features = false, features = ["std", "bits"] }
group = { version = "0.13", default-features = false }
hex = { version = "0.4", optional = true }
dalek-ff-group = { path = "../../dalek-ff-group", features = ["std"], optional = true }

View File

@@ -3,15 +3,16 @@
#![deny(missing_docs)]
#![allow(non_snake_case)]
use subtle::{Choice, ConstantTimeEq, ConditionallySelectable};
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConditionallySelectable};
use zeroize::{Zeroize, ZeroizeOnDrop};
use group::{
ff::{Field, PrimeField},
ff::{Field, PrimeField, PrimeFieldBits},
Group,
};
mod poly;
pub use poly::*;
pub use poly::Poly;
#[cfg(test)]
mod tests;
@@ -19,7 +20,7 @@ mod tests;
/// A curve usable with this library.
pub trait DivisorCurve: Group + ConstantTimeEq + ConditionallySelectable {
/// An element of the field this curve is defined over.
type FieldElement: PrimeField + ConditionallySelectable;
type FieldElement: Zeroize + PrimeField + ConditionallySelectable;
/// The A in the curve equation y^2 = x^3 + A x + B.
fn a() -> Self::FieldElement;
@@ -257,6 +258,186 @@ pub fn new_divisor<C: DivisorCurve>(points: &[C]) -> Option<Poly<C::FieldElement
Some(divisor)
}
/// The decomposition of a scalar.
///
/// The decomposition ($d$) of a scalar ($s$) has the following two properties:
///
/// - $\sum^{\mathsf{NUM_BITS} - 1}_{i=0} d_i * 2^i = s$
/// - $\sum^{\mathsf{NUM_BITS} - 1}_{i=0} d_i = \mathsf{NUM_BITS}$
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct ScalarDecomposition<F: Zeroize + PrimeFieldBits> {
scalar: F,
decomposition: Vec<u64>,
}
impl<F: Zeroize + PrimeFieldBits> ScalarDecomposition<F> {
/// Decompose a scalar.
pub fn new(scalar: F) -> Self {
/*
We need the sum of the coefficients to equal F::NUM_BITS. The scalar's bits will be less than
F::NUM_BITS. Accordingly, we need to increment the sum of the coefficients without
incrementing the scalar represented. We do this by finding the highest non-0 coefficient,
decrementing it, and increasing the immediately less significant coefficient by 2. This
increases the sum of the coefficients by 1 (-1+2=1).
*/
let num_bits = u64::from(F::NUM_BITS);
// Obtain the bits of the scalar
let num_bits_usize = usize::try_from(num_bits).unwrap();
let mut decomposition = vec![0; num_bits_usize];
for (i, bit) in scalar.to_le_bits().into_iter().take(num_bits_usize).enumerate() {
let bit = u64::from(u8::from(bit));
decomposition[i] = bit;
}
// The following algorithm only works if the value of the scalar exceeds num_bits
// If it isn't, we increase it by the modulus such that it does exceed num_bits
{
let mut less_than_num_bits = Choice::from(0);
for i in 0 .. num_bits {
less_than_num_bits |= scalar.ct_eq(&F::from(i));
}
let mut decomposition_of_modulus = vec![0; num_bits_usize];
// Decompose negative one
for (i, bit) in (-F::ONE).to_le_bits().into_iter().take(num_bits_usize).enumerate() {
let bit = u64::from(u8::from(bit));
decomposition_of_modulus[i] = bit;
}
// Increment it by one
decomposition_of_modulus[0] += 1;
// Add the decomposition onto the decomposition of the modulus
for i in 0 .. num_bits_usize {
let new_decomposition = <_>::conditional_select(
&decomposition[i],
&(decomposition[i] + decomposition_of_modulus[i]),
less_than_num_bits,
);
decomposition[i] = new_decomposition;
}
}
// Calculcate the sum of the coefficients
let mut sum_of_coefficients: u64 = 0;
for decomposition in &decomposition {
sum_of_coefficients += *decomposition;
}
/*
Now, because we added a log2(k)-bit number to a k-bit number, we may have our sum of
coefficients be *too high*. We attempt to reduce the sum of the coefficients accordingly.
This algorithm is guaranteed to complete as expected. Take the sequence `222`. `222` becomes
`032` becomes `013`. Even if the next coefficient in the sequence is `2`, the third
coefficient will be reduced once and the next coefficient (`2`, increased to `3`) will only
be eligible for reduction once. This demonstrates, even for a worst case of log2(k) `2`s
followed by `1`s (as possible if the modulus is a Mersenne prime), the log2(k) `2`s can be
reduced as necessary so long as there is a single coefficient after (requiring the entire
sequence be at least of length log2(k) + 1). For a 2-bit number, log2(k) + 1 == 2, so this
holds for any odd prime field.
To fully type out the demonstration for the Mersenne prime 3, with scalar to encode 1 (the
highest value less than the number of bits):
10 - Little-endian bits of 1
21 - Little-endian bits of 1, plus the modulus
02 - After one reduction, where the sum of the coefficients does in fact equal 2 (the target)
*/
{
let mut log2_num_bits = 0;
while (1 << log2_num_bits) < num_bits {
log2_num_bits += 1;
}
for _ in 0 .. log2_num_bits {
// If the sum of coefficients is the amount of bits, we're done
let mut done = sum_of_coefficients.ct_eq(&num_bits);
for i in 0 .. (num_bits_usize - 1) {
let should_act = (!done) & decomposition[i].ct_gt(&1);
// Subtract 2 from this coefficient
let amount_to_sub = <_>::conditional_select(&0, &2, should_act);
decomposition[i] -= amount_to_sub;
// Add 1 to the next coefficient
let amount_to_add = <_>::conditional_select(&0, &1, should_act);
decomposition[i + 1] += amount_to_add;
// Also update the sum of coefficients
sum_of_coefficients -= <_>::conditional_select(&0, &1, should_act);
// If we updated the coefficients this loop iter, we're done for this loop iter
done |= should_act;
}
}
}
for _ in 0 .. num_bits {
// If the sum of coefficients is the amount of bits, we're done
let mut done = sum_of_coefficients.ct_eq(&num_bits);
// Find the highest coefficient currently non-zero
for i in (1 .. decomposition.len()).rev() {
// If this is non-zero, we should decrement this coefficient if we haven't already
// decremented a coefficient this round
let is_non_zero = !(0.ct_eq(&decomposition[i]));
let should_act = (!done) & is_non_zero;
// Update this coefficient and the prior coefficient
let amount_to_sub = <_>::conditional_select(&0, &1, should_act);
decomposition[i] -= amount_to_sub;
let amount_to_add = <_>::conditional_select(&0, &2, should_act);
// i must be at least 1, so i - 1 will be at least 0 (meaning it's safe to index with)
decomposition[i - 1] += amount_to_add;
// Also update the sum of coefficients
sum_of_coefficients += <_>::conditional_select(&0, &1, should_act);
// If we updated the coefficients this loop iter, we're done for this loop iter
done |= should_act;
}
}
debug_assert!(bool::from(decomposition.iter().sum::<u64>().ct_eq(&num_bits)));
ScalarDecomposition { scalar, decomposition }
}
/// The decomposition of the scalar.
pub fn decomposition(&self) -> &[u64] {
&self.decomposition
}
/// A divisor to prove a scalar multiplication.
///
/// The divisor will interpolate $d_i$ instances of $2^i \cdot G$ with $-(s \cdot G)$.
///
/// This function executes in constant time with regards to the scalar.
///
/// This function MAY panic if this scalar is zero.
pub fn scalar_mul_divisor<C: Zeroize + DivisorCurve<Scalar = F>>(
&self,
mut generator: C,
) -> Poly<C::FieldElement> {
// The following for loop is constant time to the sum of `dlog`'s elements
let mut divisor_points =
Vec::with_capacity(usize::try_from(<C::Scalar as PrimeField>::NUM_BITS).unwrap());
divisor_points.push(-generator * self.scalar);
for coefficient in &self.decomposition {
let mut coefficient = *coefficient;
while coefficient != 0 {
coefficient -= 1;
divisor_points.push(generator);
}
generator = generator.double();
}
let res = new_divisor(&divisor_points).unwrap();
divisor_points.zeroize();
res
}
}
#[cfg(any(test, feature = "pasta"))]
mod pasta {
use group::{ff::Field, Curve};

View File

@@ -1,7 +1,7 @@
use core::ops::{Add, Neg, Sub, Mul, Rem};
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConditionallySelectable};
use zeroize::Zeroize;
use zeroize::{Zeroize, ZeroizeOnDrop};
use group::ff::PrimeField;
@@ -30,20 +30,20 @@ impl ConstantTimeGreater for CoefficientIndex {
}
}
/// A structure representing a Polynomial with x**i, y**i, and y**i * x**j terms.
#[derive(Clone, Debug, Zeroize)]
pub struct Poly<F: From<u64> + PrimeField> {
/// c[i] * y ** (i + 1)
/// A structure representing a Polynomial with x^i, y^i, and y^i * x^j terms.
#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
pub struct Poly<F: From<u64> + Zeroize + PrimeField> {
/// c\[i] * y^(i + 1)
pub y_coefficients: Vec<F>,
/// c[i][j] * y ** (i + 1) x ** (j + 1)
/// c\[i]\[j] * y^(i + 1) x^(j + 1)
pub yx_coefficients: Vec<Vec<F>>,
/// c[i] * x ** (i + 1)
/// c\[i] * x^(i + 1)
pub x_coefficients: Vec<F>,
/// Coefficient for x ** 0, y ** 0, and x ** 0 y ** 0 (the coefficient for 1)
/// Coefficient for x^0, y^0, and x^0 y^0 (the coefficient for 1)
pub zero_coefficient: F,
}
impl<F: From<u64> + PrimeField> PartialEq for Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> PartialEq for Poly<F> {
fn eq(&self, b: &Poly<F>) -> bool {
{
let mutual_y_coefficients = self.y_coefficients.len().min(b.y_coefficients.len());
@@ -103,9 +103,9 @@ impl<F: From<u64> + PrimeField> PartialEq for Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Poly<F> {
/// A polynomial for zero.
pub fn zero() -> Self {
pub(crate) fn zero() -> Self {
Poly {
y_coefficients: vec![],
yx_coefficients: vec![],
@@ -115,7 +115,7 @@ impl<F: From<u64> + PrimeField> Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Add<&Self> for Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Add<&Self> for Poly<F> {
type Output = Self;
fn add(mut self, other: &Self) -> Self {
@@ -153,7 +153,7 @@ impl<F: From<u64> + PrimeField> Add<&Self> for Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Neg for Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Neg for Poly<F> {
type Output = Self;
fn neg(mut self) -> Self {
@@ -174,7 +174,7 @@ impl<F: From<u64> + PrimeField> Neg for Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Sub for Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Sub for Poly<F> {
type Output = Self;
fn sub(self, other: Self) -> Self {
@@ -182,7 +182,7 @@ impl<F: From<u64> + PrimeField> Sub for Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Mul<F> for Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Mul<F> for Poly<F> {
type Output = Self;
fn mul(mut self, scalar: F) -> Self {
@@ -202,7 +202,7 @@ impl<F: From<u64> + PrimeField> Mul<F> for Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Poly<F> {
#[must_use]
fn shift_by_x(mut self, power_of_x: usize) -> Self {
if power_of_x == 0 {
@@ -256,14 +256,14 @@ impl<F: From<u64> + PrimeField> Poly<F> {
self.zero_coefficient = F::ZERO;
// Move the x coefficients
self.yx_coefficients[power_of_y - 1] = self.x_coefficients;
std::mem::swap(&mut self.yx_coefficients[power_of_y - 1], &mut self.x_coefficients);
self.x_coefficients = vec![];
self
}
}
impl<F: From<u64> + PrimeField> Mul<&Poly<F>> for Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Mul<&Poly<F>> for Poly<F> {
type Output = Self;
fn mul(self, other: &Self) -> Self {
@@ -290,7 +290,7 @@ impl<F: From<u64> + PrimeField> Mul<&Poly<F>> for Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Poly<F> {
// The leading y coefficient and associated x coefficient.
fn leading_coefficient(&self) -> (usize, usize) {
if self.y_coefficients.len() > self.yx_coefficients.len() {
@@ -355,7 +355,7 @@ impl<F: From<u64> + PrimeField> Poly<F> {
/// Perform multiplication mod `modulus`.
#[must_use]
pub fn mul_mod(self, other: &Self, modulus: &Self) -> Self {
pub(crate) fn mul_mod(self, other: &Self, modulus: &Self) -> Self {
(self * other) % modulus
}
@@ -366,10 +366,13 @@ impl<F: From<u64> + PrimeField> Poly<F> {
///
/// Panics upon division by a polynomial where all coefficients are zero.
#[must_use]
pub fn div_rem(self, denominator: &Self) -> (Self, Self) {
pub(crate) fn div_rem(self, denominator: &Self) -> (Self, Self) {
// These functions have undefined, unsafe behavior if this isn't a valid index
#[allow(clippy::needless_lifetimes)]
fn ct_get<'a, F: From<u64> + PrimeField>(poly: &'a Poly<F>, coeff: CoefficientIndex) -> &'a F {
fn ct_get<'a, F: From<u64> + Zeroize + PrimeField>(
poly: &'a Poly<F>,
coeff: CoefficientIndex,
) -> &'a F {
let y_pow = isize::try_from(coeff.y_pow).unwrap();
let x_pow = isize::try_from(coeff.x_pow).unwrap();
@@ -415,14 +418,14 @@ impl<F: From<u64> + PrimeField> Poly<F> {
}
#[allow(clippy::needless_lifetimes)]
fn ct_get_mut<'a, F: From<u64> + PrimeField>(
fn ct_get_mut<'a, F: From<u64> + Zeroize + PrimeField>(
poly: &'a mut Poly<F>,
coeff: CoefficientIndex,
) -> &'a mut F {
unsafe { (ct_get(poly, coeff) as *const F as *mut F).as_mut().unwrap() }
}
fn structurally_eq<F: From<u64> + PrimeField>(a: &Poly<F>, b: &Poly<F>) -> bool {
fn structurally_eq<F: From<u64> + Zeroize + PrimeField>(a: &Poly<F>, b: &Poly<F>) -> bool {
if a.y_coefficients.len() != b.y_coefficients.len() {
return false;
}
@@ -440,7 +443,7 @@ impl<F: From<u64> + PrimeField> Poly<F> {
true
}
fn conditional_select_poly<F: From<u64> + PrimeField>(
fn conditional_select_poly<F: From<u64> + Zeroize + PrimeField>(
mut a: Poly<F>,
b: &Poly<F>,
choice: Choice,
@@ -655,7 +658,7 @@ impl<F: From<u64> + PrimeField> Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Rem<&Self> for Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Rem<&Self> for Poly<F> {
type Output = Self;
fn rem(self, modulus: &Self) -> Self {
@@ -663,10 +666,10 @@ impl<F: From<u64> + PrimeField> Rem<&Self> for Poly<F> {
}
}
impl<F: From<u64> + PrimeField> Poly<F> {
impl<F: From<u64> + Zeroize + PrimeField> Poly<F> {
/// Evaluate this polynomial with the specified x/y values.
///
/// Panics on polynomials with terms whose powers exceed 2**64.
/// Panics on polynomials with terms whose powers exceed 2^64.
#[must_use]
pub fn eval(&self, x: F, y: F) -> F {
let mut res = self.zero_coefficient;
@@ -693,7 +696,7 @@ impl<F: From<u64> + PrimeField> Poly<F> {
res
}
/// Differentiate a polynomial, reduced by a modulus with a leading y term y**2 x**0, by x and y.
/// Differentiate a polynomial, reduced by a modulus with a leading y term y^2 x^0, by x and y.
///
/// This function has undefined behavior if unreduced.
#[must_use]