diff --git a/.travis.yml b/.travis.yml index 45a9a52..dbb5723 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,15 +11,4 @@ addons: before_install: - rustup component add --toolchain=${TRAVIS_RUST_VERSION} rustfmt-preview clippy-preview - cargo deadlinks --version || cargo install cargo-deadlinks -env: - global: - - RUST_BACKTRACE=1 - # Enables additional cpu-specific optimizations. - - RUSTFLAGS="-D warnings -C target-cpu=native" -script: - - cargo clippy --tests --examples --benches -- --deny clippy - - cargo fmt -- --check - - cargo test --release - - cargo test --all-features --release - - cargo doc - - cargo deadlinks --dir target/doc/threshold_crypto/ +script: ./ci.sh diff --git a/README.md b/README.md index 5c36011..5a71488 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,10 @@ $ RUSTFLAGS="-C target_cpu=native" cargo bench We use the [`criterion`](https://crates.io/crates/criterion) benchmarking library. +### Mock cryptography + +To speed up automatic tests of crates depending on `threshold_crypto`, the `use-insecure-test-only-mock-crypto` feature is available. **Activating this feature will effectively disable encryption and should only be used during tests!**. Essentially, the underlying elliptic curves will be replaced by small finite fields, yielding a 10-200X speed-up in execution. The resulting ciphers can be trivially broken in a number of ways and should never be used in production. + ## License Licensed under either of: diff --git a/ci.sh b/ci.sh new file mode 100755 index 0000000..e66031f --- /dev/null +++ b/ci.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -xe + +export RUST_BACKTRACE=1 + +# Enables additional cpu-specific optimizations. +export RUSTFLAGS="-D warnings -C target-cpu=native" + +cargo clippy --tests --examples --benches -- --deny clippy +cargo clippy --all-features --tests --examples --benches -- --deny clippy +cargo fmt -- --check +cargo test --release +cargo test --all-features --release +cargo doc +cargo deadlinks --dir target/doc/threshold_crypto/ diff --git a/src/lib.rs b/src/lib.rs index 9e1d8d8..2e39b9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,15 @@ // Clippy warns that it's dangerous to derive `PartialEq` and explicitly implement `Hash`, but the // `pairing::bls12_381` types don't implement `Hash`, so we can't derive it. #![cfg_attr(feature = "cargo-clippy", allow(derive_hash_xor_eq))] +// When using the mocktography, the resulting field elements become wrapped `u32`s, suddenly +// triggering pass-by-reference warnings. They are conditionally disabled for this reason: +#![cfg_attr( + all( + feature = "cargo-clippy", + feature = "use-insecure-test-only-mock-crypto" + ), + allow(trivially_copy_pass_by_ref) +)] #[cfg(test)] extern crate bincode; @@ -46,15 +55,17 @@ use into_fr::IntoFr; use poly::{Commitment, Poly}; use secret::{clear_fr, ContainsSecret, MemRange, FR_SIZE}; -// #[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] +#[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] pub use pairing::bls12_381::{Bls12 as PEngine, Fr, G1Affine, G2Affine, G1, G2}; -// TODO: Add mock cryptography for tests. -// #[cfg(feature = "use-insecure-test-only-mock-crypto")] -// pub use pairing::mock::{ -// Mersenne8 as Fr, Mocktography as PEngine, Ms8Affine as G1Affine, Ms8Affine as G2Affine, -// Ms8Projective as G1, Ms8Projective as G2, -// }; +#[cfg(feature = "use-insecure-test-only-mock-crypto")] +mod mock; + +#[cfg(feature = "use-insecure-test-only-mock-crypto")] +pub use mock::{ + Mersenne8 as Fr, Mocktography as PEngine, Ms8Affine as G1Affine, Ms8Affine as G2Affine, + Ms8Projective as G1, Ms8Projective as G2, +}; /// Wrapper for a byte array, whose `Debug` implementation outputs shortened hexadecimal strings. pub struct HexBytes<'a>(pub &'a [u8]); diff --git a/src/mock/mod.rs b/src/mock/mod.rs new file mode 100644 index 0000000..5e1c30e --- /dev/null +++ b/src/mock/mod.rs @@ -0,0 +1,322 @@ +//! Mock cryptography implementation of a `pairing` engine. +//! +//! Affectionately known as *mocktography*, the `mock` module implements a valid `pairing::Engine` +//! on top of simpler cryptographic primitives; instead of elliptic curves, a simple finite field of +//! Mersenne prime order is used. The resulting engine is trivially breakable (the key space is +//! smaller than 2^31), but should not produce accidental collisions at an unacceptable rate. +//! +//! As a result, all "cryptographic" operations can be carried out much faster. This module is +//! intended to be used during unit-tests of applications that build on top of `threshold_crypto`; +//! enabling this in production code of any application will immediately break its cryptographic +//! security. + +pub mod ms8; + +use std::{fmt, mem, slice}; + +use pairing::{EncodedPoint, Field, GroupDecodingError, PrimeField}; +use rand; + +use super::{CurveAffine, CurveProjective, Engine}; + +pub use self::ms8::Mersenne8; + +/// A `pairing` Engine based on `Mersenne8` prime fields. +#[derive(Clone, Debug)] +pub struct Mocktography; + +/// Affine type for `Mersenne8`. +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct Ms8Affine(Mersenne8); + +/// Projective type for `Mersenne8`. +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct Ms8Projective(Mersenne8); + +impl fmt::Display for Ms8Affine { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl fmt::Display for Ms8Projective { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl rand::Rand for Ms8Affine { + #[inline] + fn rand(rng: &mut R) -> Self { + Ms8Affine(rng.gen()) + } +} + +impl rand::Rand for Ms8Projective { + #[inline] + fn rand(rng: &mut R) -> Self { + Ms8Projective(rng.gen()) + } +} + +impl From for Ms8Affine { + fn from(Ms8Projective(x): Ms8Projective) -> Ms8Affine { + Ms8Affine(x) + } +} + +impl From for Ms8Projective { + fn from(Ms8Affine(x): Ms8Affine) -> Ms8Projective { + Ms8Projective(x) + } +} + +impl Engine for Mocktography { + type G1 = Ms8Projective; + type G1Affine = Ms8Affine; + type G2 = Ms8Projective; + type G2Affine = Ms8Affine; + type Fq = Mersenne8; + type Fqe = Mersenne8; + type Fqk = Mersenne8; + + // In newer versions of pairing, this must be moved to `ScalarEngine`: + type Fr = Mersenne8; + + fn miller_loop<'a, I>(_i: I) -> Self::Fqk + where + I: IntoIterator< + Item = &'a ( + &'a ::Prepared, + &'a ::Prepared, + ), + >, + { + // Unused? + unimplemented!() + } + + fn final_exponentiation(_: &Self::Fqk) -> Option { + // Unused? + unimplemented!() + } + + fn pairing(p: G1, q: G2) -> Self::Fqk + where + G1: Into, + G2: Into, + { + p.into().0 * q.into().0 + } +} + +impl AsRef<[u64]> for Mersenne8 { + #[inline] + fn as_ref(&self) -> &[u64] { + panic!("Not supported: AsRef<[u64]>") + } +} + +impl AsRef<[u8]> for Mersenne8 { + #[inline] + fn as_ref(&self) -> &[u8] { + unsafe { slice::from_raw_parts(&self.0 as *const u32 as *const u8, 4) } + } +} + +impl AsMut<[u64]> for Mersenne8 { + #[inline] + fn as_mut(&mut self) -> &mut [u64] { + panic!("Not supported: AsMut<[u64]>") + } +} + +impl AsMut<[u8]> for Mersenne8 { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(&mut self.0 as *mut u32 as *mut u8, 4) } + } +} + +impl AsRef<[u8]> for Ms8Affine { + #[inline] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsMut<[u8]> for Ms8Affine { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut() + } +} + +impl CurveAffine for Ms8Affine { + type Engine = Mocktography; + type Scalar = Mersenne8; + type Base = Mersenne8; + type Projective = Ms8Projective; + type Prepared = Ms8Affine; + type Uncompressed = Ms8Affine; + type Compressed = Ms8Affine; + type Pair = Ms8Affine; + type PairingResult = Mersenne8; + + fn zero() -> Self { + Ms8Affine(Mersenne8::zero()) + } + + fn one() -> Self { + Ms8Affine(Mersenne8::one()) + } + + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + fn negate(&mut self) { + self.0.negate(); + } + + fn mul::Repr>>(&self, other: S) -> Self::Projective { + // FIXME: Is this correct? + let s = other.into(); + + Ms8Projective(self.0 * s) + } + + fn prepare(&self) -> Self::Prepared { + *self + } + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + // This is the actual implementation of e: G_1 x G_2 -> G_T. + // We have chosen e(P, Q) = PQ. + self.0 * other.0 + } + + fn into_projective(&self) -> Self::Projective { + Ms8Projective(self.0) + } +} + +impl CurveProjective for Ms8Projective { + type Engine = Mocktography; + type Scalar = Mersenne8; + type Base = Mersenne8; + type Affine = Ms8Affine; + + fn zero() -> Self { + Ms8Projective(Mersenne8::zero()) + } + + fn one() -> Self { + Ms8Projective(Mersenne8::one()) + } + + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + fn batch_normalization(_v: &mut [Self]) { + // We just assume all values as already normalized. See `is_normalized()`. + } + + fn is_normalized(&self) -> bool { + true + } + + fn double(&mut self) { + self.0.double() + } + + fn add_assign(&mut self, other: &Self) { + self.0.add_assign(&other.0); + } + + fn add_assign_mixed(&mut self, other: &Self::Affine) { + self.0.add_assign(&other.0); + } + + fn negate(&mut self) { + self.0.negate(); + } + + fn mul_assign::Repr>>(&mut self, other: S) { + self.0 *= other.into(); + } + + fn into_affine(&self) -> Self::Affine { + Ms8Affine(self.0) + } + + fn recommended_wnaf_for_scalar(_scalar: ::Repr) -> usize { + 2 + } + + fn recommended_wnaf_for_num_scalars(_num_scalars: usize) -> usize { + 2 + } +} + +impl EncodedPoint for Ms8Affine { + type Affine = Ms8Affine; + + fn empty() -> Self { + // FIXME: Ensure we are not violating any assumptions here. + Self::default() + } + + fn size() -> usize { + mem::size_of::() + } + + fn into_affine(&self) -> Result { + Ok(*self) + } + + fn into_affine_unchecked(&self) -> Result { + Ok(*self) + } + + fn from_affine(affine: Self::Affine) -> Self { + affine + } +} + +#[cfg(test)] +mod test { + // There are copy & pasted results of calculations from external programs in these tests. + #![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] + + use super::{Mersenne8, Mocktography, Ms8Affine}; + use Engine; + + #[test] + fn example_pairings() { + let pqs: Vec<(u32, u32, u32)> = vec![ + (0, 0, 0), + (123, 0, 0), + (1, 1, 1), + (123, 1, 123), + (4, 5, 20), + (123456789, 987654321, 2137109934), + (456789123, 456789123, 1405297315), + ]; + + for (p, q, res) in pqs { + let p = Ms8Affine(p.into()); + let q = Ms8Affine(q.into()); + println!("P, Q: {}, {}", p, q); + println!("Checking e({}, {}) = {}", p, q, res); + assert_eq!(Mocktography::pairing(p, q), Mersenne8::new(res)); + + // Our pairing is bilinear. + println!("Checking e({}, {}) = {}", q, p, res); + assert_eq!(Mocktography::pairing(q, p), Mersenne8::new(res)); + } + } +} diff --git a/src/mock/ms8.rs b/src/mock/ms8.rs new file mode 100644 index 0000000..38f37ab --- /dev/null +++ b/src/mock/ms8.rs @@ -0,0 +1,815 @@ +//! Eigth Mersenne prime field +//! +//! The eighth [Mersenne Prime](https://en.wikipedia.org/wiki/Mersenne_prime) (`MS8 := 2^31-1) can +//! be used to construct a finite field supporting addition and multiplication. This module provides +//! a wrapper type around `u32` to implement this functionality. +//! +//! The resulting type also implements the `Field`, `PrimeField` and `SqrtField` traits. For +//! convenience, `PrimeFieldRepr` is also implemented. + +use std::io::{self, Read, Write}; +use std::{fmt, mem, ops}; + +use byteorder::{BigEndian, ByteOrder}; +use pairing::{ + Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField, +}; +use rand; + +/// Modular exponentiation +/// +/// Warning: Only tested using bases and exponents `< MS8`. +#[inline] +fn modular_pow(base: u32, mut exp: u32, modulus: u32) -> u32 { + // Source: https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method + if modulus == 1 { + return 0; + } + + // Need to use 64 bits to ensure the assert from Schneier's algorithm: + // (modulus - 1) * (modulus - 1) does not overflow base + let mut result: u64 = 1; + let md: u64 = u64::from(modulus); + let mut base = u64::from(base) % md; + + while exp > 0 { + if exp % 2 == 1 { + result = (result * base) % md; + } + exp >>= 1; + base = (base * base) % md; + } + + result as u32 +} + +/// Eigth Mersenne prime, aka `i32::MAX`. +pub const MS8: u32 = 0x7fff_ffff; + +/// Eighth Mersenne prime field element +/// +/// Finite field of order `2^31-1`. +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct Mersenne8(pub u32); + +// We opt to implement the same variants the standard library implements as well: +// +// FooAssign: `lhs .= rhs, lhs .= &rhs`. +// Foo: `lhs . rhs, &lhs . rhs, &lhs . &rhs, lhs . &rhs` +macro_rules! generate_op_impls { + ($t:ty, $op_name:ident, $op_name_assign:ident, $op_method:ident, $op_method_assign:ident) => { + // Supplied by caller: `lhs .= rhs` + + // `lhs . rhs` + impl ops::$op_name for $t { + type Output = Self; + + #[inline] + fn $op_method(mut self, rhs: $t) -> Self { + self.$op_method_assign(&rhs); + self + } + } + + // lhs .= &rhs + impl<'a> ops::$op_name_assign<&'a $t> for $t { + #[inline] + fn $op_method_assign(&mut self, rhs: &'a $t) { + ops::$op_name_assign::$op_method_assign(self, *rhs) + } + } + + // `&lhs . rhs` + impl<'a> ops::$op_name<$t> for &'a $t { + type Output = $t; + + #[inline] + fn $op_method(self, other: $t) -> Self::Output { + let mut tmp = *self; + tmp.$op_method_assign(&other); + tmp + } + } + + // `&lhs . &rhs` + impl<'a, 'b> ops::$op_name<&'b $t> for &'a $t { + type Output = $t; + + #[inline] + fn $op_method(self, other: &'b $t) -> Self::Output { + let mut tmp = *self; + tmp.$op_method_assign(other); + tmp + } + } + + // `lhs . &rhs` + impl<'a> ops::$op_name<&'a $t> for $t { + type Output = $t; + + #[inline] + fn $op_method(mut self, other: &'a $t) -> Self::Output { + self.$op_method_assign(other); + self + } + } + }; +} + +impl ops::SubAssign for Mersenne8 { + #[inline] + fn sub_assign(&mut self, rhs: Mersenne8) { + // Since `self.0` is < 2^31-1, `self.0 + 2^31-1` is still smaller than `u32::MAX`. + self.0 = (self.0 + MS8 - rhs.0) % MS8; + } +} +generate_op_impls!(Mersenne8, Sub, SubAssign, sub, sub_assign); + +impl ops::AddAssign for Mersenne8 { + #[inline] + fn add_assign(&mut self, rhs: Mersenne8) { + self.0 = (self.0 + rhs.0) % MS8; + } +} +generate_op_impls!(Mersenne8, Add, AddAssign, add, add_assign); + +impl ops::MulAssign for Mersenne8 { + #[inline] + fn mul_assign(&mut self, rhs: Mersenne8) { + // Usually, Schrage's method would be a good way to implement the multiplication; + // however, since we will mostly be running the code on 64-bit machines and + // `(2^31-1)^2 < 2^64`, we can cheat and do this fairly fast in 64 bits. + self.0 = (u64::from(self.0) * u64::from(rhs.0) % u64::from(MS8)) as u32; + } +} + +generate_op_impls!(Mersenne8, Mul, MulAssign, mul, mul_assign); + +impl Mersenne8 { + #[inline] + pub fn new(v: u32) -> Mersenne8 { + Mersenne8(v % MS8) + } + + #[inline] + pub fn pow(self, exp: u32) -> Mersenne8 { + Mersenne8(modular_pow(self.0, exp, MS8)) + } +} + +impl From for Mersenne8 { + #[inline] + fn from(v: u32) -> Mersenne8 { + Mersenne8::new(v) + } +} + +impl From for Mersenne8 { + #[inline] + fn from(v: u64) -> Mersenne8 { + Mersenne8((v % u64::from(MS8)) as u32) + } +} + +impl From for u32 { + fn from(v: Mersenne8) -> u32 { + v.0 + } +} + +impl PartialEq for Mersenne8 { + fn eq(&self, other: &u32) -> bool { + self.0 == *other + } +} + +impl fmt::Display for Mersenne8 { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl rand::Rand for Mersenne8 { + #[inline] + fn rand(rng: &mut R) -> Self { + Mersenne8::from(::rand(rng)) + } +} + +impl Field for Mersenne8 { + #[inline] + fn zero() -> Self { + Mersenne8(0) + } + + #[inline] + fn one() -> Self { + Mersenne8(1) + } + + #[inline] + fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[inline] + fn square(&mut self) { + self.0 = (*self * *self).0; + } + + #[inline] + fn double(&mut self) { + // MS8 fits at least twice into a single u32. + self.0 = (self.0 * 2) % MS8; + } + + #[inline] + fn negate(&mut self) { + self.0 = (Self::zero() - *self).0; + } + + #[inline] + fn add_assign(&mut self, other: &Self) { + *self += other; + } + + #[inline] + fn sub_assign(&mut self, other: &Self) { + *self -= other; + } + + #[inline] + fn mul_assign(&mut self, other: &Self) { + *self *= other; + } + + #[inline] + fn inverse(&self) -> Option { + let (d, _s, t) = ext_euclid(MS8, self.0); + + // MS8 is prime, so the gcd should always be 1, unless the number itself is 0. + if d != 1 { + debug_assert_eq!(d, MS8); + return None; + } + + Some(Mersenne8::new( + ((t + i64::from(MS8)) % i64::from(MS8)) as u32, + )) + } + + #[inline] + fn frobenius_map(&mut self, _power: usize) { + // Does nothing, the frobenius endomorphism is the identity function in every finite field + // of prime order. + } +} + +impl PrimeField for Mersenne8 { + type Repr = Self; + + const NUM_BITS: u32 = 32; + const CAPACITY: u32 = 30; + + // Not actually used. + const S: u32 = 0; + + #[inline] + fn from_repr(v: Self::Repr) -> Result { + Ok(v) + } + + #[inline] + fn into_repr(&self) -> Self::Repr { + *self + } + + #[inline] + fn char() -> Self::Repr { + // Awkward. We cannot return the characteristic itself, since it's not an element of field, + // but equal to its order. Instead, we panic. + // + // Note: The return type of this function should probably be `usize`. + panic!("Cannot return characteristic of Mersenne8 as an element of Mersenne8."); + } + + #[inline] + fn multiplicative_generator() -> Self { + // Any element of a finite field of prime order is a generator, but 3 is the smallest one + // that is a quadratic non-residue. + Mersenne8::new(3) + } + + #[inline] + fn root_of_unity() -> Self { + // Still unclear what's supposed to be implemented here, missing at least an `n`?. + unimplemented!() + } +} + +impl SqrtField for Mersenne8 { + fn legendre(&self) -> LegendreSymbol { + // Uses Euler's criterion: `(a/p) === a^((p-1)/2). + let exp = (MS8 - 1) / 2; + + match (*self).pow(exp).0 { + 1 => LegendreSymbol::QuadraticResidue, + n if n == MS8 - 1 => LegendreSymbol::QuadraticNonResidue, + 0 => LegendreSymbol::Zero, + _ => panic!("Euler's criteria did not return correct Legendre symbol"), + } + } + + fn sqrt(&self) -> Option { + unimplemented!() // FIXME, could use Tonelli-Shanks algorithm + } +} + +impl PrimeFieldRepr for Mersenne8 { + #[inline] + fn sub_noborrow(&mut self, other: &Self) { + *self -= other; + } + + #[inline] + fn add_nocarry(&mut self, other: &Self) { + *self += other; + } + + #[inline] + fn num_bits(&self) -> u32 { + 8 * mem::size_of::() as u32 + } + + #[inline] + fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[inline] + fn is_odd(&self) -> bool { + self.0 % 2 == 1 + } + + // #[inline] + fn is_even(&self) -> bool { + !self.is_odd() + } + + #[inline] + fn div2(&mut self) { + self.shr(1); + } + + #[inline] + fn shr(&mut self, amt: u32) { + self.0 >>= amt; + } + + #[inline] + fn mul2(&mut self) { + *self *= Mersenne8::new(2); + } + + #[inline] + fn shl(&mut self, amt: u32) { + // FIXME: is this correct? + self.0 <<= amt; + self.0 %= MS8; + } + + fn write_be(&self, mut writer: W) -> io::Result<()> { + let mut buf = [0u8; 4]; + BigEndian::write_u32(&mut buf, self.0); + writer.write_all(&buf)?; + Ok(()) + } + + fn read_be(&mut self, mut reader: R) -> io::Result<()> { + let mut buf = [0u8; 4]; + reader.read_exact(&mut buf)?; + self.0 = BigEndian::read_u32(&buf); + Ok(()) + } +} + +/// Extended Euclidean algorithm +/// +/// Returns the `gcd(a,b)`, as well as the +/// [Bézout coefficients](https://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity) `s` and `t` (with +/// `sa + tb = gcd(a,b)`). +/// +/// The function will neither panic nor overflow; however passing in `0` as either `a` or `b` +/// will not give useful results for `s` and `t`. +/// +/// Note: A non-recurring implementation will probably be faster, but the recursion is simpler +/// to write. +#[inline] +fn ext_euclid(a: u32, b: u32) -> (u32, i64, i64) { + // Bézout coefficients (`s` and `t`) are bound by `|s| <= |b/d|` and `|t| <= |a/d|` + // (`d` being `gdc(a,b)`). (See https://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity) + // + // FIXME: Find out of there are any larger than i32::MAX. For now, we are using an `i64` to + // avoid problems, at the expense of some runtime performance on 32 bit systems. + + if b == 0 { + return (a, 1, 0); + } + + let res = ext_euclid(b, a % b); + let s = res.2; + let t = res.1 - i64::from(a / b) * res.2; + (res.0, s, t) +} + +#[cfg(test)] +mod tests { + // There are copy & pasted results of calculations from external programs in these tests. + #![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] + #![cfg_attr(feature = "cargo-clippy", allow(op_ref))] + // We test a few mathematical identities, including `c - c = 0`. Clippy complains about these + // otherwise unusual expressions, so the lint is disabled. + #![cfg_attr(feature = "cargo-clippy", allow(eq_op))] + + use super::{ext_euclid, modular_pow, Mersenne8}; + use pairing::Field; + + #[test] + fn ext_euclid_simple() { + assert_eq!(ext_euclid(56, 15), (1, -4, 15)); + assert_eq!(ext_euclid(128, 44), (4, -1, 3)); + assert_eq!(ext_euclid(44, 128), (4, 3, -1)); + assert_eq!(ext_euclid(7, 0), (7, 1, 0)); + assert_eq!(ext_euclid(0, 7), (7, 0, 1)); + } + + #[test] + fn modular_pow_simple() { + assert_eq!(modular_pow(2, 8, 1024), 256); + + let mods: &[u64] = &[2, 7, 256, 32000, 4294967295]; + + for exp in 0..20u32 { + for base in 2..8u64 { + for &m in mods { + assert_eq!( + modular_pow(base as u32, exp, m as u32), + (base.pow(exp) % m) as u32 + ) + } + } + } + } + + #[test] + fn construction() { + let a: Mersenne8 = 0u32.into(); + let b: Mersenne8 = 1u32.into(); + let c: Mersenne8 = 2147483649u32.into(); + let d: Mersenne8 = 1099511627776u64.into(); + + assert_eq!(a, 0); + assert_eq!(b, 1); + assert_eq!(c, 2); + assert_eq!(d, 512); + + assert_eq!(a, Mersenne8::new(0)); + assert_eq!(b, Mersenne8::new(1)); + assert_eq!(c, Mersenne8::new(2)); + assert_eq!(d, Mersenne8::new(512)); + } + + #[test] + fn addition() { + let a = Mersenne8::new(0); + let b = Mersenne8::new(1); + let c = Mersenne8::new(127); + let d = Mersenne8::new(1073741824); + let e = Mersenne8::new(2147483646); + + // lhs . rhs + assert_eq!(a + b, Mersenne8::new(1)); + assert_eq!(c + c, Mersenne8::new(254)); + assert_eq!(d + c, Mersenne8::new(1073741951)); + assert_eq!(d + e, Mersenne8::new(3221225470)); + assert_eq!(d + e, 1073741823); + assert_eq!(e + e, Mersenne8::new(4294967292)); + + // &lhs . rhs + assert_eq!(a + &b, Mersenne8::new(1)); + assert_eq!(c + &c, Mersenne8::new(254)); + assert_eq!(d + &c, Mersenne8::new(1073741951)); + assert_eq!(d + &e, Mersenne8::new(3221225470)); + assert_eq!(e + &e, Mersenne8::new(4294967292)); + + // lhs . &rhs + assert_eq!(&a + b, Mersenne8::new(1)); + assert_eq!(&c + c, Mersenne8::new(254)); + assert_eq!(&d + c, Mersenne8::new(1073741951)); + assert_eq!(&d + e, Mersenne8::new(3221225470)); + assert_eq!(&e + e, Mersenne8::new(4294967292)); + + // &lhs . &rhs + assert_eq!(&a + &b, Mersenne8::new(1)); + assert_eq!(&c + &c, Mersenne8::new(254)); + assert_eq!(&d + &c, Mersenne8::new(1073741951)); + assert_eq!(&d + &e, Mersenne8::new(3221225470)); + assert_eq!(&e + &e, Mersenne8::new(4294967292)); + + // lhs .= rhs + let mut x = Mersenne8::new(8); + assert_eq!(x, Mersenne8::new(8)); + x += a; + assert_eq!(x, Mersenne8::new(8)); + x += b; + assert_eq!(x, Mersenne8::new(9)); + x += c; + assert_eq!(x, Mersenne8::new(136)); + x += d; + assert_eq!(x, Mersenne8::new(1073741960)); + x += e; + assert_eq!(x, Mersenne8::new(1073741959)); + + // lhs .= &rhs + let mut y = Mersenne8::new(8); + assert_eq!(y, Mersenne8::new(8)); + y += &a; + assert_eq!(y, Mersenne8::new(8)); + y += &b; + assert_eq!(y, Mersenne8::new(9)); + y += &c; + assert_eq!(y, Mersenne8::new(136)); + y += &d; + assert_eq!(y, Mersenne8::new(1073741960)); + y += &e; + assert_eq!(y, Mersenne8::new(1073741959)); + } + + #[test] + fn subtraction() { + let a = Mersenne8::new(0); + let b = Mersenne8::new(1); + let c = Mersenne8::new(127); + let d = Mersenne8::new(1073741824); + let e = Mersenne8::new(2147483646); + + // lhs . rhs + assert_eq!(a - b, Mersenne8::new(2147483646)); + assert_eq!(c - c, Mersenne8::new(0)); + assert_eq!(d - c, Mersenne8::new(1073741697)); + assert_eq!(d - e, Mersenne8::new(1073741825)); + + // &lhs . rhs + assert_eq!(&a - b, Mersenne8::new(2147483646)); + assert_eq!(&c - c, Mersenne8::new(0)); + assert_eq!(&d - c, Mersenne8::new(1073741697)); + assert_eq!(&d - e, Mersenne8::new(1073741825)); + + // lhs . &rhs + assert_eq!(a - &b, Mersenne8::new(2147483646)); + assert_eq!(c - &c, Mersenne8::new(0)); + assert_eq!(d - &c, Mersenne8::new(1073741697)); + assert_eq!(d - &e, Mersenne8::new(1073741825)); + + // &lhs . &rhs + assert_eq!(&a - &b, Mersenne8::new(2147483646)); + assert_eq!(&c - &c, Mersenne8::new(0)); + assert_eq!(&d - &c, Mersenne8::new(1073741697)); + assert_eq!(&d - &e, Mersenne8::new(1073741825)); + + // lhs .= rhs + let mut x = Mersenne8::new(17); + assert_eq!(x, Mersenne8::new(17)); + x -= a; + assert_eq!(x, Mersenne8::new(17)); + x -= b; + assert_eq!(x, Mersenne8::new(16)); + x -= c; + assert_eq!(x, Mersenne8::new(2147483536)); + x -= d; + assert_eq!(x, Mersenne8::new(1073741712)); + x -= e; + assert_eq!(x, Mersenne8::new(1073741713)); + + // lhs .= &rhs + let mut y = Mersenne8::new(17); + assert_eq!(y, Mersenne8::new(17)); + y -= &a; + assert_eq!(y, Mersenne8::new(17)); + y -= &b; + assert_eq!(y, Mersenne8::new(16)); + y -= &c; + assert_eq!(y, Mersenne8::new(2147483536)); + y -= &d; + assert_eq!(y, Mersenne8::new(1073741712)); + y -= &e; + assert_eq!(y, Mersenne8::new(1073741713)); + } + + #[test] + fn multiplication() { + let a = Mersenne8::new(0); + let b = Mersenne8::new(1); + let c = Mersenne8::new(127); + let d = Mersenne8::new(1073741824); + let e = Mersenne8::new(384792341); + + // lhs . rhs + assert_eq!(a * a, Mersenne8::new(0)); + assert_eq!(a * b, Mersenne8::new(0)); + assert_eq!(b * b, Mersenne8::new(1)); + assert_eq!(c * c, Mersenne8::new(16129)); + assert_eq!(d * c, Mersenne8::new(1073741887)); + assert_eq!(d * e, Mersenne8::new(1266137994)); + + // &lhs . rhs + assert_eq!(&a * a, Mersenne8::new(0)); + assert_eq!(&a * b, Mersenne8::new(0)); + assert_eq!(&b * b, Mersenne8::new(1)); + assert_eq!(&c * c, Mersenne8::new(16129)); + assert_eq!(&d * c, Mersenne8::new(1073741887)); + assert_eq!(&d * e, Mersenne8::new(1266137994)); + + // lhs . &rhs + assert_eq!(a * &a, Mersenne8::new(0)); + assert_eq!(a * &b, Mersenne8::new(0)); + assert_eq!(b * &b, Mersenne8::new(1)); + assert_eq!(c * &c, Mersenne8::new(16129)); + assert_eq!(d * &c, Mersenne8::new(1073741887)); + assert_eq!(d * &e, Mersenne8::new(1266137994)); + + // &lhs . &rhs + assert_eq!(&a * &a, Mersenne8::new(0)); + assert_eq!(&a * &b, Mersenne8::new(0)); + assert_eq!(&b * &b, Mersenne8::new(1)); + assert_eq!(&c * &c, Mersenne8::new(16129)); + assert_eq!(&d * &c, Mersenne8::new(1073741887)); + assert_eq!(&d * &e, Mersenne8::new(1266137994)); + + // lhs .= rhs + let mut x = Mersenne8::new(17); + x *= b; + assert_eq!(x, Mersenne8::new(17)); + x *= c; + assert_eq!(x, Mersenne8::new(2159)); + x *= d; + assert_eq!(x, Mersenne8::new(1073742903)); + x *= e; + assert_eq!(x, Mersenne8::new(1992730062)); + + // // lhs .= &rhs + let mut y = Mersenne8::new(17); + y *= &b; + assert_eq!(y, Mersenne8::new(17)); + y *= &c; + assert_eq!(y, Mersenne8::new(2159)); + y *= &d; + assert_eq!(y, Mersenne8::new(1073742903)); + y *= &e; + assert_eq!(y, Mersenne8::new(1992730062)); + } + + #[test] + fn square() { + let a = Mersenne8::new(7); + let b = Mersenne8::new(1073729479); + + let mut a_sq = a; + a_sq.square(); + assert_eq!(a_sq, a * a); + assert_eq!(a_sq, 49); + + let mut b_sq = b; + b_sq.square(); + assert_eq!(b_sq, b * b); + assert_eq!(b_sq, 689257592); + } + + #[test] + fn double() { + let mut a = Mersenne8::new(0); + let mut b = Mersenne8::new(1); + let mut c = Mersenne8::new(9); + let mut d = Mersenne8::new(2147483646); + + a.double(); + b.double(); + c.double(); + d.double(); + + assert_eq!(a, 0); + assert_eq!(b, 2); + assert_eq!(c, 18); + assert_eq!(d, 2147483645); + } + + #[test] + fn negate() { + let mut a = Mersenne8::new(0); + let mut b = Mersenne8::new(1); + let mut c = Mersenne8::new(9); + let mut d = Mersenne8::new(17); + let mut e = Mersenne8::new(16441); + let mut f = Mersenne8::new(1073754169); + + a.negate(); + b.negate(); + c.negate(); + d.negate(); + e.negate(); + f.negate(); + + assert_eq!(a, 0); + assert_eq!(b, 2147483646); + assert_eq!(c, 2147483638); + assert_eq!(d, 2147483630); + assert_eq!(e, 2147467206); + assert_eq!(f, 1073729478); + } + + #[test] + fn inverse() { + let a = Mersenne8::new(0); + let b = Mersenne8::new(1); + let c = Mersenne8::new(2); + let d = Mersenne8::new(1234); + let e = Mersenne8::new(1073741823); + let f = Mersenne8::new(46341); + let g = Mersenne8::new(2147483646); + let h = Mersenne8::new(923042); + + assert_eq!(a.inverse(), None); + assert_eq!(b.inverse(), Some(Mersenne8::new(1))); + assert_eq!(c.inverse(), Some(Mersenne8::new(1073741824))); + assert_eq!(d.inverse(), Some(Mersenne8::new(158363867))); + assert_eq!(e.inverse(), Some(Mersenne8::new(2147483645))); + assert_eq!(f.inverse(), Some(Mersenne8::new(2147020238))); + assert_eq!(g.inverse(), Some(Mersenne8::new(2147483646))); + assert_eq!(h.inverse(), Some(Mersenne8::new(1271194309))); + + assert_eq!(b.inverse().unwrap().inverse().unwrap(), b); + assert_eq!(c.inverse().unwrap().inverse().unwrap(), c); + assert_eq!(d.inverse().unwrap().inverse().unwrap(), d); + assert_eq!(e.inverse().unwrap().inverse().unwrap(), e); + assert_eq!(f.inverse().unwrap().inverse().unwrap(), f); + assert_eq!(g.inverse().unwrap().inverse().unwrap(), g); + assert_eq!(g.inverse().unwrap().inverse().unwrap(), g); + assert_eq!(h.inverse().unwrap().inverse().unwrap(), h); + } + + #[test] + fn frobenius_map() { + let a = Mersenne8::new(0); + let b = Mersenne8::new(1); + let c = Mersenne8::new(2); + let d = Mersenne8::new(1234); + let e = Mersenne8::new(1073741823); + let f = Mersenne8::new(46341); + let g = Mersenne8::new(2147483646); + let h = Mersenne8::new(923042); + + let mut a_fm = a; + let mut b_fm = b; + let mut c_fm = c; + let mut d_fm = d; + let mut e_fm = e; + let mut f_fm = f; + let mut g_fm = g; + let mut h_fm = h; + + a_fm.frobenius_map(1); + a_fm.frobenius_map(2); + a_fm.frobenius_map(3); + b_fm.frobenius_map(1); + b_fm.frobenius_map(2); + b_fm.frobenius_map(3); + c_fm.frobenius_map(1); + c_fm.frobenius_map(2); + c_fm.frobenius_map(3); + d_fm.frobenius_map(1); + d_fm.frobenius_map(2); + d_fm.frobenius_map(3); + e_fm.frobenius_map(1); + e_fm.frobenius_map(2); + e_fm.frobenius_map(3); + f_fm.frobenius_map(1); + f_fm.frobenius_map(2); + f_fm.frobenius_map(3); + g_fm.frobenius_map(1); + g_fm.frobenius_map(2); + g_fm.frobenius_map(3); + h_fm.frobenius_map(1); + h_fm.frobenius_map(2); + h_fm.frobenius_map(3); + + assert_eq!(a_fm, a); + assert_eq!(b_fm, b); + assert_eq!(c_fm, c); + assert_eq!(d_fm, d); + assert_eq!(e_fm, e); + assert_eq!(f_fm, f); + assert_eq!(g_fm, g); + assert_eq!(h_fm, h); + } +} diff --git a/src/serde_impl.rs b/src/serde_impl.rs index caaa869..6ea1bba 100644 --- a/src/serde_impl.rs +++ b/src/serde_impl.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use pairing::bls12_381::G1; +use super::G1; use serde::de::Error as DeserializeError; use serde::{Deserialize, Deserializer, Serialize, Serializer};