From c486b9363175cb1a7afe04544ccd7c377f5c290a Mon Sep 17 00:00:00 2001 From: Andreas Fackler Date: Fri, 1 Feb 2019 16:53:40 +0100 Subject: [PATCH] Make several types Ord, PartialOrd. Also fixes some new Clippy and rustfmt issues and extends documentation. --- src/cmp_pairing.rs | 10 ++++++ src/into_fr.rs | 1 + src/lib.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++-- src/poly.rs | 40 +++++++++++++++++++++- src/serde_impl.rs | 2 +- 5 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 src/cmp_pairing.rs diff --git a/src/cmp_pairing.rs b/src/cmp_pairing.rs new file mode 100644 index 0000000..44aeff8 --- /dev/null +++ b/src/cmp_pairing.rs @@ -0,0 +1,10 @@ +use std::cmp::Ordering; + +use pairing::{CurveAffine, CurveProjective}; + +/// Compares two curve elements and returns their `Ordering`. +pub fn cmp_projective(x: &G, y: &G) -> Ordering { + let xc = x.into_affine().into_compressed(); + let yc = y.into_affine().into_compressed(); + xc.as_ref().cmp(yc.as_ref()) +} diff --git a/src/into_fr.rs b/src/into_fr.rs index 9ff3897..39f8e03 100644 --- a/src/into_fr.rs +++ b/src/into_fr.rs @@ -3,6 +3,7 @@ use pairing::{Field, PrimeField}; /// A conversion into an element of the field `Fr`. pub trait IntoFr: Copy { + /// Converts `self` to a field element. fn into_fr(self) -> Fr; } diff --git a/src/lib.rs b/src/lib.rs index 9f146e4..40e72ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ pub use pairing; +mod cmp_pairing; mod into_fr; mod secret; @@ -20,6 +21,7 @@ pub mod error; pub mod poly; pub mod serde_impl; +use std::cmp::Ordering; use std::fmt; use std::hash::{Hash, Hasher}; use std::ptr::copy_nonoverlapping; @@ -34,11 +36,13 @@ use rand_chacha::ChaChaRng; use serde_derive::{Deserialize, Serialize}; use tiny_keccak::sha3_256; +use crate::cmp_pairing::cmp_projective; use crate::error::{Error, FromBytesError, FromBytesResult, Result}; -use crate::into_fr::IntoFr; use crate::poly::{Commitment, Poly}; use crate::secret::{clear_fr, ContainsSecret, MemRange, FR_SIZE}; +pub use crate::into_fr::IntoFr; + #[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] pub use pairing::bls12_381::{Bls12 as PEngine, Fr, FrRepr, G1Affine, G2Affine, G1, G2}; @@ -78,6 +82,18 @@ impl fmt::Debug for PublicKey { } } +impl PartialOrd for PublicKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for PublicKey { + fn cmp(&self, other: &Self) -> Ordering { + cmp_projective(&self.0, &other.0) + } +} + impl PublicKey { /// Returns `true` if the signature matches the element of `G2`. pub fn verify_g2>(&self, sig: &Signature, hash: H) -> bool { @@ -129,7 +145,7 @@ impl PublicKey { } /// A public key share. -#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash)] +#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct PublicKeyShare(PublicKey); impl fmt::Debug for PublicKeyShare { @@ -175,6 +191,18 @@ impl PublicKeyShare { #[derive(Deserialize, Serialize, Clone, PartialEq, Eq)] pub struct Signature(#[serde(with = "serde_impl::projective")] G2); +impl PartialOrd for Signature { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for Signature { + fn cmp(&self, other: &Self) -> Ordering { + cmp_projective(&self.0, &other.0) + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Signature { Signature(rng.gen04()) @@ -223,7 +251,7 @@ impl Signature { /// A signature share. // Note: Random signature shares can be generated for testing. -#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash)] +#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct SignatureShare(pub Signature); impl Distribution for Standard { @@ -455,6 +483,22 @@ impl Hash for Ciphertext { } } +impl PartialOrd for Ciphertext { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for Ciphertext { + fn cmp(&self, other: &Self) -> Ordering { + let Ciphertext(ref u0, ref v0, ref w0) = self; + let Ciphertext(ref u1, ref v1, ref w1) = other; + cmp_projective(u0, u1) + .then(v0.cmp(v1)) + .then(cmp_projective(w0, w1)) + } +} + impl Ciphertext { /// Returns `true` if this is a valid ciphertext. This check is necessary to prevent /// chosen-ciphertext attacks. @@ -520,6 +564,38 @@ impl PublicKeySet { } /// Combines the shares into a signature that can be verified with the main public key. + /// + /// The validity of the shares is not checked: If one of them is invalid, the resulting + /// signature also is. Only returns an error if there is a duplicate index or too few shares. + /// + /// Validity of signature shares should be checked beforehand, or validity of the result + /// afterwards: + /// + /// ``` + /// # extern crate rand; + /// # + /// # use std::collections::BTreeMap; + /// # use threshold_crypto::SecretKeySet; + /// # + /// let sk_set = SecretKeySet::random(3, &mut rand::thread_rng()); + /// let sk_shares: Vec<_> = (0..6).map(|i| sk_set.secret_key_share(i)).collect(); + /// let pk_set = sk_set.public_keys(); + /// let msg = "Happy birthday! If this is signed, at least four people remembered!"; + /// + /// // Create four signature shares for the message. + /// let sig_shares: BTreeMap<_, _> = (0..4).map(|i| (i, sk_shares[i].sign(msg))).collect(); + /// + /// // Validate the signature shares. + /// for (i, sig_share) in &sig_shares { + /// assert!(pk_set.public_key_share(*i).verify(sig_share, msg)); + /// } + /// + /// // Combine them to produce the main signature. + /// let sig = pk_set.combine_signatures(&sig_shares).expect("not enough shares"); + /// + /// // Validate the main signature. If the shares were valid, this can't fail. + /// assert!(pk_set.public_key().verify(&sig, msg)); + /// ``` pub fn combine_signatures<'a, T, I>(&self, shares: I) -> Result where I: IntoIterator, diff --git a/src/poly.rs b/src/poly.rs index 6ced6c3..a37758f 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -17,6 +17,7 @@ //! polynomials (in two variables) over a field `Fr`, as well as their _commitments_ in `G`. use std::borrow::Borrow; +use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::mem::size_of_val; @@ -27,6 +28,7 @@ use rand::Rng; use rand04_compat::RngExt; use serde_derive::{Deserialize, Serialize}; +use crate::cmp_pairing::cmp_projective; use crate::error::{Error, Result}; use crate::into_fr::IntoFr; use crate::secret::{clear_fr, ContainsSecret, MemRange, Safe}; @@ -436,6 +438,24 @@ pub struct Commitment { pub(super) coeff: Vec, } +impl PartialOrd for Commitment { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for Commitment { + fn cmp(&self, other: &Self) -> Ordering { + self.coeff.len().cmp(&other.coeff.len()).then_with(|| { + self.coeff + .iter() + .zip(&other.coeff) + .find(|(x, y)| x != y) + .map_or(Ordering::Equal, |(x, y)| cmp_projective(x, y)) + }) + } +} + impl Hash for Commitment { fn hash(&self, state: &mut H) { self.coeff.len().hash(state); @@ -657,6 +677,24 @@ impl Hash for BivarCommitment { } } +impl PartialOrd for BivarCommitment { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for BivarCommitment { + fn cmp(&self, other: &Self) -> Ordering { + self.degree.cmp(&other.degree).then_with(|| { + self.coeff + .iter() + .zip(&other.coeff) + .find(|(x, y)| x != y) + .map_or(Ordering::Equal, |(x, y)| cmp_projective(x, y)) + }) + } +} + impl BivarCommitment { /// Returns the polynomial's degree: It is the same in both variables. pub fn degree(&self) -> usize { @@ -760,7 +798,7 @@ mod tests { let x_pow_1 = Poly::monomial(1); let poly = x_pow_3 * 5 + x_pow_1 - 2; - let coeff: Vec<_> = [-2, 1, 0, 5].into_iter().map(IntoFr::into_fr).collect(); + let coeff: Vec<_> = [-2, 1, 0, 5].iter().map(IntoFr::into_fr).collect(); assert_eq!(Poly { coeff }, poly); let samples = vec![(-1, -8), (2, 40), (3, 136), (5, 628)]; for &(x, y) in &samples { diff --git a/src/serde_impl.rs b/src/serde_impl.rs index d440218..7f48dea 100644 --- a/src/serde_impl.rs +++ b/src/serde_impl.rs @@ -94,7 +94,7 @@ impl<'de> Deserialize<'de> for crate::SecretKey { return Err(de::Error::invalid_value( de::Unexpected::Other(&"Number outside of prime field."), &"Valid prime field element.", - )) + )); } };