diff --git a/src/error.rs b/src/error.rs index 9e27319..a3a24cd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,8 @@ pub enum Error { NotEnoughShares, #[fail(display = "Signature shares contain a duplicated index")] DuplicateEntry, + #[fail(display = "The degree is too high for the coefficients to be indexed by usize.")] + DegreeTooHigh, #[fail( display = "Failed to `mlock` {} bytes starting at address: {}", n_bytes, diff --git a/src/lib.rs b/src/lib.rs index 8d48461..9bdc274 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -863,6 +863,14 @@ mod tests { assert_eq!(None, sk_bob.decrypt(&fake_ciphertext)); } + #[test] + fn test_random_extreme_thresholds() { + let mut rng = rand::thread_rng(); + let sks = SecretKeySet::random(0, &mut rng); + assert_eq!(0, sks.threshold()); + assert!(SecretKeySet::try_random(usize::max_value(), &mut rng).is_err()); + } + #[test] fn test_threshold_enc() { let mut rng = rand::thread_rng(); diff --git a/src/poly.rs b/src/poly.rs index 165d284..0760554 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -26,7 +26,7 @@ use pairing::bls12_381::{Fr, G1Affine, G1}; use pairing::{CurveAffine, CurveProjective, Field}; use rand::Rng; -use error::Result; +use error::{Error, Result}; use into_fr::IntoFr; use secret::{clear_fr, ContainsSecret, MemRange, Safe}; @@ -357,8 +357,12 @@ impl Poly { /// /// # Errors /// - /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. + /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit or if + /// the degree is `usize::max_value()`. pub fn try_random(degree: usize, rng: &mut R) -> Result { + if degree == usize::max_value() { + return Err(Error::DegreeTooHigh); + } let coeff: Vec = (0..=degree).map(|_| rng.gen()).collect(); Poly::try_from(coeff) } @@ -744,9 +748,12 @@ impl BivarPoly { /// /// Returns an `Error::MlockFailed` if we have reached the systems's locked memory limit. pub fn try_random(degree: usize, rng: &mut R) -> Result { + let len = coeff_pos(degree, degree) + .and_then(|l| l.checked_add(1)) + .ok_or(Error::DegreeTooHigh)?; let poly = BivarPoly { degree, - coeff: (0..coeff_pos(degree + 1, 0)).map(|_| rng.gen()).collect(), + coeff: (0..len).map(|_| rng.gen()).collect(), }; poly.mlock_secret()?; Ok(poly) @@ -765,7 +772,8 @@ impl BivarPoly { let mut result = Fr::zero(); for (i, x_pow_i) in x_pow.into_iter().enumerate() { for (j, y_pow_j) in y_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; + let index = coeff_pos(i, j).expect("polynomial degree too high"); + let mut summand = self.coeff[index]; summand.mul_assign(&x_pow_i); summand.mul_assign(y_pow_j); result.add_assign(&summand); @@ -797,7 +805,8 @@ impl BivarPoly { // TODO: clear these secrets from the stack. let mut result = Fr::zero(); for (j, x_pow_j) in x_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; + let index = coeff_pos(i, j).expect("polynomial degree too high"); + let mut summand = self.coeff[index]; summand.mul_assign(x_pow_j); result.add_assign(&summand); } @@ -832,13 +841,12 @@ impl BivarPoly { } /// A commitment to a symmetric bivariate polynomial. -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct BivarCommitment { /// The polynomial's degree in each of the two variables. - degree: usize, + pub(crate) degree: usize, /// The commitments to the coefficients. - #[serde(with = "super::serde_impl::projective_vec")] - coeff: Vec, + pub(crate) coeff: Vec, } impl Hash for BivarCommitment { @@ -864,7 +872,8 @@ impl BivarCommitment { let mut result = G1::zero(); for (i, x_pow_i) in x_pow.into_iter().enumerate() { for (j, y_pow_j) in y_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; + let index = coeff_pos(i, j).expect("polynomial degree too high"); + let mut summand = self.coeff[index]; summand.mul_assign(x_pow_i); summand.mul_assign(*y_pow_j); result.add_assign(&summand); @@ -880,7 +889,8 @@ impl BivarCommitment { .map(|i| { let mut result = G1::zero(); for (j, x_pow_j) in x_pow.iter().enumerate() { - let mut summand = self.coeff[coeff_pos(i, j)]; + let index = coeff_pos(i, j).expect("polynomial degree too high"); + let mut summand = self.coeff[index]; summand.mul_assign(*x_pow_j); result.add_assign(&summand); } @@ -907,14 +917,12 @@ fn powers(into_x: T, degree: usize) -> Vec { } /// Returns the position of coefficient `(i, j)` in the vector describing a symmetric bivariate -/// polynomial. -fn coeff_pos(i: usize, j: usize) -> usize { +/// polynomial. If `i` or `j` are too large to represent the position as a `usize`, `None` is +/// returned. +pub(crate) fn coeff_pos(i: usize, j: usize) -> Option { // Since the polynomial is symmetric, we can order such that `j >= i`. - if j >= i { - j * (j + 1) / 2 + i - } else { - i * (i + 1) / 2 + j - } + let (j, i) = if j >= i { (j, i) } else { (i, j) }; + i.checked_add(j.checked_mul(j.checked_add(1)?)? / 2) } #[cfg(test)] @@ -932,7 +940,7 @@ mod tests { let mut i = 0; let mut j = 0; for n in 0..100 { - assert_eq!(n, coeff_pos(i, j)); + assert_eq!(Some(n), coeff_pos(i, j)); if i >= j { j += 1; i = 0; @@ -940,6 +948,8 @@ mod tests { i += 1; } } + let too_large = 1 << (0usize.count_zeros() / 2); + assert_eq!(None, coeff_pos(0, too_large)); } #[test] diff --git a/src/serde_impl.rs b/src/serde_impl.rs index 0078101..ae1f5fd 100644 --- a/src/serde_impl.rs +++ b/src/serde_impl.rs @@ -1,3 +1,45 @@ +use std::borrow::Cow; + +use pairing::bls12_381::G1; +use serde::de::Error as DeserializeError; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use poly::{coeff_pos, BivarCommitment}; + +const ERR_DEG: &str = "commitment degree does not match coefficients"; + +/// A type with the same content as `BivarCommitment`, but that has not been validated yet. +#[derive(Serialize, Deserialize)] +struct WireBivarCommitment<'a> { + /// The polynomial's degree in each of the two variables. + degree: usize, + /// The commitments to the coefficients. + #[serde(with = "projective_vec")] + coeff: Cow<'a, [G1]>, +} + +impl Serialize for BivarCommitment { + fn serialize(&self, s: S) -> Result { + WireBivarCommitment { + degree: self.degree, + coeff: Cow::Borrowed(&self.coeff), + }.serialize(s) + } +} + +impl<'de> Deserialize<'de> for BivarCommitment { + fn deserialize>(d: D) -> Result { + let WireBivarCommitment { degree, coeff } = Deserialize::deserialize(d)?; + if coeff_pos(degree, degree).and_then(|l| l.checked_add(1)) != Some(coeff.len()) { + return Err(D::Error::custom(ERR_DEG)); + } + Ok(BivarCommitment { + degree, + coeff: coeff.into(), + }) + } +} + /// Serialization and deserialization of a group element's compressed representation. pub mod projective { use pairing::{CurveAffine, CurveProjective, EncodedPoint}; @@ -34,6 +76,7 @@ pub mod projective { /// Serialization and deserialization of vectors of projective curve elements. pub mod projective_vec { use std::borrow::Borrow; + use std::iter::FromIterator; use std::marker::PhantomData; use pairing::CurveProjective; @@ -62,19 +105,21 @@ pub mod projective_vec { } } - pub fn serialize(vec: &[C], s: S) -> Result + pub fn serialize(vec: T, s: S) -> Result where S: Serializer, C: CurveProjective, + T: AsRef<[C]>, { - let wrap_vec: Vec> = vec.iter().map(CurveWrap::new).collect(); + let wrap_vec: Vec> = vec.as_ref().iter().map(CurveWrap::new).collect(); wrap_vec.serialize(s) } - pub fn deserialize<'de, D, C>(d: D) -> Result, D::Error> + pub fn deserialize<'de, D, C, T>(d: D) -> Result where D: Deserializer<'de>, C: CurveProjective, + T: FromIterator, { let wrap_vec = >>::deserialize(d)?; Ok(wrap_vec.into_iter().map(|CurveWrap(c, _)| c).collect()) @@ -156,6 +201,8 @@ mod tests { use pairing::Engine; use rand::{self, Rng}; + use poly::BivarPoly; + #[derive(Debug, Serialize, Deserialize)] pub struct Vecs { #[serde(with = "super::projective_vec")] @@ -181,4 +228,16 @@ mod tests { let de_vecs = bincode::deserialize(&ser_vecs).expect("deserialize vecs"); assert_eq!(vecs, de_vecs); } + + #[test] + fn bivar_commitment() { + let mut rng = rand::thread_rng(); + for deg in 1..8 { + let poly = BivarPoly::random(deg, &mut rng); + let comm = poly.commitment(); + let ser_comm = bincode::serialize(&comm).expect("serialize commitment"); + let de_comm = bincode::deserialize(&ser_comm).expect("deserialize commitment"); + assert_eq!(comm, de_comm); + } + } }