diff --git a/benches/int.rs b/benches/int.rs index 31c07253a..922e05176 100644 --- a/benches/int.rs +++ b/benches/int.rs @@ -4,7 +4,7 @@ use rand_core::SeedableRng; use std::hint::black_box; use std::ops::Div; -use crypto_bigint::{I128, I256, I512, I1024, I2048, I4096, NonZero, Random, nlimbs}; +use crypto_bigint::{I128, I256, I512, I1024, I2048, I4096, NonZero, Random}; fn bench_mul(c: &mut Criterion) { let mut rng = ChaCha8Rng::from_seed([7u8; 32]); @@ -66,7 +66,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I128xI128", |b| { b.iter_batched( || (I128::random(&mut rng), I128::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul::<{ I128::LIMBS }, { I256::LIMBS }>(&y)), + |(x, y)| black_box(x.concatenating_mul(&y)), BatchSize::SmallInput, ) }); @@ -74,7 +74,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I256xI256", |b| { b.iter_batched( || (I256::random(&mut rng), I256::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul::<{ I256::LIMBS }, { I512::LIMBS }>(&y)), + |(x, y)| black_box(x.concatenating_mul(&y)), BatchSize::SmallInput, ) }); @@ -82,7 +82,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I512xI512", |b| { b.iter_batched( || (I512::random(&mut rng), I512::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul::<{ I512::LIMBS }, { I1024::LIMBS }>(&y)), + |(x, y)| black_box(x.concatenating_mul(&y)), BatchSize::SmallInput, ) }); @@ -90,7 +90,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I1024xI1024", |b| { b.iter_batched( || (I1024::random(&mut rng), I1024::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul::<{ I1024::LIMBS }, { I2048::LIMBS }>(&y)), + |(x, y)| black_box(x.concatenating_mul(&y)), BatchSize::SmallInput, ) }); @@ -98,7 +98,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I2048xI2048", |b| { b.iter_batched( || (I2048::random(&mut rng), I2048::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul::<{ I2048::LIMBS }, { I4096::LIMBS }>(&y)), + |(x, y)| black_box(x.concatenating_mul(&y)), BatchSize::SmallInput, ) }); @@ -106,7 +106,7 @@ fn bench_concatenating_mul(c: &mut Criterion) { group.bench_function("concatenating_mul, I4096xI4096", |b| { b.iter_batched( || (I4096::random(&mut rng), I4096::random(&mut rng)), - |(x, y)| black_box(x.concatenating_mul::<{ I4096::LIMBS }, { nlimbs!(8192) }>(&y)), + |(x, y)| black_box(x.concatenating_mul(&y)), BatchSize::SmallInput, ) }); diff --git a/src/int/gcd.rs b/src/int/gcd.rs index 78ac920bf..abc6e7d56 100644 --- a/src/int/gcd.rs +++ b/src/int/gcd.rs @@ -312,7 +312,7 @@ impl Xgcd for OddInt { #[cfg(all(test, not(miri)))] mod tests { use crate::int::gcd::{IntXgcdOutput, NonZeroIntXgcdOutput, OddIntXgcdOutput}; - use crate::{ConcatenatingMul, Gcd, Int, Uint}; + use crate::{ConcatMixed, Gcd, Int, Uint}; use num_traits::Zero; impl From> for IntXgcdOutput { @@ -409,7 +409,7 @@ mod tests { rhs: Int, output: IntXgcdOutput, ) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let gcd = lhs.gcd(&rhs); assert_eq!(gcd, output.gcd); @@ -437,20 +437,20 @@ mod tests { assert_eq!( x.concatenating_mul(&lhs) .wrapping_add(&y.concatenating_mul(&rhs)), - *gcd.resize::().as_int() + *gcd.resize().as_int() ); } mod test_int_xgcd { use crate::int::gcd::tests::xgcd_test; use crate::{ - ConcatenatingMul, Gcd, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, - U4096, U8192, Uint, + ConcatMixed, Gcd, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, + U8192, Uint, }; fn test(lhs: Int, rhs: Int) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, Int: Gcd>, { xgcd_test(lhs, rhs, lhs.xgcd(&rhs)) @@ -458,7 +458,7 @@ mod tests { fn run_tests() where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, Int: Gcd>, { test(Int::MIN, Int::MIN); @@ -505,13 +505,13 @@ mod tests { mod test_nonzero_int_xgcd { use crate::int::gcd::tests::xgcd_test; use crate::{ - ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, - U8192, Uint, + ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, + Uint, }; fn test(lhs: Int, rhs: Int) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let output = lhs.to_nz().unwrap().xgcd(&rhs.to_nz().unwrap()); xgcd_test(lhs, rhs, output.into()); @@ -519,7 +519,7 @@ mod tests { fn run_tests() where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { test(Int::MIN, Int::MIN); test(Int::MIN, Int::MINUS_ONE); @@ -556,13 +556,13 @@ mod tests { mod test_odd_int_xgcd { use crate::int::gcd::tests::xgcd_test; use crate::{ - ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, - U8192, Uint, + ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, + Uint, }; fn test(lhs: Int, rhs: Int) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let output = lhs.to_odd().unwrap().xgcd(&rhs.to_nz().unwrap()); xgcd_test(lhs, rhs, output.into()); @@ -570,7 +570,7 @@ mod tests { fn run_tests() where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let neg_max = Int::MAX.wrapping_neg(); test(neg_max, neg_max); diff --git a/src/int/mul.rs b/src/int/mul.rs index 643011640..9364ef7b6 100644 --- a/src/int/mul.rs +++ b/src/int/mul.rs @@ -4,7 +4,7 @@ use core::ops::{Mul, MulAssign}; use num_traits::WrappingMul; use subtle::CtOption; -use crate::{Checked, CheckedMul, ConstChoice, ConstCtOption, Int, Uint, Zero}; +use crate::{Checked, CheckedMul, ConcatMixed, ConstChoice, ConstCtOption, Int, Uint, Zero}; impl Int { /// Compute "wide" multiplication as a 3-tuple `(lo, hi, negate)`. @@ -51,7 +51,10 @@ impl Int { pub const fn concatenating_mul( &self, rhs: &Int, - ) -> Int { + ) -> Int + where + Uint: ConcatMixed, MixedOutput = Uint>, + { let (lhs_abs, lhs_sign) = self.abs_sign(); let (rhs_abs, rhs_sign) = rhs.abs_sign(); let product_abs = lhs_abs.concatenating_mul(&rhs_abs); @@ -73,7 +76,10 @@ impl Int { /// Squaring operations. impl Int { /// Square self, returning a concatenated "wide" result. - pub fn widening_square(&self) -> Uint { + pub fn widening_square(&self) -> Uint + where + Uint: ConcatMixed, MixedOutput = Uint>, + { self.abs().widening_square() } diff --git a/src/int/mul_uint.rs b/src/int/mul_uint.rs index f95ac235b..e0868fbc9 100644 --- a/src/int/mul_uint.rs +++ b/src/int/mul_uint.rs @@ -1,7 +1,7 @@ use core::ops::Mul; use subtle::CtOption; -use crate::{CheckedMul, ConstChoice, ConstCtOption, Int, Uint}; +use crate::{CheckedMul, ConcatMixed, ConstChoice, ConstCtOption, Int, Uint}; impl Int { /// Compute "wide" multiplication between an [`Int`] and [`Uint`] as 3-tuple `(lo, hi, negate)`. @@ -70,7 +70,10 @@ impl Int { pub const fn concatenating_mul_uint( &self, rhs: &Uint, - ) -> Int { + ) -> Int + where + Uint: ConcatMixed, MixedOutput = Uint>, + { let (lhs_abs, lhs_sign) = self.abs_sign(); let product_abs = lhs_abs.concatenating_mul(rhs); diff --git a/src/lib.rs b/src/lib.rs index 234b5edb3..f81f682e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -167,8 +167,6 @@ #[macro_use] extern crate alloc; -pub use uint::encoding::{EncodedUint, TryFromSliceError}; - #[cfg(feature = "rand_core")] pub use rand_core; #[cfg(feature = "rlp")] diff --git a/src/modular.rs b/src/modular.rs index 3646f88da..809814d3b 100644 --- a/src/modular.rs +++ b/src/modular.rs @@ -55,7 +55,7 @@ pub trait Retrieve { #[cfg(test)] mod tests { use crate::{ - NonZero, U64, U128, U256, U512, Uint, const_monty_params, + NonZero, U64, U128, U256, Uint, const_monty_params, modular::{ const_monty_form::{ConstMontyForm, ConstMontyParams}, mul::{mul_montgomery_form, square_montgomery_form}, @@ -122,7 +122,7 @@ mod tests { #[test] fn test_reducing_r2_wide() { // Divide the value ONE^2 by R, which should equal ONE - let (lo, hi) = Modulus256::PARAMS.one.square::<{ nlimbs!(512) }>().split(); + let (lo, hi) = Modulus256::PARAMS.one.square().split(); assert_eq!( montgomery_reduction::<{ Modulus256::LIMBS }>( &(lo, hi), @@ -158,7 +158,7 @@ mod tests { // Computing xR mod modulus without Montgomery reduction let (lo, hi) = x.widening_mul(&Modulus256::PARAMS.one); - let c: U512 = lo.concat(&hi); + let c = lo.concat(&hi); let red = c.rem_vartime(&NonZero::new(Modulus256::PARAMS.modulus.0.concat(&U256::ZERO)).unwrap()); let (lo, hi) = red.split(); @@ -287,7 +287,7 @@ mod tests { // Computing xR mod modulus without Montgomery reduction let (lo, hi) = x.widening_mul(&Modulus256::PARAMS.one); - let c: U512 = lo.concat(&hi); + let c = lo.concat(&hi); let red = c.rem_vartime(&NonZero::new(Modulus256::PARAMS.modulus.0.concat(&U256::ZERO)).unwrap()); let (lo, hi) = red.split(); diff --git a/src/modular/bingcd/xgcd.rs b/src/modular/bingcd/xgcd.rs index 0813352ba..fd5631ec2 100644 --- a/src/modular/bingcd/xgcd.rs +++ b/src/modular/bingcd/xgcd.rs @@ -369,7 +369,7 @@ impl Uint { #[cfg(all(test, not(miri)))] mod tests { use crate::modular::bingcd::xgcd::PatternXgcdOutput; - use crate::{ConcatenatingMul, Gcd, Uint}; + use crate::{ConcatMixed, Gcd, Uint}; use core::ops::Div; use num_traits::Zero; @@ -584,7 +584,7 @@ mod tests { rhs: Uint, output: PatternXgcdOutput, ) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { // Test the gcd assert_eq!(lhs.gcd(&rhs), output.gcd, "{lhs} {rhs}"); @@ -598,7 +598,7 @@ mod tests { let (x, y) = output.bezout_coefficients(); assert_eq!( x.concatenating_mul_uint(&lhs) + y.concatenating_mul_uint(&rhs), - *output.gcd.resize::().as_int(), + *output.gcd.resize().as_int(), ); // Test the Bezout coefficients for minimality @@ -613,15 +613,15 @@ mod tests { mod test_binxgcd_nz { use crate::modular::bingcd::xgcd::tests::test_xgcd; use crate::{ - ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, - U8192, Uint, + ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, + Uint, }; fn binxgcd_nz_test( lhs: Uint, rhs: Uint, ) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let output = lhs.to_odd().unwrap().binxgcd_nz(&rhs.to_nz().unwrap()); test_xgcd(lhs, rhs, output); @@ -629,7 +629,7 @@ mod tests { fn binxgcd_nz_tests() where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let max_int = *Int::MAX.as_uint(); let int_abs_min = Int::MIN.abs(); @@ -665,15 +665,15 @@ mod tests { mod test_classic_binxgcd { use crate::modular::bingcd::xgcd::tests::test_xgcd; use crate::{ - ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, - U8192, Uint, + ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, + Uint, }; fn classic_binxgcd_test( lhs: Uint, rhs: Uint, ) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let output = lhs .to_odd() @@ -685,7 +685,7 @@ mod tests { fn classic_binxgcd_tests() where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let max_int = *Int::MAX.as_uint(); @@ -718,13 +718,13 @@ mod tests { use crate::modular::bingcd::xgcd::tests::test_xgcd; use crate::modular::bingcd::xgcd::{DOUBLE_SUMMARY_LIMBS, SUMMARY_BITS, SUMMARY_LIMBS}; use crate::{ - ConcatenatingMul, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, - U8192, Uint, + ConcatMixed, Int, U64, U128, U192, U256, U384, U512, U768, U1024, U2048, U4096, U8192, + Uint, }; fn test(lhs: Uint, rhs: Uint) where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let output = lhs .to_odd() @@ -736,7 +736,7 @@ mod tests { fn run_tests() where - Uint: ConcatenatingMul>, + Uint: ConcatMixed, MixedOutput = Uint>, { let upper_bound = *Int::MAX.as_uint(); test(Uint::ONE, Uint::ONE); @@ -771,10 +771,10 @@ mod tests { a.compact::(U256::BITS) < b.compact::(U256::BITS) ); - test::<{ U256::LIMBS }, { U512::LIMBS }>(a, b); + test(a, b); // Case #2: a < b but a.compact() > b.compact() - test::<{ U256::LIMBS }, { U512::LIMBS }>(b, a); + test(b, a); // Case #3: a > b but a.compact() = b.compact() let a = U256::from_be_hex( @@ -788,10 +788,10 @@ mod tests { a.compact::(U256::BITS), b.compact::(U256::BITS) ); - test::<{ U256::LIMBS }, { U512::LIMBS }>(a, b); + test(a, b); // Case #4: a < b but a.compact() = b.compact() - test::<{ U256::LIMBS }, { U512::LIMBS }>(b, a); + test(b, a); } #[test] diff --git a/src/traits.rs b/src/traits.rs index 11452cd08..1a49c2f15 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -595,17 +595,45 @@ pub trait CheckedSub: Sized { fn checked_sub(&self, rhs: &Rhs) -> CtOption; } -/// Concatenate two numbers into a "wide" combined-width value. -pub trait Concat { +/// Concatenate two numbers into a "wide" double-width value, using the `hi` value as the most +/// significant portion of the resulting value. +pub trait Concat: ConcatMixed { + /// Concatenated output: twice the width of `Self`. + type Output: Integer; + + /// Concatenate the two halves, with `self` as least significant and `hi` as the most significant. + fn concat(&self, hi: &Self) -> Self::Output { + self.concat_mixed(hi) + } +} + +/// Concatenate two numbers into a "wide" combined-width value, using the `hi` value as the most +/// significant value. +pub trait ConcatMixed { + /// Concatenated output: combination of `Self` and `Hi`. + type MixedOutput: Integer; + /// Concatenate the two values, with `self` as least significant and `hi` as the most /// significant. - fn concat(&self, hi: &Hi) -> Output; + fn concat_mixed(&self, hi: &Hi) -> Self::MixedOutput; } -/// Split a number into parts. -pub trait Split { +/// Split a number in half, returning the least significant half followed by the most significant. +pub trait Split: SplitMixed { + /// Split output: low/high components of the value. + type Output; + + /// Split this number in half, returning its low and high components respectively. + fn split(&self) -> (Self::Output, Self::Output) { + self.split_mixed() + } +} + +/// Split a number into parts, returning the least significant part followed by the most +/// significant. +pub trait SplitMixed { /// Split this number into parts, returning its low and high components respectively. - fn split(&self) -> (Lo, Hi); + fn split_mixed(&self) -> (Lo, Hi); } /// Encoding support. @@ -616,7 +644,7 @@ pub trait Encoding: Sized { + Copy + Clone + Sized - + for<'a> TryFrom<&'a [u8], Error: core::error::Error>; + + for<'a> TryFrom<&'a [u8], Error = core::array::TryFromSliceError>; /// Decode from big endian bytes. fn from_be_bytes(bytes: Self::Repr) -> Self; @@ -699,6 +727,12 @@ pub trait DivRemLimb: Sized { fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb); } +/// Support for calculating the remainder of two differently sized integers. +pub trait RemMixed: Sized { + /// Calculate the remainder of `self` by the `reductor`. + fn rem_mixed(&self, reductor: &NonZero) -> Reductor; +} + /// Modular reduction from a larger value `T`. /// /// This can be seen as fixed modular reduction, where the modulus is fixed at compile time @@ -866,26 +900,33 @@ pub trait Invert { /// Widening multiply: returns a value with a number of limbs equal to the sum of the inputs. #[deprecated(since = "0.7.0", note = "please use `ConcatenatingMul` instead")] -pub trait WideningMul: Sized { +pub trait WideningMul: Sized { + /// Output of the widening multiplication. + type Output: Integer; + /// Perform widening multiplication. - fn widening_mul(&self, rhs: Rhs) -> Output; + fn widening_mul(&self, rhs: Rhs) -> Self::Output; } #[allow(deprecated)] -impl WideningMul for T +impl WideningMul for T where - T: ConcatenatingMul, + T: ConcatenatingMul, { - fn widening_mul(&self, rhs: Rhs) -> Output { + type Output = >::Output; + + fn widening_mul(&self, rhs: Rhs) -> Self::Output { self.concatenating_mul(rhs) } } -/// Widening multiply: returns a value with a number of limbs greater than or equal to -/// the sum of the inputs. -pub trait ConcatenatingMul: Sized { +/// Widening multiply: returns a value with a number of limbs equal to the sum of the inputs. +pub trait ConcatenatingMul: Sized { + /// Output of the widening multiplication. + type Output: Integer; + /// Perform widening multiplication. - fn concatenating_mul(&self, rhs: Rhs) -> Output; + fn concatenating_mul(&self, rhs: Rhs) -> Self::Output; } /// Left shifts, variable time in `shift`. diff --git a/src/uint.rs b/src/uint.rs index 896720197..c27ae6a81 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -16,13 +16,10 @@ pub use extra_sizes::*; pub(crate) use ref_type::UintRef; use crate::{ - Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, FixedInteger, Int, - Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm, + Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, Encoding, FixedInteger, + Int, Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm, }; -#[cfg(feature = "serde")] -use crate::Encoding; - #[macro_use] mod macros; @@ -416,10 +413,10 @@ where where D: Deserializer<'de>, { - let mut buffer = Encoding::to_le_bytes(&Self::ZERO); + let mut buffer = Self::ZERO.to_le_bytes(); serdect::array::deserialize_hex_or_bin(buffer.as_mut(), deserializer)?; - Ok(Encoding::from_le_bytes(buffer)) + Ok(Self::from_le_bytes(buffer)) } } @@ -432,7 +429,7 @@ where where S: Serializer, { - serdect::slice::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer) + serdect::array::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer) } } @@ -478,6 +475,58 @@ impl_uint_aliases! { (U544, 544, "544-bit") // For NIST P-521 } +#[cfg(target_pointer_width = "32")] +impl_uint_concat_split_even! { + U64, +} + +// Implement concat and split for double-width Uint sizes: these should be +// multiples of 128 bits. +impl_uint_concat_split_even! { + U128, + U256, + U384, + U512, + U640, + U768, + U896, + U1024, + U1280, + U1536, + U1792, + U2048, + U3072, + U3584, + U4096, + U4224, + U4352, + U6144, + U8192, + U16384, +} + +// Implement mixed concat, split and reduce for combinations not implemented by +// impl_uint_concat_split_even. The numbers represent the size of each +// component Uint in multiple of 64 bits. For example, +// (U256, [1, 3]) will allow splitting U256 into (U64, U192) as well as +// (U192, U64), while the (U128, U128) combination is already covered. +impl_uint_concat_split_mixed! { + (U192, [1, 2]), + (U256, [1, 3]), + (U320, [1, 2, 3, 4]), + (U384, [1, 2, 4, 5]), + (U448, [1, 2, 3, 4, 5, 6]), + (U512, [1, 2, 3, 5, 6, 7]), + (U576, [1, 2, 3, 4, 5, 6, 7, 8]), + (U640, [1, 2, 3, 4, 6, 7, 8, 9]), + (U704, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + (U768, [1, 2, 3, 4, 5, 7, 8, 9, 10, 11]), + (U832, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), + (U896, [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13]), + (U960, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]), + (U1024, [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15]), +} + #[cfg(feature = "extra-sizes")] mod extra_sizes; mod mul_int; @@ -606,7 +655,7 @@ mod tests { let be_bytes = a.to_be_bytes(); let le_bytes = a.to_le_bytes(); for i in 0..16 { - assert_eq!(le_bytes.as_ref()[i], be_bytes.as_ref()[15 - i]); + assert_eq!(le_bytes[i], be_bytes[15 - i]); } let a_from_be = U128::from_be_bytes(be_bytes); diff --git a/src/uint/boxed/div.rs b/src/uint/boxed/div.rs index 6ff7e4ff2..8d3820690 100644 --- a/src/uint/boxed/div.rs +++ b/src/uint/boxed/div.rs @@ -2,7 +2,7 @@ use crate::{ BoxedUint, CheckedDiv, ConstantTimeSelect, DivRemLimb, DivVartime, Limb, NonZero, Reciprocal, - RemLimb, UintRef, Wrapping, + RemLimb, RemMixed, UintRef, Wrapping, }; use core::ops::{Div, DivAssign, Rem, RemAssign}; use subtle::CtOption; @@ -287,6 +287,12 @@ impl RemLimb for BoxedUint { } } +impl RemMixed for BoxedUint { + fn rem_mixed(&self, reductor: &NonZero) -> BoxedUint { + Self::div_rem_vartime(self, reductor).1 + } +} + #[cfg(test)] mod tests { use crate::{DivVartime, Resize, Zero}; diff --git a/src/uint/boxed/mul.rs b/src/uint/boxed/mul.rs index aaa914fde..d4add6dee 100644 --- a/src/uint/boxed/mul.rs +++ b/src/uint/boxed/mul.rs @@ -161,14 +161,18 @@ impl MulAssign<&Wrapping> for Wrapping { } } -impl ConcatenatingMul for BoxedUint { +impl ConcatenatingMul for BoxedUint { + type Output = Self; + #[inline] fn concatenating_mul(&self, rhs: BoxedUint) -> Self { self.mul(&rhs) } } -impl ConcatenatingMul for BoxedUint { +impl ConcatenatingMul<&BoxedUint> for BoxedUint { + type Output = Self; + #[inline] fn concatenating_mul(&self, rhs: &BoxedUint) -> Self { self.mul(rhs) diff --git a/src/uint/concat.rs b/src/uint/concat.rs index 22d16558a..d16a37cf9 100644 --- a/src/uint/concat.rs +++ b/src/uint/concat.rs @@ -1,72 +1,50 @@ -use crate::{Concat, Uint}; - -const fn concat( - lo: &Uint, - hi: &Uint, -) -> Uint { - const { - assert!( - L + H == O, - concat![ - "The size of the declared type of `Uint` concatenation is not equal to ", - "the sum of the sizes of argument types. ", - ] - ); - } - - let mut result = Uint::::ZERO; - - let mut i = 0; - while i < L { - result.limbs[i] = lo.limbs[i]; - i += 1; - } - while i < L + H { - result.limbs[i] = hi.limbs[i - L]; - i += 1; - } - - result -} - -impl Concat, Uint> for Uint { - /// Concatenate the two values, with `self` as least significant and `hi` as the most - /// significant. - /// - ///
- /// The sum of input lengths must be equal to the output length. - ///
- fn concat(&self, hi: &Uint) -> Uint { - self.concat_mixed(hi) - } -} +use crate::{Concat, ConcatMixed, Limb, Uint}; impl Uint { /// Concatenate the two values, with `self` as least significant and `hi` as the most /// significant. - /// - ///
- /// The sum of input lengths must be equal to the output length. - ///
- pub const fn concat(&self, hi: &Self) -> Uint { - concat(self, hi) + pub const fn concat(&self, hi: &Self) -> Uint + where + Self: Concat>, + { + Uint::concat_mixed(self, hi) } - /// Concatenate the two values, with `self` as least significant and `hi` + /// Concatenate the two values, with `lo` as least significant and `hi` /// as the most significant. - /// - ///
- /// The sum of input lengths must be equal to the output length. - ///
#[inline] - pub const fn concat_mixed(&self, hi: &Uint) -> Uint { - concat(self, hi) + pub const fn concat_mixed(lo: &Uint, hi: &Uint) -> Uint + where + Self: ConcatMixed, MixedOutput = Uint>, + { + let top = L + H; + let top = if top < O { top } else { O }; + let mut limbs = [Limb::ZERO; O]; + let mut i = 0; + + while i < top { + if i < L { + limbs[i] = lo.limbs[i]; + } else { + limbs[i] = hi.limbs[i - L]; + } + i += 1; + } + + Uint { limbs } } } +impl Concat for T +where + T: ConcatMixed, +{ + type Output = Self::MixedOutput; +} + #[cfg(test)] mod tests { - use crate::{U64, U128, U192}; + use crate::{ConcatMixed, U64, U128, U192}; #[test] fn concat() { diff --git a/src/uint/div.rs b/src/uint/div.rs index b8d34104b..aa674520a 100644 --- a/src/uint/div.rs +++ b/src/uint/div.rs @@ -668,8 +668,9 @@ impl RemLimb for Uint { #[cfg(test)] mod tests { - use crate::{DivVartime, Limb, NonZero, U64, U128, U256, U512, U896, U1024, Uint, Word, Zero}; - use core::ops::Rem; + use crate::{ + DivVartime, Limb, NonZero, RemMixed, U64, U128, U256, U512, U896, U1024, Uint, Word, Zero, + }; #[cfg(feature = "rand")] use {crate::Random, chacha20::ChaCha8Rng, rand_core::RngCore, rand_core::SeedableRng}; @@ -872,16 +873,10 @@ mod tests { ); let rem = U256::rem_wide(lo_hi, &modulus); // Lower half is zero - assert_eq!( - &rem.to_be_bytes().as_ref()[0..16], - U128::ZERO.to_be_bytes().as_ref() - ); + assert_eq!(rem.to_be_bytes()[0..16], U128::ZERO.to_be_bytes()); // Upper half let expected = U128::from_be_hex("203F80FE03F80FE03F80FE03F80FE041"); - assert_eq!( - &rem.to_be_bytes().as_ref()[16..], - expected.to_be_bytes().as_ref() - ); + assert_eq!(rem.to_be_bytes()[16..], expected.to_be_bytes()); let remv = U256::rem_wide_vartime(lo_hi, &modulus); assert_eq!(rem, remv); @@ -937,9 +932,9 @@ mod tests { "7A831C1B06D31D3618D218D6E667DBD85BFC7B6B6B93422D52516989376AA29A", ]); let y = U128::from_u64(1234567890987654321); - let rem = x.rem(&y.to_nz().unwrap()); + let rem = x.rem_mixed(&y.to_nz().unwrap()); - let y2: U1024 = y.concat_mixed(&U896::ZERO); + let y2: U1024 = U128::concat_mixed(&y, &U896::ZERO); let rem_control = x.rem(&NonZero::new(y2).unwrap()); assert_eq!(rem.bits(), rem_control.bits()); @@ -960,9 +955,9 @@ mod tests { "7A831C1B06D31D3618D218D6E667DBD85BFC7B6B6B93422D52516989376AA29A", ]); let y = U512::from_u64(1234567890987654321); - let rem: U512 = x.rem(&y.to_nz().unwrap()); + let rem: U512 = x.rem_mixed(&y.to_nz().unwrap()); - let y_wide = y.concat_mixed(&U512::ZERO); + let y_wide = U512::concat_mixed(&y, &U512::ZERO); let rem_control: U1024 = x.rem(&NonZero::new(y_wide).unwrap()); assert_eq!(rem.bits(), rem_control.bits()); @@ -974,6 +969,30 @@ mod tests { ); } + #[test] + fn rem_mixed_through_traits() { + struct A { + t: T, + u: U, + } + impl A + where + T: RemMixed, + U: Clone + Zero, + { + fn reduce_t_by_u(&self) -> U { + let rhs = &NonZero::new(self.u.clone()).unwrap(); + self.t.rem_mixed(rhs) + } + } + + let a = A { + t: U1024::from(1234567890u64), + u: U128::from(456u64), + }; + assert_eq!(a.reduce_t_by_u(), U128::from(330u64)); + } + #[test] fn div_vartime_through_traits() { struct A { diff --git a/src/uint/encoding.rs b/src/uint/encoding.rs index 0761ec586..478d257b7 100644 --- a/src/uint/encoding.rs +++ b/src/uint/encoding.rs @@ -6,17 +6,18 @@ mod der; #[cfg(feature = "rlp")] mod rlp; -use core::{fmt, ops::Deref}; - #[cfg(feature = "alloc")] use alloc::{string::String, vec::Vec}; use super::Uint; -use crate::{DecodeError, Encoding, Limb, Word}; +use crate::{DecodeError, Limb, Word}; #[cfg(feature = "alloc")] use crate::{ConstChoice, NonZero, Reciprocal, UintRef, WideWord}; +#[cfg(feature = "hybrid-array")] +use crate::Encoding; + #[cfg(feature = "alloc")] const RADIX_ENCODING_LIMBS_LARGE: usize = 16; #[cfg(feature = "alloc")] @@ -203,158 +204,58 @@ impl Uint { let mut buf = *self; radix_encode_limbs_mut_to_string(radix, buf.as_mut_uint_ref()) } - - /// Serialize as big endian bytes. - pub const fn to_be_bytes(&self) -> EncodedUint { - EncodedUint::new_be(self) - } - - /// Serialize as little endian bytes. - pub const fn to_le_bytes(&self) -> EncodedUint { - EncodedUint::new_le(self) - } -} - -/// [`Uint`] encoded as bytes. -// Until const generic expressions are stable, we cannot statically declare a `u8` array -// of the size `LIMBS * Limb::BYTES`. -// So instead we use the array of words, and treat it as an array of bytes. -// It's a little hacky, but it works, because the array is guaranteed to be contiguous. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct EncodedUint([Word; LIMBS]); - -#[allow(unsafe_code)] -const fn cast_slice(limbs: &[Word]) -> &[u8] { - let new_len = size_of_val(limbs); - unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) } } -#[allow(unsafe_code)] -const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] { - let new_len = size_of_val(limbs); - unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr() as *mut u8, new_len) } -} - -impl EncodedUint { - const fn new_le(value: &Uint) -> Self { - let mut buffer = [0; LIMBS]; - let mut i = 0; - - while i < LIMBS { - let src_bytes = &value.limbs[i].0.to_le_bytes(); - - // We could cast the whole `buffer` to bytes at once, - // but IndexMut does not work in const context. - let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i])); - - // `copy_from_slice` can be used here when MSRV moves past 1.87 - let mut j = 0; - while j < Limb::BYTES { - dst_bytes[j] = src_bytes[j]; - j += 1; - } - - i += 1; - } - Self(buffer) +/// Encode a [`Uint`] to a big endian byte array of the given size. +pub(crate) const fn uint_to_be_bytes( + uint: &Uint, +) -> [u8; BYTES] { + if BYTES != LIMBS * Limb::BYTES { + panic!("BYTES != LIMBS * Limb::BYTES"); } - const fn new_be(value: &Uint) -> Self { - let mut buffer = [0; LIMBS]; - let mut i = 0; - while i < LIMBS { - let src_bytes = &value.limbs[i].0.to_be_bytes(); + let mut ret = [0u8; BYTES]; + let mut i = 0; - // We could cast the whole `buffer` to bytes at once, - // but IndexMut does not work in const context. - let dst_bytes: &mut [u8] = - cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i])); - - // `copy_from_slice` can be used here when MSRV moves past 1.87 - let mut j = 0; - while j < Limb::BYTES { - dst_bytes[j] = src_bytes[j]; - j += 1; - } + while i < LIMBS { + let limb_bytes = uint.limbs[LIMBS - i - 1].0.to_be_bytes(); + let mut j = 0; - i += 1; + while j < Limb::BYTES { + ret[i * Limb::BYTES + j] = limb_bytes[j]; + j += 1; } - Self(buffer) - } -} -impl Default for EncodedUint { - fn default() -> Self { - Self([0; LIMBS]) + i += 1; } -} -impl AsRef<[u8]> for EncodedUint { - fn as_ref(&self) -> &[u8] { - cast_slice(&self.0) - } + ret } -impl AsMut<[u8]> for EncodedUint { - fn as_mut(&mut self) -> &mut [u8] { - cast_slice_mut(&mut self.0) +/// Encode a [`Uint`] to a little endian byte array of the given size. +pub(crate) const fn uint_to_le_bytes( + uint: &Uint, +) -> [u8; BYTES] { + if BYTES != LIMBS * Limb::BYTES { + panic!("BYTES != LIMBS * Limb::BYTES"); } -} - -impl Deref for EncodedUint { - type Target = [u8]; - fn deref(&self) -> &Self::Target { - self.as_ref() - } -} - -/// Returned if an object cannot be instantiated from the given byte slice. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct TryFromSliceError; - -impl fmt::Display for TryFromSliceError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "TryFromSliceError") - } -} -impl core::error::Error for TryFromSliceError {} + let mut ret = [0u8; BYTES]; + let mut i = 0; -impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint { - type Error = TryFromSliceError; + while i < LIMBS { + let limb_bytes = uint.limbs[i].0.to_le_bytes(); + let mut j = 0; - fn try_from(bytes: &'a [u8]) -> Result { - if bytes.len() != Uint::::BYTES { - return Err(TryFromSliceError); + while j < Limb::BYTES { + ret[i * Limb::BYTES + j] = limb_bytes[j]; + j += 1; } - let mut result = Self::default(); - result.as_mut().copy_from_slice(bytes); - Ok(result) - } -} - -impl Encoding for Uint { - type Repr = EncodedUint; - - #[inline] - fn from_be_bytes(bytes: Self::Repr) -> Self { - Self::from_be_slice(bytes.as_ref()) - } - #[inline] - fn from_le_bytes(bytes: Self::Repr) -> Self { - Self::from_le_slice(bytes.as_ref()) + i += 1; } - #[inline] - fn to_be_bytes(&self) -> Self::Repr { - self.to_be_bytes() - } - - #[inline] - fn to_le_bytes(&self) -> Self::Repr { - self.to_le_bytes() - } + ret } /// Decode a single nibble of upper or lower hex @@ -1156,7 +1057,7 @@ mod tests { let n = UintEx::from_be_hex("0011223344556677"); let bytes = n.to_be_bytes(); - assert_eq!(bytes.as_ref(), hex!("0011223344556677")); + assert_eq!(bytes, hex!("0011223344556677")); #[cfg(feature = "der")] assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7); @@ -1168,7 +1069,7 @@ mod tests { let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff"); let bytes = n.to_be_bytes(); - assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff")); + assert_eq!(bytes, hex!("00112233445566778899aabbccddeeff")); #[cfg(feature = "der")] assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15); diff --git a/src/uint/extra_sizes.rs b/src/uint/extra_sizes.rs index a0b57ad99..fb639c713 100644 --- a/src/uint/extra_sizes.rs +++ b/src/uint/extra_sizes.rs @@ -110,3 +110,51 @@ impl_uint_aliases! { (U8064, 8064, "8064-bit"), (U8128, 8128, "8128-bit") } + +impl_uint_concat_split_even! { + U1152, + U1408, + U1664, + U1920, + U2176, + U2304, + U2432, + U2560, + U2688, + U2816, + U2944, + U3200, + U3328, + U3456, + U3712, + U3840, + U3968, + U4480, + U4608, + U4736, + U4864, + U4992, + U5120, + U5248, + U5376, + U5504, + U5632, + U5760, + U5888, + U6016, + U6272, + U6400, + U6528, + U6656, + U6784, + U6912, + U7040, + U7168, + U7296, + U7424, + U7552, + U7680, + U7808, + U7936, + U8064, +} diff --git a/src/uint/from.rs b/src/uint/from.rs index 7ebe2c703..beabee201 100644 --- a/src/uint/from.rs +++ b/src/uint/from.rs @@ -1,6 +1,6 @@ //! `From`-like conversions for [`Uint`]. -use crate::{Limb, U64, U128, Uint, WideWord, Word}; +use crate::{ConcatMixed, Limb, SplitMixed, U64, U128, Uint, WideWord, Word}; impl Uint { /// Create a [`Uint`] from a `u8` (const-friendly) @@ -196,19 +196,28 @@ impl From for Uint { } } -impl From<(Uint, Uint)> for Uint { +impl From<(Uint, Uint)> for Uint +where + Uint: ConcatMixed, MixedOutput = Uint>, +{ fn from(nums: (Uint, Uint)) -> Uint { nums.0.concat_mixed(&nums.1) } } -impl From<&(Uint, Uint)> for Uint { +impl From<&(Uint, Uint)> for Uint +where + Uint: ConcatMixed, MixedOutput = Uint>, +{ fn from(nums: &(Uint, Uint)) -> Uint { nums.0.concat_mixed(&nums.1) } } -impl From> for (Uint, Uint) { +impl From> for (Uint, Uint) +where + Uint: SplitMixed, Uint>, +{ fn from(num: Uint) -> (Uint, Uint) { num.split_mixed() } diff --git a/src/uint/gcd.rs b/src/uint/gcd.rs index db62565ec..2fe1934e0 100644 --- a/src/uint/gcd.rs +++ b/src/uint/gcd.rs @@ -428,10 +428,15 @@ mod tests { } mod xgcd { - use crate::{Gcd, Int, U64, U128, U256, U512, U1024, U2048, U4096, U8192, U16384, Uint}; + use crate::{ + Concat, Gcd, Int, U64, U128, U256, U512, U1024, U2048, U4096, U8192, U16384, Uint, + }; use core::ops::Div; - fn test(lhs: Uint, rhs: Uint) { + fn test(lhs: Uint, rhs: Uint) + where + Uint: Concat>, + { let output = lhs.xgcd(&rhs); assert_eq!(output.gcd, lhs.gcd(&rhs)); @@ -442,30 +447,32 @@ mod tests { let (x, y) = output.bezout_coefficients(); assert_eq!( - x.concatenating_mul_uint::(&lhs) - + y.concatenating_mul_uint::(&rhs), + x.concatenating_mul_uint(&lhs) + y.concatenating_mul_uint(&rhs), *output.gcd.resize().as_int() ); } - fn run_tests() { + fn run_tests() + where + Uint: Concat>, + { let min = Int::MIN.abs(); - test::(Uint::ZERO, Uint::ZERO); - test::(Uint::ZERO, Uint::ONE); - test::(Uint::ZERO, min); - test::(Uint::ZERO, Uint::MAX); - test::(Uint::ONE, Uint::ZERO); - test::(Uint::ONE, Uint::ONE); - test::(Uint::ONE, min); - test::(Uint::ONE, Uint::MAX); - test::(min, Uint::ZERO); - test::(min, Uint::ONE); - test::(min, Int::MIN.abs()); - test::(min, Uint::MAX); - test::(Uint::MAX, Uint::ZERO); - test::(Uint::MAX, Uint::ONE); - test::(Uint::MAX, min); - test::(Uint::MAX, Uint::MAX); + test(Uint::ZERO, Uint::ZERO); + test(Uint::ZERO, Uint::ONE); + test(Uint::ZERO, min); + test(Uint::ZERO, Uint::MAX); + test(Uint::ONE, Uint::ZERO); + test(Uint::ONE, Uint::ONE); + test(Uint::ONE, min); + test(Uint::ONE, Uint::MAX); + test(min, Uint::ZERO); + test(min, Uint::ONE); + test(min, Int::MIN.abs()); + test(min, Uint::MAX); + test(Uint::MAX, Uint::ZERO); + test(Uint::MAX, Uint::ONE); + test(Uint::MAX, min); + test(Uint::MAX, Uint::MAX); } #[test] @@ -489,7 +496,7 @@ mod tests { let b = U256::from_be_hex( "000000000000345EAEDFA8CA03C1F0F5B578A787FE2D23B82A807F178B37FD8E", ); - test::<{ nlimbs!(256) }, { nlimbs!(512) }>(a, b); + test(a, b); // Sent in by @kayabaNerve (https://github.com/RustCrypto/crypto-bigint/pull/761#issuecomment-2771581512) let a = U256::from_be_hex( @@ -498,7 +505,7 @@ mod tests { let b = U256::from_be_hex( "000000000000072B69C9DD0AA15F135675EA9C5180CF8FF0A59298CFC92E87FA", ); - test::<{ nlimbs!(256) }, { nlimbs!(512) }>(a, b); + test(a, b); // Sent in by @kayabaNerve (https://github.com/RustCrypto/crypto-bigint/pull/761#issuecomment-2782912608) let a = U512::from_be_hex(concat![ @@ -509,7 +516,7 @@ mod tests { "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD755DB9CD5E9140777FA4BD19A06C8283", "9D671CD581C69BC5E697F5E45BCD07C52EC373A8BDC598B4493F50A1380E1281" ]); - test::<{ nlimbs!(512) }, { nlimbs!(1024) }>(a, b); + test(a, b); } } diff --git a/src/uint/macros.rs b/src/uint/macros.rs index da84fa7bb..2682fc579 100644 --- a/src/uint/macros.rs +++ b/src/uint/macros.rs @@ -7,6 +7,116 @@ macro_rules! impl_uint_aliases { #[doc = $doc] #[doc="unsigned big integer."] pub type $name = Uint<{ nlimbs!($bits) }>; + + impl $name { + /// Serialize as big endian bytes. + pub const fn to_be_bytes(&self) -> [u8; $bits / 8] { + encoding::uint_to_be_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self) + } + + /// Serialize as little endian bytes. + pub const fn to_le_bytes(&self) -> [u8; $bits / 8] { + encoding::uint_to_le_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self) + } + } + + impl Encoding for $name { + type Repr = [u8; $bits / 8]; + + #[inline] + fn from_be_bytes(bytes: Self::Repr) -> Self { + Self::from_be_slice(&bytes) + } + + #[inline] + fn from_le_bytes(bytes: Self::Repr) -> Self { + Self::from_le_slice(&bytes) + } + + #[inline] + fn to_be_bytes(&self) -> Self::Repr { + encoding::uint_to_be_bytes(self) + } + + #[inline] + fn to_le_bytes(&self) -> Self::Repr { + encoding::uint_to_le_bytes(self) + } + } )+ }; } + +macro_rules! impl_uint_concat_split_mixed { + ($name:ident, $size:literal) => { + impl $crate::traits::ConcatMixed> for Uint<{ <$name>::LIMBS - U64::LIMBS * $size }> + { + type MixedOutput = $name; + + fn concat_mixed(&self, hi: &Uint<{ U64::LIMBS * $size }>) -> Self::MixedOutput { + Uint::concat_mixed(self, hi) + } + } + + impl $crate::traits::SplitMixed, Uint<{ <$name>::LIMBS - U64::LIMBS * $size }>> for $name + { + fn split_mixed(&self) -> (Uint<{ U64::LIMBS * $size }>, Uint<{ <$name>::LIMBS - U64::LIMBS * $size }>) { + self.split_mixed() + } + } + + impl $crate::traits::RemMixed> for $name + { + fn rem_mixed(&self, reductor: &NonZero>) -> Uint<{ U64::LIMBS * $size }> { + self.div_rem_vartime(reductor).1 + } + } + }; + ($name:ident, [ $($size:literal),+ ]) => { + $( + impl_uint_concat_split_mixed!($name, $size); + )+ + }; + ($( ($name:ident, $sizes:tt), )+) => { + $( + impl_uint_concat_split_mixed!($name, $sizes); + )+ + }; +} + +macro_rules! impl_uint_concat_split_even { + ($name:ident) => { + impl $crate::traits::ConcatMixed::LIMBS / 2 }>> for Uint<{ <$name>::LIMBS / 2 }> + { + type MixedOutput = $name; + + fn concat_mixed(&self, hi: &Uint<{ <$name>::LIMBS / 2 }>) -> Self::MixedOutput { + Uint::concat_mixed(self, hi) + } + } + + impl $crate::traits::SplitMixed::LIMBS / 2 }>, Uint<{ <$name>::LIMBS / 2 }>> for $name + { + fn split_mixed(&self) -> (Uint<{ <$name>::LIMBS / 2 }>, Uint<{ <$name>::LIMBS / 2 }>) { + self.split_mixed() + } + } + + impl $crate::traits::RemMixed::LIMBS / 2 }>> for $name + { + fn rem_mixed(&self, reductor: &NonZero::LIMBS / 2 }>>) -> Uint<{ <$name>::LIMBS / 2 }> { + self.div_rem_vartime(reductor).1 + } + } + + impl $crate::traits::Split for $name + { + type Output = Uint<{ <$name>::LIMBS / 2 }>; + } + }; + ($($name:ident,)+) => { + $( + impl_uint_concat_split_even!($name); + )+ + } +} diff --git a/src/uint/mul.rs b/src/uint/mul.rs index 9d5aca980..ec732015c 100644 --- a/src/uint/mul.rs +++ b/src/uint/mul.rs @@ -5,8 +5,8 @@ use core::ops::{Mul, MulAssign}; use subtle::CtOption; use crate::{ - Checked, CheckedMul, ConcatenatingMul, ConstChoice, ConstCtOption, Limb, Uint, UintRef, - Wrapping, WrappingMul, + Checked, CheckedMul, Concat, ConcatMixed, ConcatenatingMul, ConstChoice, ConstCtOption, Limb, + Uint, UintRef, Wrapping, WrappingMul, }; pub(crate) mod karatsuba; @@ -17,9 +17,12 @@ impl Uint { pub const fn concatenating_mul( &self, rhs: &Uint, - ) -> Uint { + ) -> Uint + where + Self: ConcatMixed, MixedOutput = Uint>, + { let (lo, hi) = self.widening_mul(rhs); - lo.concat_mixed(&hi) + Uint::concat_mixed(&lo, &hi) } /// Compute "wide" multiplication as a 2-tuple containing the `(lo, hi)` components of the product, whose sizes @@ -73,9 +76,12 @@ impl Uint { } /// Square self, returning a concatenated "wide" result. - pub const fn widening_square(&self) -> Uint { + pub const fn widening_square(&self) -> Uint + where + Self: ConcatMixed, MixedOutput = Uint>, + { let (lo, hi) = self.square_wide(); - lo.concat_mixed(&hi) + Uint::concat_mixed(&lo, &hi) } /// Square self, checking that the result fits in the original [`Uint`] size. @@ -97,9 +103,12 @@ impl Uint { } } -impl Uint { +impl Uint +where + Self: Concat>, +{ /// Square self, returning a concatenated "wide" result. - pub const fn square(&self) -> Uint { + pub const fn square(&self) -> Uint { let (lo, hi) = self.square_wide(); lo.concat(&hi) } @@ -181,19 +190,27 @@ impl MulAssign<&Checked>> for Checked - ConcatenatingMul, Uint> for Uint + ConcatenatingMul> for Uint +where + Self: ConcatMixed, MixedOutput = Uint>, { + type Output = >>::MixedOutput; + #[inline] - fn concatenating_mul(&self, rhs: Uint) -> Uint { + fn concatenating_mul(&self, rhs: Uint) -> Self::Output { self.concatenating_mul(&rhs) } } impl - ConcatenatingMul, &Uint> for Uint + ConcatenatingMul<&Uint> for Uint +where + Self: ConcatMixed, MixedOutput = Uint>, { + type Output = >>::MixedOutput; + #[inline] - fn concatenating_mul(&self, rhs: &Uint) -> Uint { + fn concatenating_mul(&self, rhs: &Uint) -> Self::Output { self.concatenating_mul(rhs) } } @@ -332,7 +349,7 @@ mod tests { #[test] fn square() { let n = U64::from_u64(0xffff_ffff_ffff_ffff); - let (lo, hi) = n.square::<{ nlimbs!(128) }>().split(); + let (lo, hi) = n.square().split(); assert_eq!(lo, U64::from_u64(1)); assert_eq!(hi, U64::from_u64(0xffff_ffff_ffff_fffe)); } @@ -340,7 +357,7 @@ mod tests { #[test] fn square_larger() { let n = U256::MAX; - let (lo, hi) = n.square::<{ nlimbs!(512) }>().split(); + let (lo, hi) = n.square().split(); assert_eq!(lo, U256::ONE); assert_eq!(hi, U256::MAX.wrapping_sub(&U256::ONE)); } diff --git a/src/uint/mul_int.rs b/src/uint/mul_int.rs index c20b92078..ab7228103 100644 --- a/src/uint/mul_int.rs +++ b/src/uint/mul_int.rs @@ -1,4 +1,4 @@ -use crate::{ConstChoice, ConstCtOption, Int, Uint}; +use crate::{ConcatMixed, ConstChoice, ConstCtOption, Int, Uint}; impl Uint { /// Compute "wide" multiplication between an [`Uint`] and [`Int`] as 3-tuple `(lo, hi, negate)`. @@ -20,7 +20,10 @@ impl Uint { pub const fn concatenating_mul_int( &self, rhs: &Int, - ) -> Int { + ) -> Int + where + Uint: ConcatMixed, MixedOutput = Uint>, + { let (rhs_abs, rhs_sign) = rhs.abs_sign(); let product_abs = self.concatenating_mul(&rhs_abs); diff --git a/src/uint/split.rs b/src/uint/split.rs index d6865a732..a9aa638ed 100644 --- a/src/uint/split.rs +++ b/src/uint/split.rs @@ -1,62 +1,36 @@ -use crate::{Split, Uint}; - -const fn split( - value: &Uint, -) -> (Uint, Uint) { - const { - assert!( - L + H == I, - concat![ - "The sum of the sizes of the declared types of `Uint` split is not equal to ", - "the size of the input type. ", - ] - ); - } - - let mut lo = Uint::::ZERO; - let mut hi = Uint::::ZERO; - - let mut i = 0; - while i < L { - lo.limbs[i] = value.limbs[i]; - i += 1; - } - while i < L + H { - hi.limbs[i - L] = value.limbs[i]; - i += 1; - } - - (lo, hi) -} - -impl Split, Uint> for Uint { - /// Split this number into low and high components respectively. - /// - ///
- /// The sum of output lengths must be equal to the input length. - ///
- fn split(&self) -> (Uint, Uint) { - self.split_mixed() - } -} +use crate::{Limb, Split, SplitMixed, Uint}; impl Uint { /// Split this number in half into low and high components. - /// - ///
- /// The sum of output lengths must be equal to the input length. - ///
- pub const fn split(&self) -> (Uint, Uint) { - split::(self) + pub const fn split(&self) -> (Uint, Uint) + where + Self: Split>, + { + self.split_mixed() } /// Split this number into low and high components respectively. - /// - ///
- /// The sum of output lengths must be equal to the input length. - ///
- pub const fn split_mixed(&self) -> (Uint, Uint) { - split::(self) + #[inline] + pub const fn split_mixed(&self) -> (Uint, Uint) + where + Self: SplitMixed, Uint>, + { + let top = L + H; + let top = if top < I { top } else { I }; + let mut lo = [Limb::ZERO; L]; + let mut hi = [Limb::ZERO; H]; + let mut i = 0; + + while i < top { + if i < L { + lo[i] = self.limbs[i]; + } else { + hi[i - L] = self.limbs[i]; + } + i += 1; + } + + (Uint { limbs: lo }, Uint { limbs: hi }) } } diff --git a/tests/monty_form.rs b/tests/monty_form.rs index 4244f51cd..1c27ec601 100644 --- a/tests/monty_form.rs +++ b/tests/monty_form.rs @@ -4,8 +4,8 @@ mod common; use common::to_biguint; use crypto_bigint::{ - Bounded, Constants, EncodedUint, Encoding, Integer, Invert, Monty, NonZero, Odd, U128, U256, - U512, U1024, U2048, U4096, Unsigned, + Bounded, Constants, Encoding, Integer, Invert, Monty, NonZero, Odd, U128, U256, U512, U1024, + U2048, U4096, Unsigned, modular::{MontyForm, MontyParams}, }; use num_bigint::BigUint; @@ -99,7 +99,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U128, ::Monty , ::Monty, BigUint),TestCaseError> { - random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) + random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) } } prop_compose! { @@ -109,7 +109,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U256, ::Monty , ::Monty, BigUint),TestCaseError> { - random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) + random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) } } prop_compose! { @@ -119,7 +119,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U2048, ::Monty , ::Monty, BigUint),TestCaseError> { - random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) + random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) } } prop_compose! { @@ -129,7 +129,7 @@ prop_compose! { monty_params_from_edge::(edge_bytes, &mut rng) }) ) -> Result<(U1024, ::Monty, ::Monty, BigUint),TestCaseError> { - random_invertible_uint(EncodedUint::try_from(bytes.as_ref()).unwrap(), monty_params, monty_params.modulus().get()) + random_invertible_uint(bytes, monty_params, monty_params.modulus().get()) } } proptest! { @@ -151,7 +151,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U128::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one: U256 = r_monty_inv.retrieve().concatenating_mul(&r); + let one = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U256::ONE, @@ -166,7 +166,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) @@ -190,7 +190,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U256::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one: U512 = r_monty_inv.retrieve().concatenating_mul(&r); + let one = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U512::ONE, @@ -205,7 +205,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) @@ -229,7 +229,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one: U2048 = r_monty_inv.retrieve().concatenating_mul(&r); + let one = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U2048::ONE, @@ -244,7 +244,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) @@ -268,7 +268,7 @@ proptest! { assert_eq!(one_monty.retrieve(), U2048::ONE, "a*a⁻¹ ≠ 1 (normal form)"); // …and when converted back to normal form and used in a widening operation let wide_modulus = NonZero::new(Into::::into(&monty_params.modulus().get())).unwrap(); - let one: U4096 = r_monty_inv.retrieve().concatenating_mul(&r); + let one = r_monty_inv.retrieve().concatenating_mul(&r); assert_eq!( one % wide_modulus, U4096::ONE, @@ -283,7 +283,7 @@ proptest! { ); // …and agrees with the num_modular crate assert_eq!( - BigUint::from_be_bytes(normal_form_inv.to_be_bytes().as_ref()), + BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()), r_num_modular_inv, "num_modular ≠ crypto_bigint" ) diff --git a/tests/uint.rs b/tests/uint.rs index 78983e498..1a71131a1 100644 --- a/tests/uint.rs +++ b/tests/uint.rs @@ -531,11 +531,11 @@ proptest! { #[test] fn encoding_reverse(a in uint()) { let mut bytes = a.to_be_bytes(); - bytes.as_mut().reverse(); + bytes.reverse(); prop_assert_eq!(a, U256::from_le_bytes(bytes)); let mut bytes = a.to_le_bytes(); - bytes.as_mut().reverse(); + bytes.reverse(); prop_assert_eq!(a, U256::from_be_bytes(bytes)); }