Mocktography (#49)

Added mocktography (feature `use-insecure-test-only-mock-crypto) and factored out CI execution script.
This commit is contained in:
Marc Brinkmann 2018-10-11 18:25:15 +02:00 committed by GitHub
parent 90f63e34e9
commit d133bb6d79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1177 additions and 20 deletions

View File

@ -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

View File

@ -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:

16
ci.sh Executable file
View File

@ -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/

View File

@ -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]);

322
src/mock/mod.rs Normal file
View File

@ -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<R: rand::Rng>(rng: &mut R) -> Self {
Ms8Affine(rng.gen())
}
}
impl rand::Rand for Ms8Projective {
#[inline]
fn rand<R: rand::Rng>(rng: &mut R) -> Self {
Ms8Projective(rng.gen())
}
}
impl From<Ms8Projective> for Ms8Affine {
fn from(Ms8Projective(x): Ms8Projective) -> Ms8Affine {
Ms8Affine(x)
}
}
impl From<Ms8Affine> 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 <Self::G1Affine as CurveAffine>::Prepared,
&'a <Self::G2Affine as CurveAffine>::Prepared,
),
>,
{
// Unused?
unimplemented!()
}
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk> {
// Unused?
unimplemented!()
}
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk
where
G1: Into<Self::G1Affine>,
G2: Into<Self::G2Affine>,
{
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<S: Into<<Self::Scalar as PrimeField>::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<S: Into<<Self::Scalar as PrimeField>::Repr>>(&mut self, other: S) {
self.0 *= other.into();
}
fn into_affine(&self) -> Self::Affine {
Ms8Affine(self.0)
}
fn recommended_wnaf_for_scalar(_scalar: <Self::Scalar as PrimeField>::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::<Self>()
}
fn into_affine(&self) -> Result<Self::Affine, GroupDecodingError> {
Ok(*self)
}
fn into_affine_unchecked(&self) -> Result<Self::Affine, GroupDecodingError> {
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));
}
}
}

815
src/mock/ms8.rs Normal file
View File

@ -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<Mersenne8> 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<Mersenne8> 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<u32> for Mersenne8 {
#[inline]
fn from(v: u32) -> Mersenne8 {
Mersenne8::new(v)
}
}
impl From<u64> for Mersenne8 {
#[inline]
fn from(v: u64) -> Mersenne8 {
Mersenne8((v % u64::from(MS8)) as u32)
}
}
impl From<Mersenne8> for u32 {
fn from(v: Mersenne8) -> u32 {
v.0
}
}
impl PartialEq<u32> 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<R: rand::Rng>(rng: &mut R) -> Self {
Mersenne8::from(<u32 as rand::Rand>::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<Self> {
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<Self, PrimeFieldDecodingError> {
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<Self> {
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::<Self>() 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<W: Write>(&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<R: Read>(&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);
}
}

View File

@ -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};