diff --git a/src/limb/encoding.rs b/src/limb/encoding.rs index f6906104c..d77ab1fe5 100644 --- a/src/limb/encoding.rs +++ b/src/limb/encoding.rs @@ -34,16 +34,30 @@ impl Encoding for Limb { impl Limb { /// Decode limb from a big endian byte slice. /// - /// Panics if the slice is not the same size as [`Limb::Repr`]. + /// Panics if the slice is larger than [`Limb::Repr`]. pub(crate) fn from_be_slice(bytes: &[u8]) -> Self { - Self::from_be_bytes(bytes.try_into().expect("slice not limb-sized")) + let mut repr = Self::ZERO.to_be_bytes(); + let repr_len = repr.len(); + assert!( + bytes.len() <= repr_len, + "The given slice is larger than the limb size" + ); + repr[(repr_len - bytes.len())..].copy_from_slice(bytes); + Self::from_be_bytes(repr) } /// Decode limb from a little endian byte slice. /// /// Panics if the slice is not the same size as [`Limb::Repr`]. pub(crate) fn from_le_slice(bytes: &[u8]) -> Self { - Self::from_le_bytes(bytes.try_into().expect("slice not limb-sized")) + let mut repr = Self::ZERO.to_le_bytes(); + let repr_len = repr.len(); + assert!( + bytes.len() <= repr_len, + "The given slice is larger than the limb size" + ); + repr[..bytes.len()].copy_from_slice(bytes); + Self::from_le_bytes(repr) } } diff --git a/src/macros.rs b/src/macros.rs index a291f015f..c5a82beda 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -59,7 +59,7 @@ macro_rules! const_assert_ne { #[macro_export] macro_rules! nlimbs { ($bits:expr) => { - $bits / $crate::Limb::BITS as usize + (($bits + $crate::Limb::BITS - 1) / $crate::Limb::BITS) as usize }; } diff --git a/src/uint/boxed.rs b/src/uint/boxed.rs index 62b5f4916..71fb349d7 100644 --- a/src/uint/boxed.rs +++ b/src/uint/boxed.rs @@ -50,6 +50,10 @@ pub struct BoxedUint { } impl BoxedUint { + fn limbs_for_precision(at_least_bits_precision: u32) -> usize { + ((at_least_bits_precision + Limb::BITS - 1) / Limb::BITS) as usize + } + /// Get the value `0` represented as succinctly as possible. pub fn zero() -> Self { Self { @@ -59,15 +63,9 @@ impl BoxedUint { /// Get the value `0` with the given number of bits of precision. /// - /// Panics if the precision is not a multiple of [`Limb::BITS`]. - pub fn zero_with_precision(bits_precision: u32) -> Self { - assert_eq!( - bits_precision % Limb::BITS, - 0, - "precision is not a multiple of limb size" - ); - - vec![Limb::ZERO; (bits_precision / Limb::BITS) as usize].into() + /// `at_least_bits_precision` is rounded up to a multiple of [`Limb::BITS`]. + pub fn zero_with_precision(at_least_bits_precision: u32) -> Self { + vec![Limb::ZERO; Self::limbs_for_precision(at_least_bits_precision)].into() } /// Get the value `1`, represented as succinctly as possible. @@ -79,11 +77,9 @@ impl BoxedUint { /// Get the value `1` with the given number of bits of precision. /// - /// Panics if the precision is not at least [`Limb::BITS`] or if it is not - /// a multiple thereof. - pub fn one_with_precision(bits_precision: u32) -> Self { - assert!(bits_precision >= Limb::BITS, "precision too small"); - let mut ret = Self::zero_with_precision(bits_precision); + /// `at_least_bits_precision` is rounded up to a multiple of [`Limb::BITS`]. + pub fn one_with_precision(at_least_bits_precision: u32) -> Self { + let mut ret = Self::zero_with_precision(at_least_bits_precision); ret.limbs[0] = Limb::ONE; ret } @@ -125,17 +121,12 @@ impl BoxedUint { !self.is_odd() } - /// Get the maximum value for a given number of bits of precision. + /// Get the maximum value for a `BoxedUint` created with `at_least_bits_precision` + /// precision bits requested. /// - /// Panics if the precision is not a multiple of [`Limb::BITS`]. - pub fn max(bits_precision: u32) -> Self { - assert_eq!( - bits_precision % Limb::BITS, - 0, - "precision is not a multiple of limb size" - ); - - vec![Limb::MAX; (bits_precision / Limb::BITS) as usize].into() + /// That is, returns the value `2^self.bits_precision() - 1`. + pub fn max(at_least_bits_precision: u32) -> Self { + vec![Limb::MAX; Self::limbs_for_precision(at_least_bits_precision)].into() } /// Create a [`BoxedUint`] from an array of [`Word`]s (i.e. word-sized unsigned @@ -235,25 +226,21 @@ impl BoxedUint { /// Widen this type's precision to the given number of bits. /// - /// Panics if `bits_precision` is not a multiple of `Limb::BITS` or smaller than the current - /// precision. - pub fn widen(&self, bits_precision: u32) -> BoxedUint { - assert!(bits_precision % Limb::BITS == 0); - assert!(bits_precision >= self.bits_precision()); + /// Panics if `at_least_bits_precision` is smaller than the current precision. + pub fn widen(&self, at_least_bits_precision: u32) -> BoxedUint { + assert!(at_least_bits_precision >= self.bits_precision()); - let mut ret = BoxedUint::zero_with_precision(bits_precision); + let mut ret = BoxedUint::zero_with_precision(at_least_bits_precision); ret.limbs[..self.nlimbs()].copy_from_slice(&self.limbs); ret } /// Shortens this type's precision to the given number of bits. /// - /// Panics if `bits_precision` is not a multiple of `Limb::BITS` or smaller than the current - /// precision. - pub fn shorten(&self, bits_precision: u32) -> BoxedUint { - assert!(bits_precision % Limb::BITS == 0); - assert!(bits_precision <= self.bits_precision()); - let mut ret = BoxedUint::zero_with_precision(bits_precision); + /// Panics if `at_least_bits_precision` is larger than the current precision. + pub fn shorten(&self, at_least_bits_precision: u32) -> BoxedUint { + assert!(at_least_bits_precision <= self.bits_precision()); + let mut ret = BoxedUint::zero_with_precision(at_least_bits_precision); let nlimbs = ret.nlimbs(); ret.limbs.copy_from_slice(&self.limbs[..nlimbs]); ret diff --git a/src/uint/boxed/encoding.rs b/src/uint/boxed/encoding.rs index ab4b0c0ad..a5bd54788 100644 --- a/src/uint/boxed/encoding.rs +++ b/src/uint/boxed/encoding.rs @@ -8,18 +8,21 @@ use core::fmt; /// Decoding errors for [`BoxedUint`]. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum DecodeError { - /// Input is not a valid size. + /// Input size is too small to fit in the given precision. InputSize, - /// Precision is not a multiple of [`Limb::BYTES`]. + /// The deserialized number is larger than the given precision. Precision, } impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::InputSize => write!(f, "input is not a valid size"), - Self::Precision => write!(f, "precision is not a multiple of the word size"), + Self::InputSize => write!(f, "input size is too small to fit in the given precision"), + Self::Precision => write!( + f, + "the deserialized number is larger than the given precision" + ), } } } @@ -31,51 +34,53 @@ impl BoxedUint { /// Create a new [`BoxedUint`] from the provided big endian bytes. /// /// The `bits_precision` argument represents the precision of the resulting integer, which is - /// fixed as this type is not arbitrary-precision. It MUST be a multiple of the limb size, i.e. - /// [`Limb::BITS`], or otherwise this function will return [`DecodeError::Precision`]. + /// fixed as this type is not arbitrary-precision. + /// The new [`BoxedUint`] will be created with `bits_precision` + /// rounded up to a multiple of [`Limb::BITS`]. /// - /// If the length of `bytes` (when interpreted as bits) is larger than `bits_precision`, this - /// function will return [`DecodeError::InputSize`]. + /// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8) + /// this function will return [`DecodeError::InputSize`]. + /// If the size of the decoded integer is larger than `bits_precision`, + /// this function will return [`DecodeError::Precision`]. pub fn from_be_slice(bytes: &[u8], bits_precision: u32) -> Result { if bytes.is_empty() && bits_precision == 0 { return Ok(Self::zero()); } - if bits_precision % Limb::BITS != 0 { - return Err(DecodeError::Precision); - } - - if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision as usize { + if bytes.len() > (bits_precision as usize + 7) / 8 { return Err(DecodeError::InputSize); } let mut ret = Self::zero_with_precision(bits_precision); - for (chunk, limb) in bytes.chunks(Limb::BYTES).rev().zip(ret.limbs.iter_mut()) { + for (chunk, limb) in bytes.rchunks(Limb::BYTES).zip(ret.limbs.iter_mut()) { *limb = Limb::from_be_slice(chunk); } + if bits_precision < ret.bits() { + return Err(DecodeError::Precision); + } + Ok(ret) } /// Create a new [`BoxedUint`] from the provided little endian bytes. /// /// The `bits_precision` argument represents the precision of the resulting integer, which is - /// fixed as this type is not arbitrary-precision. It MUST be a multiple of the limb size, i.e. - /// [`Limb::BITS`], or otherwise this function will return [`DecodeError::Precision`]. + /// fixed as this type is not arbitrary-precision. + /// The new [`BoxedUint`] will be created with `bits_precision` + /// rounded up to a multiple of [`Limb::BITS`]. /// - /// If the length of `bytes` (when interpreted as bits) is larger than `bits_precision`, this - /// function will return [`DecodeError::InputSize`]. + /// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8) + /// this function will return [`DecodeError::InputSize`]. + /// If the size of the decoded integer is larger than `bits_precision`, + /// this function will return [`DecodeError::Precision`]. pub fn from_le_slice(bytes: &[u8], bits_precision: u32) -> Result { if bytes.is_empty() && bits_precision == 0 { return Ok(Self::zero()); } - if bits_precision % Limb::BITS != 0 { - return Err(DecodeError::Precision); - } - - if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision as usize { + if bytes.len() > (bits_precision as usize + 7) / 8 { return Err(DecodeError::InputSize); } @@ -85,6 +90,10 @@ impl BoxedUint { *limb = Limb::from_le_slice(chunk); } + if bits_precision < ret.bits() { + return Err(DecodeError::Precision); + } + Ok(ret) } @@ -186,19 +195,39 @@ mod tests { } #[test] + #[cfg(target_pointer_width = "32")] fn from_be_slice_not_word_sized() { - let bytes = hex!("00112233445566778899aabbccddee"); + let bytes = hex!("112233445566778899aabbccddeeff"); + let n = BoxedUint::from_be_slice(&bytes, 127).unwrap(); assert_eq!( - BoxedUint::from_be_slice(&bytes, 128), - Err(DecodeError::InputSize) + n.as_limbs(), + &[ + Limb(0xccddeeff), + Limb(0x8899aabb), + Limb(0x44556677), + Limb(0x00112233) + ] ); + assert_eq!(n.bits_precision(), 128); } #[test] - fn from_be_slice_bad_precision() { - let bytes = hex!("00112233445566778899aabbccddeeff"); + #[cfg(target_pointer_width = "64")] + fn from_be_slice_not_word_sized() { + let bytes = hex!("112233445566778899aabbccddeeff"); + let n = BoxedUint::from_be_slice(&bytes, 127).unwrap(); assert_eq!( - BoxedUint::from_be_slice(&bytes, 127), + n.as_limbs(), + &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)] + ); + assert_eq!(n.bits_precision(), 128); + } + + #[test] + fn from_be_slice_non_multiple_precision() { + let bytes = hex!("0f112233445566778899aabbccddeeff"); + assert_eq!( + BoxedUint::from_be_slice(&bytes, 121), Err(DecodeError::Precision) ); } @@ -259,19 +288,39 @@ mod tests { } #[test] + #[cfg(target_pointer_width = "32")] fn from_le_slice_not_word_sized() { let bytes = hex!("ffeeddccbbaa998877665544332211"); + let n = BoxedUint::from_le_slice(&bytes, 127).unwrap(); assert_eq!( - BoxedUint::from_be_slice(&bytes, 128), - Err(DecodeError::InputSize) + n.as_limbs(), + &[ + Limb(0xccddeeff), + Limb(0x8899aabb), + Limb(0x44556677), + Limb(0x00112233) + ] ); + assert_eq!(n.bits_precision(), 128); } #[test] - fn from_le_slice_bad_precision() { - let bytes = hex!("ffeeddccbbaa99887766554433221100"); + #[cfg(target_pointer_width = "64")] + fn from_le_slice_not_word_sized() { + let bytes = hex!("ffeeddccbbaa998877665544332211"); + let n = BoxedUint::from_le_slice(&bytes, 127).unwrap(); + assert_eq!( + n.as_limbs(), + &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)] + ); + assert_eq!(n.bits_precision(), 128); + } + + #[test] + fn from_le_slice_non_multiple_precision() { + let bytes = hex!("ffeeddccbbaa998877665544332211f0"); assert_eq!( - BoxedUint::from_le_slice(&bytes, 127), + BoxedUint::from_le_slice(&bytes, 121), Err(DecodeError::Precision) ); } diff --git a/src/uint/boxed/rand.rs b/src/uint/boxed/rand.rs index 386cfec51..d6890b347 100644 --- a/src/uint/boxed/rand.rs +++ b/src/uint/boxed/rand.rs @@ -6,13 +6,19 @@ use rand_core::CryptoRngCore; impl BoxedUint { /// Generate a cryptographically secure random [`BoxedUint`]. - pub fn random(mut rng: &mut impl CryptoRngCore, bits_precision: u32) -> Self { + /// in range `[0, 2^bits_precision)`. + pub fn random(rng: &mut impl CryptoRngCore, bits_precision: u32) -> Self { let mut ret = BoxedUint::zero_with_precision(bits_precision); for limb in &mut *ret.limbs { - *limb = Limb::random(&mut rng) + *limb = Limb::random(rng) } + // Since `bits_precision` will be rounded up on creation of `ret`, + // we need to clear the high bits if the rounding occurred. + ret.limbs[ret.limbs.len() - 1] = + ret.limbs[ret.limbs.len() - 1] & (Limb::MAX >> (ret.bits_precision() - bits_precision)); + ret } } @@ -36,28 +42,38 @@ impl RandomMod for BoxedUint { #[cfg(test)] mod tests { - use crate::{NonZero, RandomMod, U256}; + use crate::{BoxedUint, NonZero, RandomMod}; use rand_core::SeedableRng; + #[test] + fn random() { + let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1); + + let r = BoxedUint::random(&mut rng, 256); + assert!(r.bits_precision() == 256); + + let r = BoxedUint::random(&mut rng, 256 - 32 + 1); + assert!(r.bits_precision() == 256); + assert!(r < BoxedUint::one_with_precision(256) << (256 - 32 + 1)); + } + #[test] fn random_mod() { let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1); // Ensure `random_mod` runs in a reasonable amount of time - let modulus = NonZero::new(U256::from(42u8)).unwrap(); - let res = U256::random_mod(&mut rng, &modulus); + let modulus = NonZero::new(BoxedUint::from(42u8)).unwrap(); + let res = BoxedUint::random_mod(&mut rng, &modulus); // Check that the value is in range - assert!(res >= U256::ZERO); - assert!(res < U256::from(42u8)); + assert!(res < BoxedUint::from(42u8)); // Ensure `random_mod` runs in a reasonable amount of time // when the modulus is larger than 1 limb - let modulus = NonZero::new(U256::from(0x10000000000000001u128)).unwrap(); - let res = U256::random_mod(&mut rng, &modulus); + let modulus = NonZero::new(BoxedUint::from(0x10000000000000001u128)).unwrap(); + let res = BoxedUint::random_mod(&mut rng, &modulus); // Check that the value is in range - assert!(res >= U256::ZERO); - assert!(res < U256::from(0x10000000000000001u128)); + assert!(res < BoxedUint::from(0x10000000000000001u128)); } } diff --git a/src/uint/rand.rs b/src/uint/rand.rs index 8f97c7b1b..3028a5e9e 100644 --- a/src/uint/rand.rs +++ b/src/uint/rand.rs @@ -87,7 +87,6 @@ mod tests { let res = U256::random_mod(&mut rng, &modulus); // Check that the value is in range - assert!(res >= U256::ZERO); assert!(res < U256::from(42u8)); // Ensure `random_mod` runs in a reasonable amount of time @@ -96,7 +95,6 @@ mod tests { let res = U256::random_mod(&mut rng, &modulus); // Check that the value is in range - assert!(res >= U256::ZERO); assert!(res < U256::from(0x10000000000000001u128)); } }