crypto_bigint/uint/boxed/
encoding.rs

1//! Const-friendly decoding operations for [`BoxedUint`].
2
3use super::BoxedUint;
4use crate::{uint::encoding, DecodeError, Limb, Word};
5use alloc::{boxed::Box, string::String, vec::Vec};
6use subtle::{Choice, CtOption};
7
8impl BoxedUint {
9    /// Create a new [`BoxedUint`] from the provided big endian bytes.
10    ///
11    /// The `bits_precision` argument represents the precision of the resulting integer, which is
12    /// fixed as this type is not arbitrary-precision.
13    /// The new [`BoxedUint`] will be created with `bits_precision`
14    /// rounded up to a multiple of [`Limb::BITS`].
15    ///
16    /// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8)
17    /// this function will return [`DecodeError::InputSize`].
18    /// If the size of the decoded integer is larger than `bits_precision`,
19    /// this function will return [`DecodeError::Precision`].
20    pub fn from_be_slice(bytes: &[u8], bits_precision: u32) -> Result<Self, DecodeError> {
21        if bytes.is_empty() && bits_precision == 0 {
22            return Ok(Self::zero());
23        }
24
25        if bytes.len() > (bits_precision as usize + 7) / 8 {
26            return Err(DecodeError::InputSize);
27        }
28
29        let mut ret = Self::zero_with_precision(bits_precision);
30
31        for (chunk, limb) in bytes.rchunks(Limb::BYTES).zip(ret.limbs.iter_mut()) {
32            *limb = Limb::from_be_slice(chunk);
33        }
34
35        if bits_precision < ret.bits() {
36            return Err(DecodeError::Precision);
37        }
38
39        Ok(ret)
40    }
41
42    /// Create a new [`BoxedUint`] from the provided little endian bytes.
43    ///
44    /// The `bits_precision` argument represents the precision of the resulting integer, which is
45    /// fixed as this type is not arbitrary-precision.
46    /// The new [`BoxedUint`] will be created with `bits_precision`
47    /// rounded up to a multiple of [`Limb::BITS`].
48    ///
49    /// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8)
50    /// this function will return [`DecodeError::InputSize`].
51    /// If the size of the decoded integer is larger than `bits_precision`,
52    /// this function will return [`DecodeError::Precision`].
53    pub fn from_le_slice(bytes: &[u8], bits_precision: u32) -> Result<Self, DecodeError> {
54        if bytes.is_empty() && bits_precision == 0 {
55            return Ok(Self::zero());
56        }
57
58        if bytes.len() > (bits_precision as usize + 7) / 8 {
59            return Err(DecodeError::InputSize);
60        }
61
62        let mut ret = Self::zero_with_precision(bits_precision);
63
64        for (chunk, limb) in bytes.chunks(Limb::BYTES).zip(ret.limbs.iter_mut()) {
65            *limb = Limb::from_le_slice(chunk);
66        }
67
68        if bits_precision < ret.bits() {
69            return Err(DecodeError::Precision);
70        }
71
72        Ok(ret)
73    }
74
75    /// Serialize this [`BoxedUint`] as big-endian.
76    #[inline]
77    pub fn to_be_bytes(&self) -> Box<[u8]> {
78        let mut out = vec![0u8; self.limbs.len() * Limb::BYTES];
79
80        for (src, dst) in self
81            .limbs
82            .iter()
83            .rev()
84            .cloned()
85            .zip(out.chunks_exact_mut(Limb::BYTES))
86        {
87            dst.copy_from_slice(&src.0.to_be_bytes());
88        }
89
90        out.into()
91    }
92
93    /// Serialize this [`BoxedUint`] as little-endian.
94    #[inline]
95    pub fn to_le_bytes(&self) -> Box<[u8]> {
96        let mut out = vec![0u8; self.limbs.len() * Limb::BYTES];
97
98        for (src, dst) in self
99            .limbs
100            .iter()
101            .cloned()
102            .zip(out.chunks_exact_mut(Limb::BYTES))
103        {
104            dst.copy_from_slice(&src.0.to_le_bytes());
105        }
106
107        out.into()
108    }
109
110    /// Create a new [`BoxedUint`] from the provided big endian hex string.
111    pub fn from_be_hex(hex: &str, bits_precision: u32) -> CtOption<Self> {
112        let nlimbs = (bits_precision / Limb::BITS) as usize;
113        let bytes = hex.as_bytes();
114
115        assert!(
116            bytes.len() == Limb::BYTES * nlimbs * 2,
117            "hex string is not the expected size"
118        );
119        let mut res = vec![Limb::ZERO; nlimbs];
120        let mut buf = [0u8; Limb::BYTES];
121        let mut i = 0;
122        let mut err = 0;
123
124        while i < nlimbs {
125            let mut j = 0;
126            while j < Limb::BYTES {
127                let offset = (i * Limb::BYTES + j) * 2;
128                let (result, byte_err) =
129                    encoding::decode_hex_byte([bytes[offset], bytes[offset + 1]]);
130                err |= byte_err;
131                buf[j] = result;
132                j += 1;
133            }
134            res[nlimbs - i - 1] = Limb(Word::from_be_bytes(buf));
135            i += 1;
136        }
137        CtOption::new(Self { limbs: res.into() }, Choice::from((err == 0) as u8))
138    }
139
140    /// Create a new [`BoxedUint`] from a big-endian string in a given base.
141    ///
142    /// The string may begin with a `+` character, and may use underscore
143    /// characters to separate digits.
144    ///
145    /// If the input value contains non-digit characters or digits outside of the range `0..radix`
146    /// this function will return [`DecodeError::InvalidDigit`].
147    /// Panics if `radix` is not in the range from 2 to 36.
148    pub fn from_str_radix_vartime(src: &str, radix: u32) -> Result<Self, DecodeError> {
149        let mut dec = VecDecodeByLimb::default();
150        encoding::radix_decode_str(src, radix, &mut dec)?;
151        Ok(Self {
152            limbs: dec.limbs.into(),
153        })
154    }
155
156    /// Create a new [`BoxedUint`] from a big-endian string in a given base,
157    /// with a given precision.
158    ///
159    /// The string may begin with a `+` character, and may use underscore
160    /// characters to separate digits.
161    ///
162    /// The `bits_precision` argument represents the precision of the resulting integer, which is
163    /// fixed as this type is not arbitrary-precision.
164    /// The new [`BoxedUint`] will be created with `bits_precision` rounded up to a multiple
165    /// of [`Limb::BITS`].
166    ///
167    /// If the input value contains non-digit characters or digits outside of the range `0..radix`
168    /// this function will return [`DecodeError::InvalidDigit`].
169    /// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8)
170    /// this function will return [`DecodeError::InputSize`].
171    /// If the size of the decoded integer is larger than `bits_precision`,
172    /// this function will return [`DecodeError::Precision`].
173    /// Panics if `radix` is not in the range from 2 to 36.
174    pub fn from_str_radix_with_precision_vartime(
175        src: &str,
176        radix: u32,
177        bits_precision: u32,
178    ) -> Result<Self, DecodeError> {
179        let mut ret = Self::zero_with_precision(bits_precision);
180        encoding::radix_decode_str(
181            src,
182            radix,
183            &mut encoding::SliceDecodeByLimb::new(&mut ret.limbs),
184        )?;
185        if bits_precision < ret.bits() {
186            return Err(DecodeError::Precision);
187        }
188        Ok(ret)
189    }
190
191    /// Format a [`BoxedUint`] as a string in a given base.
192    ///
193    /// Panics if `radix` is not in the range from 2 to 36.
194    pub fn to_string_radix_vartime(&self, radix: u32) -> String {
195        encoding::radix_encode_limbs_to_string(radix, &self.limbs)
196    }
197}
198
199/// Decoder target producing a Vec<Limb>
200#[derive(Default)]
201struct VecDecodeByLimb {
202    limbs: Vec<Limb>,
203}
204
205impl encoding::DecodeByLimb for VecDecodeByLimb {
206    #[inline]
207    fn limbs_mut(&mut self) -> &mut [Limb] {
208        self.limbs.as_mut_slice()
209    }
210
211    #[inline]
212    fn push_limb(&mut self, limb: Limb) -> bool {
213        self.limbs.push(limb);
214        true
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::{BoxedUint, DecodeError};
221    use crate::Limb;
222    use hex_literal::hex;
223
224    #[test]
225    #[cfg(target_pointer_width = "32")]
226    fn from_be_slice_eq() {
227        let bytes = hex!("0011223344556677");
228        let n = BoxedUint::from_be_slice(&bytes, 64).unwrap();
229        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
230    }
231
232    #[test]
233    #[cfg(target_pointer_width = "64")]
234    fn from_be_slice_eq() {
235        let bytes = hex!("00112233445566778899aabbccddeeff");
236        let n = BoxedUint::from_be_slice(&bytes, 128).unwrap();
237        assert_eq!(
238            n.as_limbs(),
239            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
240        );
241    }
242
243    #[test]
244    #[cfg(target_pointer_width = "64")]
245    fn from_be_hex_eq() {
246        let hex = "00112233445566778899aabbccddeeff";
247        let n = BoxedUint::from_be_hex(hex, 128).unwrap();
248        assert_eq!(
249            n.as_limbs(),
250            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
251        );
252    }
253
254    #[test]
255    #[cfg(target_pointer_width = "32")]
256    fn from_be_slice_short() {
257        let bytes = hex!("0011223344556677");
258        let n = BoxedUint::from_be_slice(&bytes, 128).unwrap();
259        assert_eq!(
260            n.as_limbs(),
261            &[Limb(0x44556677), Limb(0x00112233), Limb::ZERO, Limb::ZERO]
262        );
263    }
264
265    #[test]
266    #[cfg(target_pointer_width = "64")]
267    fn from_be_slice_short() {
268        let bytes = hex!("00112233445566778899aabbccddeeff");
269        let n = BoxedUint::from_be_slice(&bytes, 256).unwrap();
270        assert_eq!(
271            n.as_limbs(),
272            &[
273                Limb(0x8899aabbccddeeff),
274                Limb(0x0011223344556677),
275                Limb::ZERO,
276                Limb::ZERO
277            ]
278        );
279    }
280
281    #[test]
282    fn from_be_slice_too_long() {
283        let bytes = hex!("00112233445566778899aabbccddeeff");
284        assert_eq!(
285            BoxedUint::from_be_slice(&bytes, 64),
286            Err(DecodeError::InputSize)
287        );
288    }
289
290    #[test]
291    #[cfg(target_pointer_width = "32")]
292    fn from_be_slice_not_word_sized() {
293        let bytes = hex!("112233445566778899aabbccddeeff");
294        let n = BoxedUint::from_be_slice(&bytes, 127).unwrap();
295        assert_eq!(
296            n.as_limbs(),
297            &[
298                Limb(0xccddeeff),
299                Limb(0x8899aabb),
300                Limb(0x44556677),
301                Limb(0x00112233)
302            ]
303        );
304        assert_eq!(n.bits_precision(), 128);
305    }
306
307    #[test]
308    #[cfg(target_pointer_width = "64")]
309    fn from_be_slice_not_word_sized() {
310        let bytes = hex!("112233445566778899aabbccddeeff");
311        let n = BoxedUint::from_be_slice(&bytes, 127).unwrap();
312        assert_eq!(
313            n.as_limbs(),
314            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
315        );
316        assert_eq!(n.bits_precision(), 128);
317    }
318
319    #[test]
320    fn from_be_slice_non_multiple_precision() {
321        let bytes = hex!("0f112233445566778899aabbccddeeff");
322        assert_eq!(
323            BoxedUint::from_be_slice(&bytes, 121),
324            Err(DecodeError::Precision)
325        );
326    }
327
328    #[test]
329    #[cfg(target_pointer_width = "32")]
330    fn from_le_slice_eq() {
331        let bytes = hex!("7766554433221100");
332        let n = BoxedUint::from_le_slice(&bytes, 64).unwrap();
333        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
334    }
335
336    #[test]
337    #[cfg(target_pointer_width = "64")]
338    fn from_le_slice_eq() {
339        let bytes = hex!("ffeeddccbbaa99887766554433221100");
340        let n = BoxedUint::from_le_slice(&bytes, 128).unwrap();
341        assert_eq!(
342            n.as_limbs(),
343            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
344        );
345    }
346
347    #[test]
348    #[cfg(target_pointer_width = "32")]
349    fn from_le_slice_short() {
350        let bytes = hex!("7766554433221100");
351        let n = BoxedUint::from_le_slice(&bytes, 128).unwrap();
352        assert_eq!(
353            n.as_limbs(),
354            &[Limb(0x44556677), Limb(0x00112233), Limb::ZERO, Limb::ZERO]
355        );
356    }
357
358    #[test]
359    #[cfg(target_pointer_width = "64")]
360    fn from_le_slice_short() {
361        let bytes = hex!("ffeeddccbbaa99887766554433221100");
362        let n = BoxedUint::from_le_slice(&bytes, 256).unwrap();
363        assert_eq!(
364            n.as_limbs(),
365            &[
366                Limb(0x8899aabbccddeeff),
367                Limb(0x0011223344556677),
368                Limb::ZERO,
369                Limb::ZERO
370            ]
371        );
372    }
373
374    #[test]
375    fn from_le_slice_too_long() {
376        let bytes = hex!("ffeeddccbbaa99887766554433221100");
377        assert_eq!(
378            BoxedUint::from_be_slice(&bytes, 64),
379            Err(DecodeError::InputSize)
380        );
381    }
382
383    #[test]
384    #[cfg(target_pointer_width = "32")]
385    fn from_le_slice_not_word_sized() {
386        let bytes = hex!("ffeeddccbbaa998877665544332211");
387        let n = BoxedUint::from_le_slice(&bytes, 127).unwrap();
388        assert_eq!(
389            n.as_limbs(),
390            &[
391                Limb(0xccddeeff),
392                Limb(0x8899aabb),
393                Limb(0x44556677),
394                Limb(0x00112233)
395            ]
396        );
397        assert_eq!(n.bits_precision(), 128);
398    }
399
400    #[test]
401    #[cfg(target_pointer_width = "64")]
402    fn from_le_slice_not_word_sized() {
403        let bytes = hex!("ffeeddccbbaa998877665544332211");
404        let n = BoxedUint::from_le_slice(&bytes, 127).unwrap();
405        assert_eq!(
406            n.as_limbs(),
407            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
408        );
409        assert_eq!(n.bits_precision(), 128);
410    }
411
412    #[test]
413    fn from_le_slice_non_multiple_precision() {
414        let bytes = hex!("ffeeddccbbaa998877665544332211f0");
415        assert_eq!(
416            BoxedUint::from_le_slice(&bytes, 121),
417            Err(DecodeError::Precision)
418        );
419    }
420
421    #[test]
422    fn to_be_bytes() {
423        let bytes = hex!("00112233445566778899aabbccddeeff");
424        let n = BoxedUint::from_be_slice(&bytes, 128).unwrap();
425        assert_eq!(bytes.as_slice(), &*n.to_be_bytes());
426    }
427
428    #[test]
429    fn to_le_bytes() {
430        let bytes = hex!("ffeeddccbbaa99887766554433221100");
431        let n = BoxedUint::from_be_slice(&bytes, 128).unwrap();
432        assert_eq!(bytes.as_slice(), &*n.to_be_bytes());
433    }
434
435    #[test]
436    fn from_str_radix_invalid() {
437        assert_eq!(
438            BoxedUint::from_str_radix_vartime("?", 10,),
439            Err(DecodeError::InvalidDigit)
440        );
441        assert_eq!(
442            BoxedUint::from_str_radix_with_precision_vartime(
443                "ffffffffffffffff_ffffffffffffffff_f",
444                16,
445                128
446            ),
447            Err(DecodeError::InputSize)
448        );
449        assert_eq!(
450            BoxedUint::from_str_radix_with_precision_vartime("1111111111111111", 2, 10),
451            Err(DecodeError::Precision)
452        );
453    }
454
455    #[test]
456    fn from_str_radix_10() {
457        let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455";
458        let res = BoxedUint::from_str_radix_vartime(dec, 10).expect("error decoding");
459        assert_eq!(res, BoxedUint::max(128));
460    }
461
462    #[test]
463    fn from_str_radix_16() {
464        let hex = "fedcba9876543210fedcba9876543210";
465        let res = BoxedUint::from_str_radix_vartime(hex, 16).expect("error decoding");
466        assert_eq!(hex, format!("{res:x}"));
467    }
468
469    #[test]
470    #[cfg(feature = "rand_core")]
471    fn encode_radix_round_trip() {
472        use crate::RandomBits;
473        use rand_core::SeedableRng;
474        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1);
475
476        for _ in 0..100 {
477            let uint = BoxedUint::random_bits(&mut rng, 4096);
478            for radix in 2..=36 {
479                let enc = uint.to_string_radix_vartime(radix);
480                let res = BoxedUint::from_str_radix_vartime(&enc, radix).expect("decoding error");
481                assert_eq!(
482                    res, uint,
483                    "round trip failure: radix {radix} encoded {uint} as {enc}"
484                );
485            }
486        }
487    }
488}