From 5239eb2e1192f352c74927909b8ae8c22ef0990a Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Mon, 19 Jul 2021 20:42:58 +0300 Subject: [PATCH] Implement BigInt traits for the gmp backend using rug --- Cargo.toml | 9 +- src/arithmetic/big_gmp.rs | 432 -------------------------------------- src/arithmetic/errors.rs | 11 +- src/arithmetic/gmp.rs | 165 +++++++++++++++ 4 files changed, 178 insertions(+), 439 deletions(-) delete mode 100644 src/arithmetic/big_gmp.rs create mode 100644 src/arithmetic/gmp.rs diff --git a/Cargo.toml b/Cargo.toml index 196ee0dd..d4e20cac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ sha2 = "0.8.0" sha3 = "0.8.2" zeroize = "1" -rust-gmp-kzen = { version = "0.5", features = ["serde_support"], optional = true } num-bigint = { version = "0.4", features = ["serde"], optional = true } [dependencies.secp256k1] @@ -46,6 +45,12 @@ features = ["serde", "rand-std"] version = "0.5" features = ["ecdsa"] +[dependencies.rug] +version = "1" +default-features = false +features = ["integer", "serde"] +optional = true + [dev-dependencies] bincode = "1.1" serde_json = "1.0" @@ -54,4 +59,4 @@ proptest = "0.10" proptest-derive = "0.2" [features] -default = ["rust-gmp-kzen"] +default = ["rug"] diff --git a/src/arithmetic/big_gmp.rs b/src/arithmetic/big_gmp.rs deleted file mode 100644 index 4526174f..00000000 --- a/src/arithmetic/big_gmp.rs +++ /dev/null @@ -1,432 +0,0 @@ -/* - Curv - - Copyright 2018 by Kzen Networks - - This file is part of Cryptography utilities library - (https://github.com/KZen-networks/cryptography-utils) - - Cryptography utilities is free software: you can redistribute - it and/or modify it under the terms of the GNU General Public - License as published by the Free Software Foundation, either - version 3 of the License, or (at your option) any later version. - - @license GPL-3.0+ -*/ - -use std::convert::{TryFrom, TryInto}; -use std::sync::atomic; -use std::{fmt, ops, ptr}; - -use gmp::mpz::Mpz; -use gmp::sign::Sign; -use num_traits::{One, Zero}; -use serde::{Deserialize, Serialize}; -use zeroize::Zeroize; - -use super::errors::*; -use super::traits::*; - -type BN = Mpz; - -/// Big integer -/// -/// Wraps underlying BigInt implementation (either GMP bindings or num-bigint), exposes only -/// very limited API that allows easily switching between implementations. -/// -/// Set of traits implemented on BigInt remains the same regardless of underlying implementation. -#[derive(PartialOrd, PartialEq, Ord, Eq, Clone, Serialize, Deserialize)] -#[serde(transparent)] -pub struct BigInt { - gmp: Mpz, -} - -impl BigInt { - fn inner_ref(&self) -> &Mpz { - &self.gmp - } - fn inner_mut(&mut self) -> &mut Mpz { - &mut self.gmp - } - fn into_inner(self) -> Mpz { - self.gmp - } -} - -#[allow(deprecated)] -impl ZeroizeBN for BigInt { - fn zeroize_bn(&mut self) { - unsafe { ptr::write_volatile(&mut self.gmp, Mpz::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } -} - -impl Zeroize for BigInt { - fn zeroize(&mut self) { - unsafe { ptr::write_volatile(&mut self.gmp, Mpz::zero()) }; - atomic::fence(atomic::Ordering::SeqCst); - atomic::compiler_fence(atomic::Ordering::SeqCst); - } -} - -impl Converter for BigInt { - fn to_bytes(&self) -> Vec { - (&self.gmp).into() - } - - fn from_bytes(bytes: &[u8]) -> Self { - Mpz::from(bytes).wrap() - } - - fn to_hex(&self) -> String { - self.gmp.to_str_radix(16) - } - - fn from_hex(value: &str) -> Result { - Mpz::from_str_radix(value, 16) - .map(Wrap::wrap) - .map_err(|e| ParseBigIntError { - reason: ParseErrorReason::Gmp(e), - radix: 16, - }) - } - - fn to_str_radix(&self, radix: u8) -> String { - self.gmp.to_str_radix(radix) - } - - fn from_str_radix(str: &str, radix: u8) -> Result { - Mpz::from_str_radix(str, radix) - .map(Wrap::wrap) - .map_err(|e| ParseBigIntError { - reason: ParseErrorReason::Gmp(e), - radix: radix.into(), - }) - } -} - -impl num_traits::Num for BigInt { - type FromStrRadixErr = ParseBigIntError; - fn from_str_radix(str: &str, radix: u32) -> Result { - ::from_str_radix(str, radix.try_into().unwrap()) - } -} - -impl BasicOps for BigInt { - fn pow(&self, exponent: u32) -> Self { - self.gmp.pow(exponent).wrap() - } - - fn mul(&self, other: &Self) -> Self { - self * other - } - - fn sub(&self, other: &Self) -> Self { - self - other - } - - fn add(&self, other: &Self) -> Self { - self + other - } - - fn abs(&self) -> Self { - self.gmp.abs().wrap() - } -} - -impl Primes for BigInt { - fn next_prime(&self) -> Self { - self.gmp.nextprime().wrap() - } - - fn is_probable_prime(&self, n: u32) -> bool { - use gmp::mpz::ProbabPrimeResult::*; - match self.gmp.probab_prime(n as i32) { - Prime | ProbablyPrime => true, - NotPrime => false, - } - } -} - -impl Modulo for BigInt { - fn mod_pow(base: &Self, exponent: &Self, modulus: &Self) -> Self { - assert!(exponent >= &BigInt::zero(), "exponent must be non-negative"); - base.gmp.powm(&exponent.gmp, &modulus.gmp).wrap() - } - - fn mod_mul(a: &Self, b: &Self, modulus: &Self) -> Self { - (a.gmp.mod_floor(&modulus.gmp) * b.gmp.mod_floor(&modulus.gmp)) - .mod_floor(&modulus.gmp) - .wrap() - } - - fn mod_sub(a: &Self, b: &Self, modulus: &Self) -> Self { - let a_m = a.gmp.mod_floor(&modulus.gmp); - let b_m = b.gmp.mod_floor(&modulus.gmp); - - let sub_op = a_m - b_m + &modulus.gmp; - sub_op.mod_floor(&modulus.gmp).wrap() - } - - fn mod_add(a: &Self, b: &Self, modulus: &Self) -> Self { - (a.gmp.mod_floor(&modulus.gmp) + b.gmp.mod_floor(&modulus.gmp)) - .mod_floor(&modulus.gmp) - .wrap() - } - - fn mod_inv(a: &Self, modulus: &Self) -> Option { - Some(a.gmp.invert(&modulus.gmp)?.wrap()) - } - - fn modulus(&self, modulus: &Self) -> Self { - self.gmp.modulus(&modulus.gmp).wrap() - } -} - -impl NumberTests for BigInt { - fn is_zero(me: &Self) -> bool { - me.gmp.is_zero() - } - fn is_negative(me: &Self) -> bool { - matches!(me.gmp.sign(), Sign::Negative) - } -} - -impl EGCD for BigInt { - #[allow(clippy::many_single_char_names)] - fn egcd(a: &Self, b: &Self) -> (Self, Self, Self) { - let (s, p, q) = a.gmp.gcdext(&b.gmp); - (s.wrap(), p.wrap(), q.wrap()) - } -} - -impl BitManipulation for BigInt { - fn set_bit(&mut self, bit: usize, bit_val: bool) { - if bit_val { - self.gmp.setbit(bit); - } else { - self.gmp.clrbit(bit); - } - } - - fn test_bit(&self, bit: usize) -> bool { - self.gmp.tstbit(bit) - } - - fn bit_length(&self) -> usize { - self.gmp.bit_length() - } -} - -impl Integer for BigInt { - fn div_floor(&self, other: &Self) -> Self { - self.gmp.div_floor(&other.gmp).wrap() - } - - fn mod_floor(&self, other: &Self) -> Self { - self.gmp.mod_floor(&other.gmp).wrap() - } - - fn gcd(&self, other: &Self) -> Self { - self.gmp.gcd(&other.gmp).wrap() - } - - fn lcm(&self, other: &Self) -> Self { - self.gmp.lcm(&other.gmp).wrap() - } - - fn divides(&self, other: &Self) -> bool { - self.gmp.divides(&other.gmp) - } - - fn is_multiple_of(&self, other: &Self) -> bool { - self.gmp.is_multiple_of(&other.gmp) - } - - fn is_even(&self) -> bool { - self.gmp.is_multiple_of(&Mpz::from(2)) - } - - fn is_odd(&self) -> bool { - !self.gmp.is_multiple_of(&Mpz::from(2)) - } - - fn div_rem(&self, other: &Self) -> (Self, Self) { - let n = self / other; - let m = self % other; - (n, m) - } -} - -impl Roots for BigInt { - fn nth_root(&self, n: u32) -> Self { - self.gmp.root(n).wrap() - } - - fn sqrt(&self) -> Self { - self.gmp.sqrt().wrap() - } -} - -impl fmt::Display for BigInt { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.gmp.fmt(f) - } -} - -impl fmt::Debug for BigInt { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.gmp.fmt(f) - } -} - -macro_rules! impl_try_from { - ($($primitive:ty),*$(,)?) => { - $( - impl TryFrom<&BigInt> for $primitive { - type Error = TryFromBigIntError; - - fn try_from(value: &BigInt) -> Result { - Option::<$primitive>::from(&value.gmp) - .ok_or(TryFromBigIntError { type_name: stringify!($primitive) }) - } - } - )* - }; -} - -impl_try_from! { u64, i64 } - -#[allow(deprecated)] -impl ConvertFrom for u64 { - fn _from(x: &BigInt) -> u64 { - let opt_x: Option = (&x.gmp).into(); - opt_x.unwrap() - } -} - -crate::__bigint_impl_ops! { - Add add, - Sub sub, - Mul mul, - Div div, - Rem rem, - BitAnd bitand, - BitXor bitxor, - Shl shl usize, - Shr shr usize, - - Add add u64 [swap], - Sub sub u64 [swap], - Mul mul u64 [swap], - Div div u64, - Rem rem u64, -} - -crate::__bigint_impl_assigns! { - AddAssign add_assign, - AddAssign add_assign u64, - BitAndAssign bitand_assign, - BitOrAssign bitor_assign, - BitXorAssign bitxor_assign, - DivAssign div_assign, - DivAssign div_assign u64, - MulAssign mul_assign, - MulAssign mul_assign u64, - RemAssign rem_assign, - RemAssign rem_assign u64, - ShlAssign shl_assign usize, - ShrAssign shr_assign usize, - SubAssign sub_assign, - SubAssign sub_assign u64, -} - -impl ops::Neg for BigInt { - type Output = BigInt; - fn neg(self) -> Self::Output { - self.gmp.neg().wrap() - } -} -impl ops::Neg for &BigInt { - type Output = BigInt; - fn neg(self) -> Self::Output { - (&self.gmp).neg().wrap() - } -} - -impl Zero for BigInt { - fn zero() -> Self { - Mpz::zero().wrap() - } - - fn is_zero(&self) -> bool { - self.gmp.is_zero() - } -} - -impl One for BigInt { - fn one() -> Self { - Mpz::one().wrap() - } - fn is_one(&self) -> bool { - self.gmp.is_one() - } -} - -impl ring_algorithm::RingNormalize for BigInt { - fn leading_unit(&self) -> Self { - match self.gmp.sign() { - Sign::Negative => -BigInt::one(), - _ => BigInt::one(), - } - } - - fn normalize_mut(&mut self) { - self.gmp = self.gmp.abs(); - } -} - -crate::__bigint_impl_from! { u32, i32, u64 } - -/// Internal helper trait. Creates short-hand for wrapping Mpz into BigInt. -trait Wrap { - fn wrap(self) -> BigInt; -} -impl Wrap for Mpz { - fn wrap(self) -> BigInt { - BigInt { gmp: self } - } -} - -/// Tests that ring_algorithm work as expected -#[cfg(test)] -mod ring_algorithm_test { - const PRIME: u32 = u32::MAX - 4; - - use super::*; - - proptest::proptest! { - #[test] - fn fuzz_inverse(n in 1..PRIME) { - test_inverse(BigInt::from(n)) - } - #[test] - fn fuzz_xgcd(a in 1u32.., b in 1u32..) { - test_xgcd(BigInt::from(a), BigInt::from(b)) - } - } - - fn test_inverse(n: BigInt) { - let prime = BigInt::from(PRIME); - let n_inv_expected = BigInt::mod_inv(&n, &prime).unwrap(); - let n_inv_actual = ring_algorithm::modulo_inverse(n, prime.clone()).unwrap(); - assert_eq!(n_inv_expected, n_inv_actual.modulus(&prime)); - } - - fn test_xgcd(a: BigInt, b: BigInt) { - let (s1, p1, q1) = BigInt::egcd(&a, &b); - let (s2, p2, q2) = ring_algorithm::normalized_extended_euclidian_algorithm(a, b); - assert_eq!((s1, p1, q1), (s2, p2, q2)); - } -} diff --git a/src/arithmetic/errors.rs b/src/arithmetic/errors.rs index d26e5244..425c5f44 100644 --- a/src/arithmetic/errors.rs +++ b/src/arithmetic/errors.rs @@ -4,13 +4,14 @@ use std::{error, fmt}; #[derive(Debug)] pub struct ParseBigIntError { pub(super) reason: ParseErrorReason, - pub(super) radix: u32, + pub(super) radix: u8, } #[derive(Debug)] +#[non_exhaustive] pub enum ParseErrorReason { - #[cfg(feature = "rust-gmp-kzen")] - Gmp(gmp::mpz::ParseMpzError), + #[cfg(feature = "rug")] + Gmp(rug::integer::ParseIntegerError), #[cfg(feature = "num-bigint")] NumBigint, } @@ -18,7 +19,7 @@ pub enum ParseErrorReason { impl fmt::Display for ParseBigIntError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.reason { - #[cfg(feature = "rust-gmp-kzen")] + #[cfg(feature = "rug")] ParseErrorReason::Gmp(reason) => write!(f, "{}", reason), #[cfg(feature = "num-bigint")] ParseErrorReason::NumBigint => { @@ -31,7 +32,7 @@ impl fmt::Display for ParseBigIntError { impl error::Error for ParseBigIntError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match &self.reason { - #[cfg(feature = "rust-gmp-kzen")] + #[cfg(feature = "rug")] ParseErrorReason::Gmp(reason) => Some(reason), #[cfg(feature = "num-bigint")] ParseErrorReason::NumBigint => None, diff --git a/src/arithmetic/gmp.rs b/src/arithmetic/gmp.rs new file mode 100644 index 00000000..1c72732e --- /dev/null +++ b/src/arithmetic/gmp.rs @@ -0,0 +1,165 @@ +/* + Curv + + Copyright 2018 by Kzen Networks + + This file is part of Cryptography utilities library + (https://github.com/KZen-networks/cryptography-utils) + + Cryptography utilities is free software: you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + @license GPL-3.0+ +*/ + +use std::cmp::Ordering; +use std::convert::{TryFrom, TryInto}; +use std::ops::{Add, AddAssign, Mul}; +use std::sync::atomic; +use std::{fmt, ops, ptr, slice}; + +use num_traits::{One, Zero}; +use rug::integer::Order; +use rug::ops::RemRounding; +pub use rug::Integer as BigInt; +use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; + +use crate::arithmetic::traits::{self, BigInt as _, NumberTheoreticOps as _}; + +use super::errors::*; +use rug::Assign; + +/// Big integer +/// +/// Wraps underlying BigInt implementation (either GMP bindings or num-bigint), exposes only +/// very limited API that allows easily switching between implementations. +/// +/// Set of traits implemented on BigInt remains the same regardless of underlying implementation. + +impl traits::BigInt for BigInt { + fn zero() -> Self { + Self::new() + } + + fn is_zero(&self) -> bool { + self.cmp0() == Ordering::Equal + } + + fn set_zero(&mut self) { + self.assign(0); + } + + fn one() -> Self { + Self::from(1u8) + } + + fn set_one(&mut self) { + self.assign(1); + } + + fn is_negative(&self) -> bool { + self.cmp0() == Ordering::Less + } + + fn set_bit(&mut self, bit: usize, bit_val: bool) { + self.set_bit( + u32::try_from(bit).expect("There shouldn't be more than 2^32-1 bits"), + bit_val, + ); + } + + fn test_bit(&self, bit: usize) -> bool { + self.get_bit(u32::try_from(bit).expect("There shouldn't be more than 2^32-1 bits")) + } + + fn bit_length(&self) -> usize { + usize::try_from(self.significant_bits()).expect("usize should always be bigger than u32") + } + + fn to_bytes(&self) -> Vec { + self.to_digits(Order::MsfBe) + } + + fn from_bytes(bytes: &[u8]) -> Self { + Self::from_digits(bytes, Order::MsfBe) + } + + fn to_str_radix(&self, radix: u8) -> String { + self.to_string_radix(i32::from(radix)) + } + + fn from_str_radix(s: &str, radix: u8) -> Result { + Self::from_str_radix(s, i32::from(radix)).map_err(|e| ParseBigIntError { + reason: ParseErrorReason::Gmp(e), + radix, + }) + } + + fn zeroize(&mut self) { + let mpz = unsafe { self.as_raw_mut().read() }; + let mut ptr = mpz.d.as_ptr(); + for _ in 0..mpz.alloc { + unsafe { + // SAFETY: The pointer is properly aligned and valid + // because we got it from the gmp allocation which allocates limbs + // The pointer is valid for writes because we assume `rug` handles it correctly. + ptr.write_volatile(0); + // SAFETY: The starting pointer is in bounds, + // and the last pointer will point at a single byte past the last element. + ptr = ptr.add(1) + } + } + atomic::fence(atomic::Ordering::SeqCst); + atomic::compiler_fence(atomic::Ordering::SeqCst); + } +} + +impl traits::NumberTheoreticOps for BigInt { + fn mod_pow(&self, exponent: &Self, modulo: &Self) -> Self { + Self::from( + self.pow_mod_ref(exponent, modulo) + .expect("exponent must be non-negative"), + ) + } + + fn mod_mul(&self, b: &Self, modulus: &Self) -> Self { + BigInt::from(self * b).rem_floor(modulus) + } + + fn mod_sub(&self, b: &Self, modulus: &Self) -> Self { + BigInt::from(self - b).rem_floor(modulus) + } + + fn mod_add(&self, b: &Self, modulus: &Self) -> Self { + todo!() + } + + fn mod_inv(&self, modulo: &Self) -> Option { + todo!() + } + + fn modulus(&self, modulus: &Self) -> Self { + todo!() + } + + fn egcd(&self, b: &Self) -> (Self, Self, Self) { + let (s, p, q) = self.clone().gcd_cofactors(b.clone(), Self::zero()); + (s, p, q) + } + + fn gcd(&self, m: &Self) -> Self { + self.clone().gcd(&m) + } + + fn next_prime(&self) -> Self { + self.next_prime() + } + + fn is_probable_prime(&self, n: u32) -> bool { + use rug::integer::IsPrime; + self.is_probably_prime() != IsPrime::No + } +}