crypto_bigint/
uint.rs

1//! Big unsigned integers.
2
3#![allow(
4    clippy::needless_range_loop,
5    clippy::many_single_char_names,
6    clippy::derive_hash_xor_eq
7)]
8
9#[macro_use]
10mod concat;
11#[macro_use]
12mod split;
13
14mod add;
15mod add_mod;
16mod bit_and;
17mod bit_not;
18mod bit_or;
19mod bit_xor;
20mod bits;
21mod cmp;
22mod div;
23mod encoding;
24mod from;
25mod inv_mod;
26mod mul;
27mod mul_mod;
28mod neg_mod;
29mod resize;
30mod shl;
31mod shr;
32mod sqrt;
33mod sub;
34mod sub_mod;
35
36#[cfg(feature = "generic-array")]
37mod array;
38
39#[cfg(feature = "rand_core")]
40mod rand;
41
42use crate::{Concat, Encoding, Integer, Limb, Split, Word, Zero};
43use core::{fmt, mem};
44use subtle::{Choice, ConditionallySelectable};
45
46#[cfg(feature = "serde")]
47use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer};
48
49#[cfg(feature = "zeroize")]
50use zeroize::DefaultIsZeroes;
51
52/// Big unsigned integer.
53///
54/// Generic over the given number of `LIMBS`
55///
56/// # Encoding support
57/// This type supports many different types of encodings, either via the
58/// [`Encoding`][`crate::Encoding`] trait or various `const fn` decoding and
59/// encoding functions that can be used with [`UInt`] constants.
60///
61/// Optional crate features for encoding (off-by-default):
62/// - `generic-array`: enables [`ArrayEncoding`][`crate::ArrayEncoding`] trait which can be used to
63///   [`UInt`] as `GenericArray<u8, N>` and a [`ArrayDecoding`][`crate::ArrayDecoding`] trait which
64///   can be used to `GenericArray<u8, N>` as [`UInt`].
65/// - `rlp`: support for [Recursive Length Prefix (RLP)][RLP] encoding.
66///
67/// [RLP]: https://eth.wiki/fundamentals/rlp
68// TODO(tarcieri): make generic around a specified number of bits.
69#[derive(Copy, Clone, Debug, Hash)]
70pub struct UInt<const LIMBS: usize> {
71    /// Inner limb array. Stored from least significant to most significant.
72    limbs: [Limb; LIMBS],
73}
74
75impl<const LIMBS: usize> UInt<LIMBS> {
76    /// The value `0`.
77    pub const ZERO: Self = Self::from_u8(0);
78
79    /// The value `1`.
80    pub const ONE: Self = Self::from_u8(1);
81
82    /// The number of limbs used on this platform.
83    pub const LIMBS: usize = LIMBS;
84
85    /// Maximum value this [`UInt`] can express.
86    pub const MAX: Self = Self {
87        limbs: [Limb::MAX; LIMBS],
88    };
89
90    /// Const-friendly [`UInt`] constructor.
91    pub const fn new(limbs: [Limb; LIMBS]) -> Self {
92        Self { limbs }
93    }
94
95    /// Create a [`UInt`] from an array of [`Word`]s (i.e. word-sized unsigned
96    /// integers).
97    #[inline]
98    pub const fn from_words(arr: [Word; LIMBS]) -> Self {
99        let mut limbs = [Limb::ZERO; LIMBS];
100        let mut i = 0;
101
102        while i < LIMBS {
103            limbs[i] = Limb(arr[i]);
104            i += 1;
105        }
106
107        Self { limbs }
108    }
109
110    /// Create an array of [`Word`]s (i.e. word-sized unsigned integers) from
111    /// a [`UInt`].
112    #[inline]
113    pub const fn to_words(self) -> [Word; LIMBS] {
114        let mut arr = [0; LIMBS];
115        let mut i = 0;
116
117        while i < LIMBS {
118            arr[i] = self.limbs[i].0;
119            i += 1;
120        }
121
122        arr
123    }
124
125    /// Borrow the inner limbs as an array of [`Word`]s.
126    pub const fn as_words(&self) -> &[Word; LIMBS] {
127        // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word`
128        #[allow(unsafe_code)]
129        unsafe {
130            // TODO(tarcieri): use &*((&self.limbs as *const _) as *const [Word; LIMBS])
131            mem::transmute(&self.limbs)
132        }
133    }
134
135    /// Borrow the inner limbs as a mutable array of [`Word`]s.
136    pub fn as_words_mut(&mut self) -> &mut [Word; LIMBS] {
137        // SAFETY: `Limb` is a `repr(transparent)` newtype for `Word`
138        #[allow(trivial_casts, unsafe_code)]
139        unsafe {
140            &mut *((&mut self.limbs as *mut _) as *mut [Word; LIMBS])
141        }
142    }
143
144    /// Deprecated: borrow the inner limbs as an array of [`Word`]s.
145    #[deprecated(since = "0.4.8", note = "please use `as_words` instead")]
146    pub const fn as_uint_array(&self) -> &[Word; LIMBS] {
147        self.as_words()
148    }
149
150    /// Deprecated: create a [`UInt`] from an array of [`Word`]s.
151    #[deprecated(since = "0.4.8", note = "please use `from_words` instead")]
152    pub const fn from_uint_array(words: [Word; LIMBS]) -> Self {
153        Self::from_words(words)
154    }
155
156    /// Deprecated: create an array of [`Word`]s from a [`UInt`].
157    #[deprecated(since = "0.4.8", note = "please use `to_words` instead")]
158    pub const fn to_uint_array(self) -> [Word; LIMBS] {
159        self.to_words()
160    }
161
162    /// Borrow the limbs of this [`UInt`].
163    // TODO(tarcieri): rename to `as_limbs` for consistency with `as_words`
164    pub const fn limbs(&self) -> &[Limb; LIMBS] {
165        &self.limbs
166    }
167
168    /// Borrow the limbs of this [`UInt`] mutably.
169    // TODO(tarcieri): rename to `as_limbs_mut` for consistency with `as_words_mut`
170    pub fn limbs_mut(&mut self) -> &mut [Limb; LIMBS] {
171        &mut self.limbs
172    }
173
174    /// Convert this [`UInt`] into its inner limbs.
175    // TODO(tarcieri): rename to `to_limbs` for consistency with `to_words`
176    pub const fn into_limbs(self) -> [Limb; LIMBS] {
177        self.limbs
178    }
179}
180
181impl<const LIMBS: usize> AsRef<[Word; LIMBS]> for UInt<LIMBS> {
182    fn as_ref(&self) -> &[Word; LIMBS] {
183        self.as_words()
184    }
185}
186
187impl<const LIMBS: usize> AsMut<[Word; LIMBS]> for UInt<LIMBS> {
188    fn as_mut(&mut self) -> &mut [Word; LIMBS] {
189        self.as_words_mut()
190    }
191}
192
193// TODO(tarcieri): eventually phase this out in favor of `limbs()`?
194impl<const LIMBS: usize> AsRef<[Limb]> for UInt<LIMBS> {
195    fn as_ref(&self) -> &[Limb] {
196        self.limbs()
197    }
198}
199
200// TODO(tarcieri): eventually phase this out in favor of `limbs_mut()`?
201impl<const LIMBS: usize> AsMut<[Limb]> for UInt<LIMBS> {
202    fn as_mut(&mut self) -> &mut [Limb] {
203        self.limbs_mut()
204    }
205}
206
207impl<const LIMBS: usize> ConditionallySelectable for UInt<LIMBS> {
208    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
209        let mut limbs = [Limb::ZERO; LIMBS];
210
211        for i in 0..LIMBS {
212            limbs[i] = Limb::conditional_select(&a.limbs[i], &b.limbs[i], choice);
213        }
214
215        Self { limbs }
216    }
217}
218
219impl<const LIMBS: usize> Default for UInt<LIMBS> {
220    fn default() -> Self {
221        Self::ZERO
222    }
223}
224
225impl<const LIMBS: usize> Integer for UInt<LIMBS> {
226    const ONE: Self = Self::ONE;
227    const MAX: Self = Self::MAX;
228
229    fn is_odd(&self) -> Choice {
230        self.limbs
231            .first()
232            .map(|limb| limb.is_odd())
233            .unwrap_or_else(|| Choice::from(0))
234    }
235}
236
237impl<const LIMBS: usize> Zero for UInt<LIMBS> {
238    const ZERO: Self = Self::ZERO;
239}
240
241impl<const LIMBS: usize> fmt::Display for UInt<LIMBS> {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        fmt::UpperHex::fmt(self, f)
244    }
245}
246
247impl<const LIMBS: usize> fmt::LowerHex for UInt<LIMBS> {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        for limb in self.limbs.iter().rev() {
250            fmt::LowerHex::fmt(limb, f)?;
251        }
252        Ok(())
253    }
254}
255
256impl<const LIMBS: usize> fmt::UpperHex for UInt<LIMBS> {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        for limb in self.limbs.iter().rev() {
259            fmt::UpperHex::fmt(limb, f)?;
260        }
261        Ok(())
262    }
263}
264
265#[cfg(feature = "serde")]
266#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
267impl<'de, const LIMBS: usize> Deserialize<'de> for UInt<LIMBS>
268where
269    UInt<LIMBS>: Encoding,
270{
271    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
272    where
273        D: Deserializer<'de>,
274    {
275        let mut buffer = Self::ZERO.to_le_bytes();
276        serdect::array::deserialize_hex_or_bin(buffer.as_mut(), deserializer)?;
277
278        Ok(Self::from_le_bytes(buffer))
279    }
280}
281
282#[cfg(feature = "serde")]
283#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
284impl<'de, const LIMBS: usize> Serialize for UInt<LIMBS>
285where
286    UInt<LIMBS>: Encoding,
287{
288    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
289    where
290        S: Serializer,
291    {
292        serdect::array::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer)
293    }
294}
295
296#[cfg(feature = "zeroize")]
297#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
298impl<const LIMBS: usize> DefaultIsZeroes for UInt<LIMBS> {}
299
300// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits.
301macro_rules! impl_uint_aliases {
302    ($(($name:ident, $bits:expr, $doc:expr)),+) => {
303        $(
304            #[doc = $doc]
305            #[doc="unsigned big integer."]
306            pub type $name = UInt<{nlimbs!($bits)}>;
307
308            impl Encoding for $name {
309                const BIT_SIZE: usize = $bits;
310                const BYTE_SIZE: usize = $bits / 8;
311
312                type Repr = [u8; $bits / 8];
313
314                #[inline]
315                fn from_be_bytes(bytes: Self::Repr) -> Self {
316                    Self::from_be_slice(&bytes)
317                }
318
319                #[inline]
320                fn from_le_bytes(bytes: Self::Repr) -> Self {
321                    Self::from_le_slice(&bytes)
322                }
323
324                #[inline]
325                fn to_be_bytes(&self) -> Self::Repr {
326                    let mut result = [0u8; $bits / 8];
327                    self.write_be_bytes(&mut result);
328                    result
329                }
330
331                #[inline]
332                fn to_le_bytes(&self) -> Self::Repr {
333                    let mut result = [0u8; $bits / 8];
334                    self.write_le_bytes(&mut result);
335                    result
336                }
337            }
338        )+
339     };
340}
341
342// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits.
343impl_uint_aliases! {
344    (U64, 64, "64-bit"),
345    (U128, 128, "128-bit"),
346    (U192, 192, "192-bit"),
347    (U256, 256, "256-bit"),
348    (U384, 384, "384-bit"),
349    (U448, 448, "448-bit"),
350    (U512, 512, "512-bit"),
351    (U576, 576, "576-bit"),
352    (U768, 768, "768-bit"),
353    (U896, 896, "896-bit"),
354    (U1024, 1024, "1024-bit"),
355    (U1536, 1536, "1536-bit"),
356    (U1792, 1792, "1792-bit"),
357    (U2048, 2048, "2048-bit"),
358    (U3072, 3072, "3072-bit"),
359    (U3584, 3584, "3584-bit"),
360    (U4096, 4096, "4096-bit"),
361    (U6144, 6144, "6144-bit"),
362    (U8192, 8192, "8192-bit")
363}
364
365// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits.
366impl_concat! {
367    (U64, 64),
368    (U128, 128),
369    (U192, 192),
370    (U256, 256),
371    (U384, 384),
372    (U448, 448),
373    (U512, 512),
374    (U768, 768),
375    (U896, 896),
376    (U1024, 1024),
377    (U1536, 1536),
378    (U1792, 1792),
379    (U2048, 2048),
380    (U3072, 3072),
381    (U4096, 4096)
382}
383
384// TODO(tarcieri): use `const_evaluatable_checked` when stable to make generic around bits.
385impl_split! {
386    (U128, 128),
387    (U192, 192),
388    (U256, 256),
389    (U384, 384),
390    (U448, 448),
391    (U512, 512),
392    (U768, 768),
393    (U896, 896),
394    (U1024, 1024),
395    (U1536, 1536),
396    (U1792, 1792),
397    (U2048, 2048),
398    (U3072, 3072),
399    (U3584, 3584),
400    (U4096, 4096),
401    (U6144, 6144),
402    (U8192, 8192)
403}
404
405#[cfg(test)]
406mod tests {
407    use crate::{Encoding, U128};
408    use subtle::ConditionallySelectable;
409
410    #[cfg(feature = "serde")]
411    use crate::U64;
412
413    #[test]
414    #[cfg(feature = "alloc")]
415    fn display() {
416        let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
417        let n = U128::from_be_hex(hex);
418
419        use alloc::string::ToString;
420        assert_eq!(hex, n.to_string());
421
422        let hex = "AAAAAAAABBBBBBBB0000000000000000";
423        let n = U128::from_be_hex(hex);
424        assert_eq!(hex, n.to_string());
425
426        let hex = "AAAAAAAABBBBBBBB00000000DDDDDDDD";
427        let n = U128::from_be_hex(hex);
428        assert_eq!(hex, n.to_string());
429
430        let hex = "AAAAAAAABBBBBBBB0CCCCCCCDDDDDDDD";
431        let n = U128::from_be_hex(hex);
432        assert_eq!(hex, n.to_string());
433    }
434
435    #[test]
436    fn from_bytes() {
437        let a = U128::from_be_hex("AAAAAAAABBBBBBBB0CCCCCCCDDDDDDDD");
438
439        let be_bytes = a.to_be_bytes();
440        let le_bytes = a.to_le_bytes();
441        for i in 0..16 {
442            assert_eq!(le_bytes[i], be_bytes[15 - i]);
443        }
444
445        let a_from_be = U128::from_be_bytes(be_bytes);
446        let a_from_le = U128::from_le_bytes(le_bytes);
447        assert_eq!(a_from_be, a_from_le);
448        assert_eq!(a_from_be, a);
449    }
450
451    #[test]
452    fn conditional_select() {
453        let a = U128::from_be_hex("00002222444466668888AAAACCCCEEEE");
454        let b = U128::from_be_hex("11113333555577779999BBBBDDDDFFFF");
455
456        let select_0 = U128::conditional_select(&a, &b, 0.into());
457        assert_eq!(a, select_0);
458
459        let select_1 = U128::conditional_select(&a, &b, 1.into());
460        assert_eq!(b, select_1);
461    }
462
463    #[test]
464    #[cfg(feature = "serde")]
465    fn serde() {
466        const TEST: U64 = U64::from_u64(0x0011223344556677);
467
468        let serialized = bincode::serialize(&TEST).unwrap();
469        let deserialized: U64 = bincode::deserialize(&serialized).unwrap();
470
471        assert_eq!(TEST, deserialized);
472    }
473
474    #[test]
475    #[cfg(feature = "serde")]
476    fn serde_owned() {
477        const TEST: U64 = U64::from_u64(0x0011223344556677);
478
479        let serialized = bincode::serialize(&TEST).unwrap();
480        let deserialized: U64 = bincode::deserialize_from(serialized.as_slice()).unwrap();
481
482        assert_eq!(TEST, deserialized);
483    }
484}