//! Stack-allocated big unsigned integers. #![allow(clippy::needless_range_loop, clippy::many_single_char_names)] #[macro_use] mod macros; mod add; mod add_mod; mod bit_and; mod bit_not; mod bit_or; mod bit_xor; mod bits; mod cmp; mod concat; mod div; pub(crate) mod div_limb; mod encoding; mod from; mod inv_mod; mod mul; mod mul_mod; mod neg; mod neg_mod; mod resize; mod shl; mod shr; mod split; mod sqrt; mod sub; mod sub_mod; /// Implements modular arithmetic for constant moduli. pub mod modular; #[cfg(feature = "generic-array")] mod array; #[cfg(feature = "rand_core")] mod rand; use crate::{Bounded, Encoding, Integer, Limb, Word, Zero}; use core::fmt; use subtle::{Choice, ConditionallySelectable}; #[cfg(feature = "serde")] use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "zeroize")] use zeroize::DefaultIsZeroes; /// Stack-allocated big unsigned integer. /// /// Generic over the given number of `LIMBS` /// /// # Encoding support /// This type supports many different types of encodings, either via the /// [`Encoding`][`crate::Encoding`] trait or various `const fn` decoding and /// encoding functions that can be used with [`Uint`] constants. /// /// Optional crate features for encoding (off-by-default): /// - `generic-array`: enables [`ArrayEncoding`][`crate::ArrayEncoding`] trait which can be used to /// [`Uint`] as `GenericArray` and a [`ArrayDecoding`][`crate::ArrayDecoding`] trait which /// can be used to `GenericArray` as [`Uint`]. /// - `rlp`: support for [Recursive Length Prefix (RLP)][RLP] encoding. /// /// [RLP]: https://eth.wiki/fundamentals/rlp // TODO(tarcieri): make generic around a specified number of bits. // Our PartialEq impl only differs from the default one by being constant-time, so this is safe #[allow(clippy::derived_hash_with_manual_eq)] #[derive(Copy, Clone, Hash)] pub struct Uint { /// Inner limb array. Stored from least significant to most significant. limbs: [Limb; LIMBS], } impl Uint { /// The value `0`. pub const ZERO: Self = Self::from_u8(0); /// The value `1`. pub const ONE: Self = Self::from_u8(1); /// Maximum value this [`Uint`] can express. pub const MAX: Self = Self { limbs: [Limb::MAX; LIMBS], }; /// Total size of the represented integer in bits. pub const BITS: usize = LIMBS * Limb::BITS; /// Bit size of `BITS`. // Note: assumes the type of `BITS` is `usize`. Any way to assert that? pub(crate) const LOG2_BITS: usize = (usize::BITS - Self::BITS.leading_zeros()) as usize; /// Total size of the represented integer in bytes. pub const BYTES: usize = LIMBS * Limb::BYTES; /// The number of limbs used on this platform. pub const LIMBS: usize = LIMBS; /// Const-friendly [`Uint`] constructor. pub const fn new(limbs: [Limb; LIMBS]) -> Self { Self { limbs } } /// Create a [`Uint`] from an array of [`Word`]s (i.e. word-sized unsigned /// integers). #[inline] pub const fn from_words(arr: [Word; LIMBS]) -> Self { let mut limbs = [Limb::ZERO; LIMBS]; let mut i = 0; while i < LIMBS { limbs[i] = Limb(arr[i]); i += 1; } Self { limbs } } /// Create an array of [`Word`]s (i.e. word-sized unsigned integers) from /// a [`Uint`]. #[inline] pub const fn to_words(self) -> [Word; LIMBS] { let mut arr = [0; LIMBS]; let mut i = 0; while i < LIMBS { arr[i] = self.limbs[i].0; i += 1; } arr } /// Borrow the inner limbs as an array of [`Word`]s. pub const fn as_words(&self) -> &[Word; LIMBS] { // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` #[allow(trivial_casts, unsafe_code)] unsafe { &*((&self.limbs as *const _) as *const [Word; LIMBS]) } } /// Borrow the inner limbs as a mutable array of [`Word`]s. pub fn as_words_mut(&mut self) -> &mut [Word; LIMBS] { // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word` #[allow(trivial_casts, unsafe_code)] unsafe { &mut *((&mut self.limbs as *mut _) as *mut [Word; LIMBS]) } } /// Borrow the limbs of this [`Uint`]. pub const fn as_limbs(&self) -> &[Limb; LIMBS] { &self.limbs } /// Borrow the limbs of this [`Uint`] mutably. pub fn as_limbs_mut(&mut self) -> &mut [Limb; LIMBS] { &mut self.limbs } /// Convert this [`Uint`] into its inner limbs. pub const fn to_limbs(self) -> [Limb; LIMBS] { self.limbs } } impl AsRef<[Word; LIMBS]> for Uint { fn as_ref(&self) -> &[Word; LIMBS] { self.as_words() } } impl AsMut<[Word; LIMBS]> for Uint { fn as_mut(&mut self) -> &mut [Word; LIMBS] { self.as_words_mut() } } impl AsRef<[Limb]> for Uint { fn as_ref(&self) -> &[Limb] { self.as_limbs() } } impl AsMut<[Limb]> for Uint { fn as_mut(&mut self) -> &mut [Limb] { self.as_limbs_mut() } } impl ConditionallySelectable for Uint { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { let mut limbs = [Limb::ZERO; LIMBS]; for i in 0..LIMBS { limbs[i] = Limb::conditional_select(&a.limbs[i], &b.limbs[i], choice); } Self { limbs } } } impl Default for Uint { fn default() -> Self { Self::ZERO } } impl Integer for Uint { const ONE: Self = Self::ONE; const MAX: Self = Self::MAX; const BITS: usize = Self::BITS; const BYTES: usize = Self::BYTES; const LIMBS: usize = Self::LIMBS; fn is_odd(&self) -> Choice { self.limbs .first() .map(|limb| limb.is_odd()) .unwrap_or_else(|| Choice::from(0)) } } impl Zero for Uint { const ZERO: Self = Self::ZERO; } impl Bounded for Uint { const BITS: usize = Self::BITS; const BYTES: usize = Self::BYTES; } impl fmt::Debug for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Uint(0x{self:X})") } } impl fmt::Display for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::UpperHex::fmt(self, f) } } impl fmt::LowerHex for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for limb in self.limbs.iter().rev() { fmt::LowerHex::fmt(limb, f)?; } Ok(()) } } impl fmt::UpperHex for Uint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for limb in self.limbs.iter().rev() { fmt::UpperHex::fmt(limb, f)?; } Ok(()) } } #[cfg(feature = "serde")] impl<'de, const LIMBS: usize> Deserialize<'de> for Uint where Uint: Encoding, { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let mut buffer = Self::ZERO.to_le_bytes(); serdect::array::deserialize_hex_or_bin(buffer.as_mut(), deserializer)?; Ok(Self::from_le_bytes(buffer)) } } #[cfg(feature = "serde")] impl Serialize for Uint where Uint: Encoding, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serdect::array::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer) } } #[cfg(feature = "zeroize")] impl DefaultIsZeroes for Uint {} // TODO(tarcieri): use `generic_const_exprs` when stable to make generic around bits. impl_uint_aliases! { (U64, 64, "64-bit"), (U128, 128, "128-bit"), (U192, 192, "192-bit"), (U256, 256, "256-bit"), (U320, 320, "320-bit"), (U384, 384, "384-bit"), (U448, 448, "448-bit"), (U512, 512, "512-bit"), (U576, 576, "576-bit"), (U640, 640, "640-bit"), (U704, 704, "704-bit"), (U768, 768, "768-bit"), (U832, 832, "832-bit"), (U896, 896, "896-bit"), (U960, 960, "960-bit"), (U1024, 1024, "1024-bit"), (U1280, 1280, "1280-bit"), (U1536, 1536, "1536-bit"), (U1792, 1792, "1792-bit"), (U2048, 2048, "2048-bit"), (U3072, 3072, "3072-bit"), (U3584, 3584, "3584-bit"), (U4096, 4096, "4096-bit"), (U4224, 4224, "4224-bit"), (U4352, 4352, "4352-bit"), (U6144, 6144, "6144-bit"), (U8192, 8192, "8192-bit"), (U16384, 16384, "16384-bit"), (U32768, 32768, "32768-bit") } #[cfg(target_pointer_width = "32")] impl_uint_aliases! { (U224, 224, "224-bit"), // For NIST P-224 (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 and split 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; #[cfg(feature = "extra-sizes")] pub use extra_sizes::*; #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { use crate::{Encoding, U128}; use subtle::ConditionallySelectable; #[cfg(feature = "alloc")] use alloc::format; #[cfg(feature = "serde")] use crate::U64; #[cfg(feature = "alloc")] #[test] fn debug() { let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD"; let n = U128::from_be_hex(hex); assert_eq!( format!("{:?}", n), "Uint(0xAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD)" ); } #[cfg(feature = "alloc")] #[test] fn display() { let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD"; let n = U128::from_be_hex(hex); use alloc::string::ToString; assert_eq!(hex, n.to_string()); let hex = "AAAAAAAABBBBBBBB0000000000000000"; let n = U128::from_be_hex(hex); assert_eq!(hex, n.to_string()); let hex = "AAAAAAAABBBBBBBB00000000DDDDDDDD"; let n = U128::from_be_hex(hex); assert_eq!(hex, n.to_string()); let hex = "AAAAAAAABBBBBBBB0CCCCCCCDDDDDDDD"; let n = U128::from_be_hex(hex); assert_eq!(hex, n.to_string()); } #[test] fn from_bytes() { let a = U128::from_be_hex("AAAAAAAABBBBBBBB0CCCCCCCDDDDDDDD"); let be_bytes = a.to_be_bytes(); let le_bytes = a.to_le_bytes(); for i in 0..16 { assert_eq!(le_bytes[i], be_bytes[15 - i]); } let a_from_be = U128::from_be_bytes(be_bytes); let a_from_le = U128::from_le_bytes(le_bytes); assert_eq!(a_from_be, a_from_le); assert_eq!(a_from_be, a); } #[test] fn conditional_select() { let a = U128::from_be_hex("00002222444466668888AAAACCCCEEEE"); let b = U128::from_be_hex("11113333555577779999BBBBDDDDFFFF"); let select_0 = U128::conditional_select(&a, &b, 0.into()); assert_eq!(a, select_0); let select_1 = U128::conditional_select(&a, &b, 1.into()); assert_eq!(b, select_1); } #[cfg(feature = "serde")] #[test] fn serde() { const TEST: U64 = U64::from_u64(0x0011223344556677); let serialized = bincode::serialize(&TEST).unwrap(); let deserialized: U64 = bincode::deserialize(&serialized).unwrap(); assert_eq!(TEST, deserialized); } #[cfg(feature = "serde")] #[test] fn serde_owned() { const TEST: U64 = U64::from_u64(0x0011223344556677); let serialized = bincode::serialize(&TEST).unwrap(); let deserialized: U64 = bincode::deserialize_from(serialized.as_slice()).unwrap(); assert_eq!(TEST, deserialized); } }