• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 David Judd.
2 // Copyright 2016 Brian Smith.
3 //
4 // Permission to use, copy, modify, and/or distribute this software for any
5 // purpose with or without fee is hereby granted, provided that the above
6 // copyright notice and this permission notice appear in all copies.
7 //
8 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
9 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
11 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 
16 //! Unsigned multi-precision integer arithmetic.
17 //!
18 //! Limbs ordered least-significant-limb to most-significant-limb. The bits
19 //! limbs use the native endianness.
20 
21 use crate::{c, error};
22 
23 #[cfg(feature = "alloc")]
24 use crate::bits;
25 
26 #[cfg(feature = "alloc")]
27 use core::num::Wrapping;
28 
29 // XXX: Not correct for x32 ABIs.
30 #[cfg(target_pointer_width = "64")]
31 pub type Limb = u64;
32 #[cfg(target_pointer_width = "32")]
33 pub type Limb = u32;
34 #[cfg(target_pointer_width = "64")]
35 pub const LIMB_BITS: usize = 64;
36 #[cfg(target_pointer_width = "32")]
37 pub const LIMB_BITS: usize = 32;
38 
39 #[cfg(target_pointer_width = "64")]
40 #[derive(Debug, PartialEq)]
41 #[repr(u64)]
42 pub enum LimbMask {
43     True = 0xffff_ffff_ffff_ffff,
44     False = 0,
45 }
46 
47 #[cfg(target_pointer_width = "32")]
48 #[derive(Debug, PartialEq)]
49 #[repr(u32)]
50 pub enum LimbMask {
51     True = 0xffff_ffff,
52     False = 0,
53 }
54 
55 pub const LIMB_BYTES: usize = (LIMB_BITS + 7) / 8;
56 
57 #[inline]
limbs_equal_limbs_consttime(a: &[Limb], b: &[Limb]) -> LimbMask58 pub fn limbs_equal_limbs_consttime(a: &[Limb], b: &[Limb]) -> LimbMask {
59     prefixed_extern! {
60         fn LIMBS_equal(a: *const Limb, b: *const Limb, num_limbs: c::size_t) -> LimbMask;
61     }
62 
63     assert_eq!(a.len(), b.len());
64     unsafe { LIMBS_equal(a.as_ptr(), b.as_ptr(), a.len()) }
65 }
66 
67 #[inline]
limbs_less_than_limbs_consttime(a: &[Limb], b: &[Limb]) -> LimbMask68 pub fn limbs_less_than_limbs_consttime(a: &[Limb], b: &[Limb]) -> LimbMask {
69     assert_eq!(a.len(), b.len());
70     unsafe { LIMBS_less_than(a.as_ptr(), b.as_ptr(), b.len()) }
71 }
72 
73 #[inline]
limbs_less_than_limbs_vartime(a: &[Limb], b: &[Limb]) -> bool74 pub fn limbs_less_than_limbs_vartime(a: &[Limb], b: &[Limb]) -> bool {
75     limbs_less_than_limbs_consttime(a, b) == LimbMask::True
76 }
77 
78 #[inline]
79 #[cfg(feature = "alloc")]
limbs_less_than_limb_constant_time(a: &[Limb], b: Limb) -> LimbMask80 pub fn limbs_less_than_limb_constant_time(a: &[Limb], b: Limb) -> LimbMask {
81     unsafe { LIMBS_less_than_limb(a.as_ptr(), b, a.len()) }
82 }
83 
84 #[inline]
limbs_are_zero_constant_time(limbs: &[Limb]) -> LimbMask85 pub fn limbs_are_zero_constant_time(limbs: &[Limb]) -> LimbMask {
86     unsafe { LIMBS_are_zero(limbs.as_ptr(), limbs.len()) }
87 }
88 
89 #[cfg(feature = "alloc")]
90 #[inline]
limbs_are_even_constant_time(limbs: &[Limb]) -> LimbMask91 pub fn limbs_are_even_constant_time(limbs: &[Limb]) -> LimbMask {
92     unsafe { LIMBS_are_even(limbs.as_ptr(), limbs.len()) }
93 }
94 
95 #[cfg(feature = "alloc")]
96 #[inline]
limbs_equal_limb_constant_time(a: &[Limb], b: Limb) -> LimbMask97 pub fn limbs_equal_limb_constant_time(a: &[Limb], b: Limb) -> LimbMask {
98     unsafe { LIMBS_equal_limb(a.as_ptr(), b, a.len()) }
99 }
100 
101 /// Returns the number of bits in `a`.
102 //
103 // This strives to be constant-time with respect to the values of all bits
104 // except the most significant bit. This does not attempt to be constant-time
105 // with respect to `a.len()` or the value of the result or the value of the
106 // most significant bit (It's 1, unless the input is zero, in which case it's
107 // zero.)
108 #[cfg(feature = "alloc")]
limbs_minimal_bits(a: &[Limb]) -> bits::BitLength109 pub fn limbs_minimal_bits(a: &[Limb]) -> bits::BitLength {
110     for num_limbs in (1..=a.len()).rev() {
111         let high_limb = a[num_limbs - 1];
112 
113         // Find the number of set bits in |high_limb| by a linear scan from the
114         // most significant bit to the least significant bit. This works great
115         // for the most common inputs because usually the most significant bit
116         // it set.
117         for high_limb_num_bits in (1..=LIMB_BITS).rev() {
118             let shifted = unsafe { LIMB_shr(high_limb, high_limb_num_bits - 1) };
119             if shifted != 0 {
120                 return bits::BitLength::from_usize_bits(
121                     ((num_limbs - 1) * LIMB_BITS) + high_limb_num_bits,
122                 );
123             }
124         }
125     }
126 
127     // No bits were set.
128     bits::BitLength::from_usize_bits(0)
129 }
130 
131 /// Equivalent to `if (r >= m) { r -= m; }`
132 #[inline]
limbs_reduce_once_constant_time(r: &mut [Limb], m: &[Limb])133 pub fn limbs_reduce_once_constant_time(r: &mut [Limb], m: &[Limb]) {
134     assert_eq!(r.len(), m.len());
135     unsafe { LIMBS_reduce_once(r.as_mut_ptr(), m.as_ptr(), m.len()) };
136 }
137 
138 #[derive(Clone, Copy, PartialEq)]
139 pub enum AllowZero {
140     No,
141     Yes,
142 }
143 
144 /// Parses `input` into `result`, reducing it via conditional subtraction
145 /// (mod `m`). Assuming 2**((self.num_limbs * LIMB_BITS) - 1) < m and
146 /// m < 2**(self.num_limbs * LIMB_BITS), the value will be reduced mod `m` in
147 /// constant time so that the result is in the range [0, m) if `allow_zero` is
148 /// `AllowZero::Yes`, or [1, m) if `allow_zero` is `AllowZero::No`. `result` is
149 /// padded with zeros to its length.
parse_big_endian_in_range_partially_reduced_and_pad_consttime( input: untrusted::Input, allow_zero: AllowZero, m: &[Limb], result: &mut [Limb], ) -> Result<(), error::Unspecified>150 pub fn parse_big_endian_in_range_partially_reduced_and_pad_consttime(
151     input: untrusted::Input,
152     allow_zero: AllowZero,
153     m: &[Limb],
154     result: &mut [Limb],
155 ) -> Result<(), error::Unspecified> {
156     parse_big_endian_and_pad_consttime(input, result)?;
157     limbs_reduce_once_constant_time(result, m);
158     if allow_zero != AllowZero::Yes {
159         if limbs_are_zero_constant_time(&result) != LimbMask::False {
160             return Err(error::Unspecified);
161         }
162     }
163     Ok(())
164 }
165 
166 /// Parses `input` into `result`, verifies that the value is less than
167 /// `max_exclusive`, and pads `result` with zeros to its length. If `allow_zero`
168 /// is not `AllowZero::Yes`, zero values are rejected.
169 ///
170 /// This attempts to be constant-time with respect to the actual value *only if*
171 /// the value is actually in range. In other words, this won't leak anything
172 /// about a valid value, but it might leak small amounts of information about an
173 /// invalid value (which constraint it failed).
parse_big_endian_in_range_and_pad_consttime( input: untrusted::Input, allow_zero: AllowZero, max_exclusive: &[Limb], result: &mut [Limb], ) -> Result<(), error::Unspecified>174 pub fn parse_big_endian_in_range_and_pad_consttime(
175     input: untrusted::Input,
176     allow_zero: AllowZero,
177     max_exclusive: &[Limb],
178     result: &mut [Limb],
179 ) -> Result<(), error::Unspecified> {
180     parse_big_endian_and_pad_consttime(input, result)?;
181     if limbs_less_than_limbs_consttime(&result, max_exclusive) != LimbMask::True {
182         return Err(error::Unspecified);
183     }
184     if allow_zero != AllowZero::Yes {
185         if limbs_are_zero_constant_time(&result) != LimbMask::False {
186             return Err(error::Unspecified);
187         }
188     }
189     Ok(())
190 }
191 
192 /// Parses `input` into `result`, padding `result` with zeros to its length.
193 /// This attempts to be constant-time with respect to the value but not with
194 /// respect to the length; it is assumed that the length is public knowledge.
parse_big_endian_and_pad_consttime( input: untrusted::Input, result: &mut [Limb], ) -> Result<(), error::Unspecified>195 pub fn parse_big_endian_and_pad_consttime(
196     input: untrusted::Input,
197     result: &mut [Limb],
198 ) -> Result<(), error::Unspecified> {
199     if input.is_empty() {
200         return Err(error::Unspecified);
201     }
202 
203     // `bytes_in_current_limb` is the number of bytes in the current limb.
204     // It will be `LIMB_BYTES` for all limbs except maybe the highest-order
205     // limb.
206     let mut bytes_in_current_limb = input.len() % LIMB_BYTES;
207     if bytes_in_current_limb == 0 {
208         bytes_in_current_limb = LIMB_BYTES;
209     }
210 
211     let num_encoded_limbs = (input.len() / LIMB_BYTES)
212         + (if bytes_in_current_limb == LIMB_BYTES {
213             0
214         } else {
215             1
216         });
217     if num_encoded_limbs > result.len() {
218         return Err(error::Unspecified);
219     }
220 
221     for r in &mut result[..] {
222         *r = 0;
223     }
224 
225     // XXX: Questionable as far as constant-timedness is concerned.
226     // TODO: Improve this.
227     input.read_all(error::Unspecified, |input| {
228         for i in 0..num_encoded_limbs {
229             let mut limb: Limb = 0;
230             for _ in 0..bytes_in_current_limb {
231                 let b: Limb = input.read_byte()?.into();
232                 limb = (limb << 8) | b;
233             }
234             result[num_encoded_limbs - i - 1] = limb;
235             bytes_in_current_limb = LIMB_BYTES;
236         }
237         Ok(())
238     })
239 }
240 
big_endian_from_limbs(limbs: &[Limb], out: &mut [u8])241 pub fn big_endian_from_limbs(limbs: &[Limb], out: &mut [u8]) {
242     let num_limbs = limbs.len();
243     let out_len = out.len();
244     assert_eq!(out_len, num_limbs * LIMB_BYTES);
245     for i in 0..num_limbs {
246         let mut limb = limbs[i];
247         for j in 0..LIMB_BYTES {
248             out[((num_limbs - i - 1) * LIMB_BYTES) + (LIMB_BYTES - j - 1)] = (limb & 0xff) as u8;
249             limb >>= 8;
250         }
251     }
252 }
253 
254 #[cfg(feature = "alloc")]
255 pub type Window = Limb;
256 
257 /// Processes `limbs` as a sequence of 5-bit windows, folding the windows from
258 /// most significant to least significant and returning the accumulated result.
259 /// The first window will be mapped by `init` to produce the initial value for
260 /// the accumulator. Then `f` will be called to fold the accumulator and the
261 /// next window until all windows are processed. When the input's bit length
262 /// isn't divisible by 5, the window passed to `init` will be partial; all
263 /// windows passed to `fold` will be full.
264 ///
265 /// This is designed to avoid leaking the contents of `limbs` through side
266 /// channels as long as `init` and `fold` are side-channel free.
267 ///
268 /// Panics if `limbs` is empty.
269 #[cfg(feature = "alloc")]
fold_5_bit_windows<R, I: FnOnce(Window) -> R, F: Fn(R, Window) -> R>( limbs: &[Limb], init: I, fold: F, ) -> R270 pub fn fold_5_bit_windows<R, I: FnOnce(Window) -> R, F: Fn(R, Window) -> R>(
271     limbs: &[Limb],
272     init: I,
273     fold: F,
274 ) -> R {
275     #[derive(Clone, Copy)]
276     #[repr(transparent)]
277     struct BitIndex(Wrapping<c::size_t>);
278 
279     const WINDOW_BITS: Wrapping<c::size_t> = Wrapping(5);
280 
281     prefixed_extern! {
282         fn LIMBS_window5_split_window(
283             lower_limb: Limb,
284             higher_limb: Limb,
285             index_within_word: BitIndex,
286         ) -> Window;
287         fn LIMBS_window5_unsplit_window(limb: Limb, index_within_word: BitIndex) -> Window;
288     }
289 
290     let num_limbs = limbs.len();
291     let mut window_low_bit = {
292         let num_whole_windows = (num_limbs * LIMB_BITS) / 5;
293         let mut leading_bits = (num_limbs * LIMB_BITS) - (num_whole_windows * 5);
294         if leading_bits == 0 {
295             leading_bits = WINDOW_BITS.0;
296         }
297         BitIndex(Wrapping(LIMB_BITS - leading_bits))
298     };
299 
300     let initial_value = {
301         let leading_partial_window =
302             unsafe { LIMBS_window5_split_window(*limbs.last().unwrap(), 0, window_low_bit) };
303         window_low_bit.0 -= WINDOW_BITS;
304         init(leading_partial_window)
305     };
306 
307     let mut low_limb = 0;
308     limbs
309         .iter()
310         .rev()
311         .fold(initial_value, |mut acc, current_limb| {
312             let higher_limb = low_limb;
313             low_limb = *current_limb;
314 
315             if window_low_bit.0 > Wrapping(LIMB_BITS) - WINDOW_BITS {
316                 let window =
317                     unsafe { LIMBS_window5_split_window(low_limb, higher_limb, window_low_bit) };
318                 window_low_bit.0 -= WINDOW_BITS;
319                 acc = fold(acc, window);
320             };
321             while window_low_bit.0 < Wrapping(LIMB_BITS) {
322                 let window = unsafe { LIMBS_window5_unsplit_window(low_limb, window_low_bit) };
323                 // The loop exits when this subtraction underflows, causing `window_low_bit` to
324                 // wrap around to a very large value.
325                 window_low_bit.0 -= WINDOW_BITS;
326                 acc = fold(acc, window);
327             }
328             window_low_bit.0 += Wrapping(LIMB_BITS); // "Fix" the underflow.
329 
330             acc
331         })
332 }
333 
334 #[inline]
limbs_add_assign_mod(a: &mut [Limb], b: &[Limb], m: &[Limb])335 pub(crate) fn limbs_add_assign_mod(a: &mut [Limb], b: &[Limb], m: &[Limb]) {
336     debug_assert_eq!(a.len(), m.len());
337     debug_assert_eq!(b.len(), m.len());
338     prefixed_extern! {
339         // `r` and `a` may alias.
340         fn LIMBS_add_mod(
341             r: *mut Limb,
342             a: *const Limb,
343             b: *const Limb,
344             m: *const Limb,
345             num_limbs: c::size_t,
346         );
347     }
348     unsafe { LIMBS_add_mod(a.as_mut_ptr(), a.as_ptr(), b.as_ptr(), m.as_ptr(), m.len()) }
349 }
350 
351 prefixed_extern! {
352     fn LIMBS_are_zero(a: *const Limb, num_limbs: c::size_t) -> LimbMask;
353     fn LIMBS_less_than(a: *const Limb, b: *const Limb, num_limbs: c::size_t) -> LimbMask;
354     fn LIMBS_reduce_once(r: *mut Limb, m: *const Limb, num_limbs: c::size_t);
355 }
356 
357 #[cfg(feature = "alloc")]
358 prefixed_extern! {
359     fn LIMB_shr(a: Limb, shift: c::size_t) -> Limb;
360     fn LIMBS_are_even(a: *const Limb, num_limbs: c::size_t) -> LimbMask;
361     fn LIMBS_equal_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask;
362     fn LIMBS_less_than_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask;
363 }
364 
365 #[cfg(test)]
366 mod tests {
367     use super::*;
368 
369     const MAX: Limb = LimbMask::True as Limb;
370 
371     #[test]
test_limbs_are_even()372     fn test_limbs_are_even() {
373         static EVENS: &[&[Limb]] = &[
374             &[],
375             &[0],
376             &[2],
377             &[0, 0],
378             &[2, 0],
379             &[0, 1],
380             &[0, 2],
381             &[0, 3],
382             &[0, 0, 0, 0, MAX],
383         ];
384         for even in EVENS {
385             assert_eq!(limbs_are_even_constant_time(even), LimbMask::True);
386         }
387         static ODDS: &[&[Limb]] = &[
388             &[1],
389             &[3],
390             &[1, 0],
391             &[3, 0],
392             &[1, 1],
393             &[1, 2],
394             &[1, 3],
395             &[1, 0, 0, 0, MAX],
396         ];
397         for odd in ODDS {
398             assert_eq!(limbs_are_even_constant_time(odd), LimbMask::False);
399         }
400     }
401 
402     static ZEROES: &[&[Limb]] = &[
403         &[],
404         &[0],
405         &[0, 0],
406         &[0, 0, 0],
407         &[0, 0, 0, 0],
408         &[0, 0, 0, 0, 0],
409         &[0, 0, 0, 0, 0, 0, 0],
410         &[0, 0, 0, 0, 0, 0, 0, 0],
411         &[0, 0, 0, 0, 0, 0, 0, 0, 0],
412     ];
413 
414     static NONZEROES: &[&[Limb]] = &[
415         &[1],
416         &[0, 1],
417         &[1, 1],
418         &[1, 0, 0, 0],
419         &[0, 1, 0, 0],
420         &[0, 0, 1, 0],
421         &[0, 0, 0, 1],
422     ];
423 
424     #[test]
test_limbs_are_zero()425     fn test_limbs_are_zero() {
426         for zero in ZEROES {
427             assert_eq!(limbs_are_zero_constant_time(zero), LimbMask::True);
428         }
429         for nonzero in NONZEROES {
430             assert_eq!(limbs_are_zero_constant_time(nonzero), LimbMask::False);
431         }
432     }
433 
434     #[test]
test_limbs_equal_limb()435     fn test_limbs_equal_limb() {
436         for zero in ZEROES {
437             assert_eq!(limbs_equal_limb_constant_time(zero, 0), LimbMask::True);
438         }
439         for nonzero in NONZEROES {
440             assert_eq!(limbs_equal_limb_constant_time(nonzero, 0), LimbMask::False);
441         }
442         static EQUAL: &[(&[Limb], Limb)] = &[
443             (&[1], 1),
444             (&[MAX], MAX),
445             (&[1, 0], 1),
446             (&[MAX, 0, 0], MAX),
447             (&[0b100], 0b100),
448             (&[0b100, 0], 0b100),
449         ];
450         for &(a, b) in EQUAL {
451             assert_eq!(limbs_equal_limb_constant_time(a, b), LimbMask::True);
452         }
453         static UNEQUAL: &[(&[Limb], Limb)] = &[
454             (&[0], 1),
455             (&[2], 1),
456             (&[3], 1),
457             (&[1, 1], 1),
458             (&[0b100, 0b100], 0b100),
459             (&[1, 0, 0b100, 0, 0, 0, 0, 0], 1),
460             (&[1, 0, 0, 0, 0, 0, 0, 0b100], 1),
461             (&[MAX, MAX], MAX),
462             (&[MAX, 1], MAX),
463         ];
464         for &(a, b) in UNEQUAL {
465             assert_eq!(limbs_equal_limb_constant_time(a, b), LimbMask::False);
466         }
467     }
468 
469     #[test]
470     #[cfg(feature = "alloc")]
test_limbs_less_than_limb_constant_time()471     fn test_limbs_less_than_limb_constant_time() {
472         static LESSER: &[(&[Limb], Limb)] = &[
473             (&[0], 1),
474             (&[0, 0], 1),
475             (&[1, 0], 2),
476             (&[2, 0], 3),
477             (&[2, 0], 3),
478             (&[MAX - 1], MAX),
479             (&[MAX - 1, 0], MAX),
480         ];
481         for &(a, b) in LESSER {
482             assert_eq!(limbs_less_than_limb_constant_time(a, b), LimbMask::True);
483         }
484         static EQUAL: &[(&[Limb], Limb)] = &[
485             (&[0], 0),
486             (&[0, 0, 0, 0], 0),
487             (&[1], 1),
488             (&[1, 0, 0, 0, 0, 0, 0], 1),
489             (&[MAX], MAX),
490         ];
491         static GREATER: &[(&[Limb], Limb)] = &[
492             (&[1], 0),
493             (&[2, 0], 1),
494             (&[3, 0, 0, 0], 1),
495             (&[0, 1, 0, 0], 1),
496             (&[0, 0, 1, 0], 1),
497             (&[0, 0, 1, 1], 1),
498             (&[MAX], MAX - 1),
499         ];
500         for &(a, b) in EQUAL.iter().chain(GREATER.iter()) {
501             assert_eq!(limbs_less_than_limb_constant_time(a, b), LimbMask::False);
502         }
503     }
504 
505     #[test]
test_parse_big_endian_and_pad_consttime()506     fn test_parse_big_endian_and_pad_consttime() {
507         const LIMBS: usize = 4;
508 
509         {
510             // Empty input.
511             let inp = untrusted::Input::from(&[]);
512             let mut result = [0; LIMBS];
513             assert!(parse_big_endian_and_pad_consttime(inp, &mut result).is_err());
514         }
515 
516         // The input is longer than will fit in the given number of limbs.
517         {
518             let inp = [1, 2, 3, 4, 5, 6, 7, 8, 9];
519             let inp = untrusted::Input::from(&inp);
520             let mut result = [0; 8 / LIMB_BYTES];
521             assert!(parse_big_endian_and_pad_consttime(inp, &mut result[..]).is_err());
522         }
523 
524         // Less than a full limb.
525         {
526             let inp = [0xfe];
527             let inp = untrusted::Input::from(&inp);
528             let mut result = [0; LIMBS];
529             assert_eq!(
530                 Ok(()),
531                 parse_big_endian_and_pad_consttime(inp, &mut result[..])
532             );
533             assert_eq!(&[0xfe, 0, 0, 0], &result);
534         }
535 
536         // A whole limb for 32-bit, half a limb for 64-bit.
537         {
538             let inp = [0xbe, 0xef, 0xf0, 0x0d];
539             let inp = untrusted::Input::from(&inp);
540             let mut result = [0; LIMBS];
541             assert_eq!(Ok(()), parse_big_endian_and_pad_consttime(inp, &mut result));
542             assert_eq!(&[0xbeeff00d, 0, 0, 0], &result);
543         }
544 
545         // XXX: This is a weak set of tests. TODO: expand it.
546     }
547 
548     #[test]
test_big_endian_from_limbs_same_length()549     fn test_big_endian_from_limbs_same_length() {
550         #[cfg(target_pointer_width = "32")]
551         let limbs = [
552             0xbccddeef, 0x89900aab, 0x45566778, 0x01122334, 0xddeeff00, 0x99aabbcc, 0x55667788,
553             0x11223344,
554         ];
555 
556         #[cfg(target_pointer_width = "64")]
557         let limbs = [
558             0x8990_0aab_bccd_deef,
559             0x0112_2334_4556_6778,
560             0x99aa_bbcc_ddee_ff00,
561             0x1122_3344_5566_7788,
562         ];
563 
564         let expected = [
565             0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
566             0xff, 0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x90, 0x0a, 0xab,
567             0xbc, 0xcd, 0xde, 0xef,
568         ];
569 
570         let mut out = [0xabu8; 32];
571         big_endian_from_limbs(&limbs[..], &mut out);
572         assert_eq!(&out[..], &expected[..]);
573     }
574 
575     #[should_panic]
576     #[test]
test_big_endian_from_limbs_fewer_limbs()577     fn test_big_endian_from_limbs_fewer_limbs() {
578         #[cfg(target_pointer_width = "32")]
579         // Two fewer limbs.
580         let limbs = [
581             0xbccddeef, 0x89900aab, 0x45566778, 0x01122334, 0xddeeff00, 0x99aabbcc,
582         ];
583 
584         // One fewer limb.
585         #[cfg(target_pointer_width = "64")]
586         let limbs = [
587             0x8990_0aab_bccd_deef,
588             0x0112_2334_4556_6778,
589             0x99aa_bbcc_ddee_ff00,
590         ];
591 
592         let mut out = [0xabu8; 32];
593 
594         big_endian_from_limbs(&limbs[..], &mut out);
595     }
596 
597     #[test]
test_limbs_minimal_bits()598     fn test_limbs_minimal_bits() {
599         const ALL_ONES: Limb = LimbMask::True as Limb;
600         static CASES: &[(&[Limb], usize)] = &[
601             (&[], 0),
602             (&[0], 0),
603             (&[ALL_ONES], LIMB_BITS),
604             (&[ALL_ONES, 0], LIMB_BITS),
605             (&[ALL_ONES, 1], LIMB_BITS + 1),
606             (&[0, 0], 0),
607             (&[1, 0], 1),
608             (&[0, 1], LIMB_BITS + 1),
609             (&[0, ALL_ONES], 2 * LIMB_BITS),
610             (&[ALL_ONES, ALL_ONES], 2 * LIMB_BITS),
611             (&[ALL_ONES, ALL_ONES >> 1], 2 * LIMB_BITS - 1),
612             (&[ALL_ONES, 0b100_0000], LIMB_BITS + 7),
613             (&[ALL_ONES, 0b101_0000], LIMB_BITS + 7),
614             (&[ALL_ONES, ALL_ONES >> 1], LIMB_BITS + (LIMB_BITS) - 1),
615         ];
616         for (limbs, bits) in CASES {
617             assert_eq!(limbs_minimal_bits(limbs).as_usize_bits(), *bits);
618         }
619     }
620 }
621