From 3bfb55ece2f70fc9b1969a2ec9c55f7a00358b90 Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Tue, 11 Oct 2022 23:31:21 +0200 Subject: Refactor base_conversion. Fix hmac byte bug. Moved the basis conversion into a submodule, to ease the upcoming rewrite. Add a couple of new integration tests. Fix a bug caused by misreading the PasswordMaker Pro HMAC code. --- src/passwordmaker/base_conversion/mod.rs | 65 +++++++++++ src/passwordmaker/base_conversion/remainders.rs | 74 ++++++++++++ .../base_conversion/remainders_impl.rs | 76 ++++++++++++ src/passwordmaker/mod.rs | 129 +++++++-------------- src/passwordmaker/remainders.rs | 74 ------------ src/passwordmaker/remainders_impl.rs | 76 ------------ 6 files changed, 260 insertions(+), 234 deletions(-) create mode 100644 src/passwordmaker/base_conversion/mod.rs create mode 100644 src/passwordmaker/base_conversion/remainders.rs create mode 100644 src/passwordmaker/base_conversion/remainders_impl.rs delete mode 100644 src/passwordmaker/remainders.rs delete mode 100644 src/passwordmaker/remainders_impl.rs (limited to 'src/passwordmaker') diff --git a/src/passwordmaker/base_conversion/mod.rs b/src/passwordmaker/base_conversion/mod.rs new file mode 100644 index 0000000..705917a --- /dev/null +++ b/src/passwordmaker/base_conversion/mod.rs @@ -0,0 +1,65 @@ +use std::convert::TryInto; + +use self::remainders::CalcRemainders; + +mod remainders; +mod remainders_impl; + +/// Converts an input to a different base (which fits in usize). Returns the digits starting at the most significant one. +pub(super) trait BaseConversion { + // return type is subject to change. Hopefully soon the math will be rewritten, so we can skip the Vec and IntoIter. + // will have to remain an ExactSizeIterator though. + fn convert_to_base(self, base : usize) -> std::iter::Rev>; +} + +impl BaseConversion for T where T : ToI32Array{ + fn convert_to_base(self, base : usize) -> std::iter::Rev> { + self.to_int_array().calc_remainders(base).collect::>().into_iter().rev() + } +} + +impl BaseConversion for [u8;16]{ + fn convert_to_base(self, base : usize) -> std::iter::Rev> { + u128::from_be_bytes(self).calc_remainders(base as u128).map(|ll| ll as usize).collect::>().into_iter().rev() + } +} + + + +// Rust 1.52 only has a very limited support for const generics. This means, we'll have to live with this not-too-constrained solution... +// Well, it's private, so no big loss. +trait ToI32Array { + type Output; + fn to_int_array(self) -> Self::Output; +} + +//this could of course be done in a generic manner, but it's ugly without array_mut, which we don't have in Rust 1.52. +//Soo, pedestrian's approach :D +impl ToI32Array for [u8;20] { + type Output = [u32; 5]; + fn to_int_array(self) -> [u32; 5] { + [ + u32::from_be_bytes(self[0..4].try_into().unwrap()), + u32::from_be_bytes(self[4..8].try_into().unwrap()), + u32::from_be_bytes(self[8..12].try_into().unwrap()), + u32::from_be_bytes(self[12..16].try_into().unwrap()), + u32::from_be_bytes(self[16..20].try_into().unwrap()), + ] + } +} + +impl ToI32Array for [u8;32] { + type Output = [u32; 8]; + fn to_int_array(self) -> [u32; 8] { + [ + u32::from_be_bytes(self[0..4].try_into().unwrap()), + u32::from_be_bytes(self[4..8].try_into().unwrap()), + u32::from_be_bytes(self[8..12].try_into().unwrap()), + u32::from_be_bytes(self[12..16].try_into().unwrap()), + u32::from_be_bytes(self[16..20].try_into().unwrap()), + u32::from_be_bytes(self[20..24].try_into().unwrap()), + u32::from_be_bytes(self[24..28].try_into().unwrap()), + u32::from_be_bytes(self[28..32].try_into().unwrap()), + ] + } +} \ No newline at end of file diff --git a/src/passwordmaker/base_conversion/remainders.rs b/src/passwordmaker/base_conversion/remainders.rs new file mode 100644 index 0000000..93570a1 --- /dev/null +++ b/src/passwordmaker/base_conversion/remainders.rs @@ -0,0 +1,74 @@ +/// Adds `calc_remainders(divisor)` method to types that have some implementation of the Division trait. +pub(super) trait CalcRemainders{ + fn calc_remainders(self, divisor : D) -> Remainders; +} + +/// Implement `Division` to enable the `calc_remainders()` method for your type. +pub(super) trait Division where Self:Sized { + /// does in-place arbitrary-length division. Returns remainder. + fn divide(self, divisor : &D) -> DivisionResult; + fn is_zero(&self) -> bool; +} + +/// Or mark your type as `UseGenericDivision` to just use `/` and `%` operators for types. Makes only sense for integers. +pub(super) trait UseGenericDivision : Clone + + for <'a> std::ops::Div<&'a Self, Output = Self> + + for <'a> std::ops::Rem<&'a Self, Output = Self> + + Default + + Eq {} + +impl CalcRemainders for T + where T:Division +{ + fn calc_remainders(self, divisor : D) -> Remainders { + Remainders::new(self,divisor) + } +} + +pub(super) struct Remainders{ + value : Option, + divisor : U, +} + +impl> Remainders { + fn new(value : T, divisor : U) -> Self { + let value = if value.is_zero() { None } else { Some(value) }; + Remainders { + value, + divisor, + } + } +} + +impl> Iterator for Remainders{ + type Item=U; + + fn next(&mut self) -> Option { + if let Some(v) = self.value.take() { + let DivisionResult{result, remainder} = v.divide(&self.divisor); + self.value = if result.is_zero() { None } else { Some(result) }; + Some(remainder) + } else { + None + } + } +} + +pub(super) struct DivisionResult, U> { + pub result : T, + pub remainder : U, +} + +impl Division for U + where U: UseGenericDivision +{ + fn divide(self, divisor : &Self) -> DivisionResult { + DivisionResult { + result: self.clone().div(divisor), + remainder: self.rem(divisor) + } + } + fn is_zero(&self) -> bool { + *self == Self::default() + } +} \ No newline at end of file diff --git a/src/passwordmaker/base_conversion/remainders_impl.rs b/src/passwordmaker/base_conversion/remainders_impl.rs new file mode 100644 index 0000000..7de2189 --- /dev/null +++ b/src/passwordmaker/base_conversion/remainders_impl.rs @@ -0,0 +1,76 @@ +use super::remainders::{Division, UseGenericDivision, DivisionResult}; + +impl UseGenericDivision for u128{} //for Md4, Md5 + +impl Division for [u32;N] { + #[allow(clippy::cast_possible_truncation)] + fn divide(mut self, divisor : &usize) -> DivisionResult { + #[cfg(target_pointer_width = "64")] + type UsizeAndFour = u128; + #[cfg(not(target_pointer_width = "64"))] + type UsizeAndFour = u64; + assert!((UsizeAndFour::MAX >> 32) as u128 >= usize::MAX as u128); + + //uses mutation, because why not? self is owned after all :D + let divisor : UsizeAndFour = *divisor as UsizeAndFour; + let remainder = self.iter_mut().fold(0 as UsizeAndFour,|carry, current| { + assert_eq!(carry, carry & (usize::MAX as UsizeAndFour)); //carry has to be lower than divisor, and divisor is usize. + let carry_shifted = carry << 32; + let dividend = (carry_shifted) + (*current as UsizeAndFour); + let ratio = dividend / divisor; + assert_eq!(ratio, ratio & 0xffff_ffff); //this is fine. The first digit after re-adding the carry is alwys zero. + *current = (ratio) as u32; + dividend - (*current as UsizeAndFour) * divisor + }); + assert_eq!(remainder, remainder & (usize::MAX as UsizeAndFour)); + let remainder = remainder as usize; + DivisionResult{ + result: self, + remainder, + } + } + + fn is_zero(&self) -> bool { + self.iter().all(|x| *x == 0) + } +} + +#[cfg(test)] +mod remainders_tests{ + use super::super::remainders::CalcRemainders; + + use super::*; + #[test] + fn test_generic_division(){ + let v = 50u128; + let d = 7u128; + let DivisionResult{result, remainder}=v.divide(&d); + assert_eq!(7, result); + assert_eq!(1, remainder); + } + + #[test] + fn test_remainders() { + //relies on generic division. + let v = 141u128; + let d = 3u128; + let results : Vec = v.calc_remainders(d).collect(); + assert_eq!(results, vec![0u128,2u128,0u128,2u128,1u128]) + } + + #[test] + fn test_array_divide() { + let dividend_int = 0xe7f1ec3a5f35af805407a8a531eefb79u128; + let dividend = [(dividend_int >> 96) as u32, ((dividend_int >> 64) & 0xffffffff) as u32, ((dividend_int >> 32) & 0xffffffff) as u32, (dividend_int & 0xffffffff) as u32]; + #[cfg(target_pointer_width = "64")] + let divisor = 1531534813576487; + #[cfg(not(target_pointer_width = "64"))] + let divisor = 1531534813; + let result_int = dividend_int / (divisor as u128); + let remainder_int = dividend_int % (divisor as u128); + let result = dividend.divide(&divisor); + assert_eq!(result.result, [(result_int >> 96) as u32, ((result_int >> 64) & 0xffffffff) as u32, ((result_int >> 32) & 0xffffffff) as u32, (result_int & 0xffffffff) as u32]); + assert_eq!(remainder_int, result.remainder as u128); + } + +} \ No newline at end of file diff --git a/src/passwordmaker/mod.rs b/src/passwordmaker/mod.rs index ba85623..8fd4f30 100644 --- a/src/passwordmaker/mod.rs +++ b/src/passwordmaker/mod.rs @@ -1,14 +1,13 @@ -use std::convert::identity; -use std::convert::TryInto; +use std::iter::repeat; use unicode_segmentation::UnicodeSegmentation; use leet::LeetReplacementTable; -use remainders::CalcRemainders; use grapheme::Grapheme; +use base_conversion::BaseConversion; + use super::Hasher; -mod remainders; -mod remainders_impl; +mod base_conversion; mod grapheme; mod hmac; pub(crate) mod leet; @@ -94,11 +93,11 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ let message = pre_leet_level.as_ref().map(|l| l.leetify(&message)).unwrap_or(message); let message = yeet_upper_bytes(&message).collect::>(); let hash = H::MD5::hash(&message); - let hash_as_integer = u128::from_be_bytes(hash); - let grapheme_indices : Vec<_> = hash_as_integer.calc_remainders(characters.len() as u128).map(|llll| llll as usize).collect(); + let grapheme_indices = hash.convert_to_base(characters.len()); let grapheme_indices = yoink_additional_graphemes_for_06_if_needed(grapheme_indices); - GetGraphemesIterator { graphemes : characters, inner: grapheme_indices.into_iter().rev()} + GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::V06(grapheme_indices)} } + fn generate_password_part_v06_hmac<'a>( data : &str, @@ -112,10 +111,9 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ let key = yeet_upper_bytes(&key); let data = yeet_upper_bytes(data); let hash = hmac::hmac::(key, data); - let hash_as_integer = u128::from_be_bytes(hash); - let grapheme_indices : Vec<_> = hash_as_integer.calc_remainders(characters.len() as u128).map(|llll| llll as usize).collect(); + let grapheme_indices = hash.convert_to_base(characters.len()); let grapheme_indices = yoink_additional_graphemes_for_06_if_needed(grapheme_indices); - GetGraphemesIterator { graphemes : characters, inner: grapheme_indices.into_iter().rev()} + GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::V06(grapheme_indices)} } fn generate_password_part_modern_hmac<'a>( @@ -128,20 +126,19 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ let key = pre_leet_level.as_ref().map(|l| l.leetify(&key)).unwrap_or(key); let leetified_data = pre_leet_level.as_ref().map(|l| l.leetify(data)); let data = leetified_data.as_deref().unwrap_or(data); - let to_usize = |l : u128| l as usize; - let grapheme_indices : Vec<_> = match algo { + let grapheme_indices = match algo { Algorithm::Md4 => - modern_hmac_to_grapheme_indices::(&key, data, u128::from_be_bytes, characters.len() as u128, to_usize), + modern_hmac_to_grapheme_indices::(&key, data, characters.len()), Algorithm::Md5 => - modern_hmac_to_grapheme_indices::(&key, data, u128::from_be_bytes, characters.len() as u128, to_usize), + modern_hmac_to_grapheme_indices::(&key, data, characters.len()), Algorithm::Sha1 => - modern_hmac_to_grapheme_indices::(&key, data, ToI32Array::to_int_array, characters.len(), identity), + modern_hmac_to_grapheme_indices::(&key, data, characters.len()), Algorithm::Sha256 => - modern_hmac_to_grapheme_indices::(&key, data, ToI32Array::to_int_array, characters.len(), identity), + modern_hmac_to_grapheme_indices::(&key, data, characters.len()), Algorithm::Ripemd160 => - modern_hmac_to_grapheme_indices::(&key, data, ToI32Array::to_int_array, characters.len(), identity), + modern_hmac_to_grapheme_indices::(&key, data, characters.len()), }; - GetGraphemesIterator { graphemes : characters, inner: grapheme_indices.into_iter().rev()} + GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::Modern(grapheme_indices)} } fn generate_password_part_modern<'a>( @@ -153,20 +150,19 @@ impl<'y, H : super::HasherList> super::PasswordMaker<'y, H>{ ) -> GetGraphemesIterator<'a> { let message = message + second_part; let message = pre_leet_level.as_ref().map(|l| l.leetify(&message)).unwrap_or(message); - let to_usize = |l : u128| l as usize; - let grapheme_indices : Vec<_> = match algo { + let grapheme_indices = match algo { Algorithm::Md4 => - modern_message_to_grapheme_indices::(&message,u128::from_be_bytes,characters.len() as u128, to_usize), + modern_message_to_grapheme_indices::(&message, characters.len()), Algorithm::Md5 => - modern_message_to_grapheme_indices::(&message,u128::from_be_bytes,characters.len() as u128, to_usize), + modern_message_to_grapheme_indices::(&message,characters.len()), Algorithm::Sha1 => - modern_message_to_grapheme_indices::(&message,ToI32Array::to_int_array,characters.len(), identity), + modern_message_to_grapheme_indices::(&message,characters.len()), Algorithm::Sha256 => - modern_message_to_grapheme_indices::(&message,ToI32Array::to_int_array,characters.len(), identity), + modern_message_to_grapheme_indices::(&message,characters.len()), Algorithm::Ripemd160 => - modern_message_to_grapheme_indices::(&message,ToI32Array::to_int_array,characters.len(), identity), + modern_message_to_grapheme_indices::(&message,characters.len()), }; - GetGraphemesIterator { graphemes : characters, inner: grapheme_indices.into_iter().rev()} + GetGraphemesIterator { graphemes : characters, inner: GetGraphemesIteratorInner::Modern(grapheme_indices)} } } @@ -199,9 +195,13 @@ fn combine_prefix_password_suffix<'a, T : Iterator>>(password: .collect() } +enum GetGraphemesIteratorInner { + Modern(std::iter::Rev>), + V06(std::iter::Chain>, std::iter::Rev>>) +} struct GetGraphemesIterator<'a> { graphemes : &'a Vec>, - inner : std::iter::Rev>, + inner : GetGraphemesIteratorInner //There really should be a better solution than storing those values. If we had arbitrary-length multiplication and subtraction maybe? //like, finding the highest potence of divisor that still is smaller than the dividend, and dividing by that one to get the left-most digit, //dividing the remainder of this operation by the next-lower potence of divisor to get the second digit, and so on? @@ -211,32 +211,26 @@ impl<'a> Iterator for GetGraphemesIterator<'a> { type Item = Grapheme<'a>; fn next(&mut self) -> Option { - self.inner.next().and_then(|i| self.graphemes.get(i)).cloned() + let idx = match &mut self.inner { + GetGraphemesIteratorInner::Modern(i) => i.next(), + GetGraphemesIteratorInner::V06(i) => i.next(), + }; + idx.and_then(|idx| self.graphemes.get(idx).cloned()) } } -fn modern_hmac_to_grapheme_indices(key : &str, data: &str, to_dividend : F, divisor : D, to_usize : U) -> Vec +fn modern_hmac_to_grapheme_indices(key : &str, data: &str, divisor : usize) -> std::iter::Rev> where T:Hasher, - C: CalcRemainders, - F: Fn(T::Output) -> C, - Z:remainders::Division, - U: Fn(D) -> usize, - ::Output: AsRef<[u8]> + ::Output: BaseConversion + AsRef<[u8]> { - let key = yeet_upper_bytes(key); - let data = yeet_upper_bytes(data); - to_dividend(hmac::hmac::(key, data)).calc_remainders(divisor).map(to_usize).collect() + hmac::hmac::(key.bytes(), data.bytes()).convert_to_base(divisor) } -fn modern_message_to_grapheme_indices(data: &str, to_dividend : F, divisor : D, to_usize : U) -> Vec +fn modern_message_to_grapheme_indices(data: &str, divisor : usize) -> std::iter::Rev> where T:Hasher, - C: CalcRemainders, - F: Fn(T::Output) -> C, - Z:remainders::Division, - U: Fn(D) -> usize, - ::Output: AsRef<[u8]> + ::Output: BaseConversion { - to_dividend(T::hash(data.as_bytes())).calc_remainders(divisor).map(to_usize).collect() + T::hash(data.as_bytes()).convert_to_base(divisor) } pub(super) struct PasswordPartParameters<'a>{ @@ -308,49 +302,16 @@ impl AlgoSelection { } } -// Rust 1.52 only has a very limited support for const generics. This means, we'll have multiple impls for array conversion. -trait ToI32Array { - fn to_int_array(self) -> [u32; I]; -} - -//this could of course be done in a generic manner, but it's ugly without array_mut, which we don't have in Rust 1.52. -//Soo, pedestrian's approach :D -impl ToI32Array<5> for [u8;20] { - fn to_int_array(self) -> [u32; 5] { - [ - u32::from_be_bytes(self[0..4].try_into().unwrap()), - u32::from_be_bytes(self[4..8].try_into().unwrap()), - u32::from_be_bytes(self[8..12].try_into().unwrap()), - u32::from_be_bytes(self[12..16].try_into().unwrap()), - u32::from_be_bytes(self[16..20].try_into().unwrap()), - ] - } -} - -impl ToI32Array<8> for [u8;32] { - fn to_int_array(self) -> [u32; 8] { - [ - u32::from_be_bytes(self[0..4].try_into().unwrap()), - u32::from_be_bytes(self[4..8].try_into().unwrap()), - u32::from_be_bytes(self[8..12].try_into().unwrap()), - u32::from_be_bytes(self[12..16].try_into().unwrap()), - u32::from_be_bytes(self[16..20].try_into().unwrap()), - u32::from_be_bytes(self[20..24].try_into().unwrap()), - u32::from_be_bytes(self[24..28].try_into().unwrap()), - u32::from_be_bytes(self[28..32].try_into().unwrap()), - ] - } -} - -// Yeets the upper bytes of each UTF-16 char representation. Needed, because PasswordMaker Pro does that for some algorithms (HMAC, MD5 0.6) +// Yeets the upper bytes of each UTF-16 char representation. Needed, because PasswordMaker Pro did that for MD5 in version 0.6 // Returns bytes, because there's no way that this transform doesn't break the string. #[allow(clippy::cast_possible_truncation)] //clippy, stop complaining. Truncating is the very purpose of this function... fn yeet_upper_bytes(input : &str) -> impl Iterator + Clone + '_ { input.encode_utf16().map(|wide_char| wide_char as u8) } -// 0.6 Md5 might need to yoink an additional 0 for output graphemes. -fn yoink_additional_graphemes_for_06_if_needed(mut input : Vec) -> Vec { - input.resize(32, 0); - input +//signature subject to change, but need named types... +fn yoink_additional_graphemes_for_06_if_needed(input : std::iter::Rev>) + -> std::iter::Chain>, std::iter::Rev>> +{ + repeat(0_usize).take(32-input.len()).chain(input) } \ No newline at end of file diff --git a/src/passwordmaker/remainders.rs b/src/passwordmaker/remainders.rs deleted file mode 100644 index 93570a1..0000000 --- a/src/passwordmaker/remainders.rs +++ /dev/null @@ -1,74 +0,0 @@ -/// Adds `calc_remainders(divisor)` method to types that have some implementation of the Division trait. -pub(super) trait CalcRemainders{ - fn calc_remainders(self, divisor : D) -> Remainders; -} - -/// Implement `Division` to enable the `calc_remainders()` method for your type. -pub(super) trait Division where Self:Sized { - /// does in-place arbitrary-length division. Returns remainder. - fn divide(self, divisor : &D) -> DivisionResult; - fn is_zero(&self) -> bool; -} - -/// Or mark your type as `UseGenericDivision` to just use `/` and `%` operators for types. Makes only sense for integers. -pub(super) trait UseGenericDivision : Clone - + for <'a> std::ops::Div<&'a Self, Output = Self> - + for <'a> std::ops::Rem<&'a Self, Output = Self> - + Default - + Eq {} - -impl CalcRemainders for T - where T:Division -{ - fn calc_remainders(self, divisor : D) -> Remainders { - Remainders::new(self,divisor) - } -} - -pub(super) struct Remainders{ - value : Option, - divisor : U, -} - -impl> Remainders { - fn new(value : T, divisor : U) -> Self { - let value = if value.is_zero() { None } else { Some(value) }; - Remainders { - value, - divisor, - } - } -} - -impl> Iterator for Remainders{ - type Item=U; - - fn next(&mut self) -> Option { - if let Some(v) = self.value.take() { - let DivisionResult{result, remainder} = v.divide(&self.divisor); - self.value = if result.is_zero() { None } else { Some(result) }; - Some(remainder) - } else { - None - } - } -} - -pub(super) struct DivisionResult, U> { - pub result : T, - pub remainder : U, -} - -impl Division for U - where U: UseGenericDivision -{ - fn divide(self, divisor : &Self) -> DivisionResult { - DivisionResult { - result: self.clone().div(divisor), - remainder: self.rem(divisor) - } - } - fn is_zero(&self) -> bool { - *self == Self::default() - } -} \ No newline at end of file diff --git a/src/passwordmaker/remainders_impl.rs b/src/passwordmaker/remainders_impl.rs deleted file mode 100644 index 7de2189..0000000 --- a/src/passwordmaker/remainders_impl.rs +++ /dev/null @@ -1,76 +0,0 @@ -use super::remainders::{Division, UseGenericDivision, DivisionResult}; - -impl UseGenericDivision for u128{} //for Md4, Md5 - -impl Division for [u32;N] { - #[allow(clippy::cast_possible_truncation)] - fn divide(mut self, divisor : &usize) -> DivisionResult { - #[cfg(target_pointer_width = "64")] - type UsizeAndFour = u128; - #[cfg(not(target_pointer_width = "64"))] - type UsizeAndFour = u64; - assert!((UsizeAndFour::MAX >> 32) as u128 >= usize::MAX as u128); - - //uses mutation, because why not? self is owned after all :D - let divisor : UsizeAndFour = *divisor as UsizeAndFour; - let remainder = self.iter_mut().fold(0 as UsizeAndFour,|carry, current| { - assert_eq!(carry, carry & (usize::MAX as UsizeAndFour)); //carry has to be lower than divisor, and divisor is usize. - let carry_shifted = carry << 32; - let dividend = (carry_shifted) + (*current as UsizeAndFour); - let ratio = dividend / divisor; - assert_eq!(ratio, ratio & 0xffff_ffff); //this is fine. The first digit after re-adding the carry is alwys zero. - *current = (ratio) as u32; - dividend - (*current as UsizeAndFour) * divisor - }); - assert_eq!(remainder, remainder & (usize::MAX as UsizeAndFour)); - let remainder = remainder as usize; - DivisionResult{ - result: self, - remainder, - } - } - - fn is_zero(&self) -> bool { - self.iter().all(|x| *x == 0) - } -} - -#[cfg(test)] -mod remainders_tests{ - use super::super::remainders::CalcRemainders; - - use super::*; - #[test] - fn test_generic_division(){ - let v = 50u128; - let d = 7u128; - let DivisionResult{result, remainder}=v.divide(&d); - assert_eq!(7, result); - assert_eq!(1, remainder); - } - - #[test] - fn test_remainders() { - //relies on generic division. - let v = 141u128; - let d = 3u128; - let results : Vec = v.calc_remainders(d).collect(); - assert_eq!(results, vec![0u128,2u128,0u128,2u128,1u128]) - } - - #[test] - fn test_array_divide() { - let dividend_int = 0xe7f1ec3a5f35af805407a8a531eefb79u128; - let dividend = [(dividend_int >> 96) as u32, ((dividend_int >> 64) & 0xffffffff) as u32, ((dividend_int >> 32) & 0xffffffff) as u32, (dividend_int & 0xffffffff) as u32]; - #[cfg(target_pointer_width = "64")] - let divisor = 1531534813576487; - #[cfg(not(target_pointer_width = "64"))] - let divisor = 1531534813; - let result_int = dividend_int / (divisor as u128); - let remainder_int = dividend_int % (divisor as u128); - let result = dividend.divide(&divisor); - assert_eq!(result.result, [(result_int >> 96) as u32, ((result_int >> 64) & 0xffffffff) as u32, ((result_int >> 32) & 0xffffffff) as u32, (result_int & 0xffffffff) as u32]); - assert_eq!(remainder_int, result.remainder as u128); - } - -} \ No newline at end of file -- cgit v1.2.3