• 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     extern "C" {
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     extern "C" {
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 extern "C" {
335     #[cfg(feature = "alloc")]
LIMB_shr(a: Limb, shift: c::size_t) -> Limb336     fn LIMB_shr(a: Limb, shift: c::size_t) -> Limb;
337 
338     #[cfg(feature = "alloc")]
LIMBS_are_even(a: *const Limb, num_limbs: c::size_t) -> LimbMask339     fn LIMBS_are_even(a: *const Limb, num_limbs: c::size_t) -> LimbMask;
LIMBS_are_zero(a: *const Limb, num_limbs: c::size_t) -> LimbMask340     fn LIMBS_are_zero(a: *const Limb, num_limbs: c::size_t) -> LimbMask;
341     #[cfg(feature = "alloc")]
LIMBS_equal_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask342     fn LIMBS_equal_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask;
LIMBS_less_than(a: *const Limb, b: *const Limb, num_limbs: c::size_t) -> LimbMask343     fn LIMBS_less_than(a: *const Limb, b: *const Limb, num_limbs: c::size_t) -> LimbMask;
344     #[cfg(feature = "alloc")]
LIMBS_less_than_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask345     fn LIMBS_less_than_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask;
LIMBS_reduce_once(r: *mut Limb, m: *const Limb, num_limbs: c::size_t)346     fn LIMBS_reduce_once(r: *mut Limb, m: *const Limb, num_limbs: c::size_t);
347 }
348 
349 #[cfg(test)]
350 mod tests {
351     use super::*;
352 
353     const MAX: Limb = LimbMask::True as Limb;
354 
355     #[test]
test_limbs_are_even()356     fn test_limbs_are_even() {
357         static EVENS: &[&[Limb]] = &[
358             &[],
359             &[0],
360             &[2],
361             &[0, 0],
362             &[2, 0],
363             &[0, 1],
364             &[0, 2],
365             &[0, 3],
366             &[0, 0, 0, 0, MAX],
367         ];
368         for even in EVENS {
369             assert_eq!(limbs_are_even_constant_time(even), LimbMask::True);
370         }
371         static ODDS: &[&[Limb]] = &[
372             &[1],
373             &[3],
374             &[1, 0],
375             &[3, 0],
376             &[1, 1],
377             &[1, 2],
378             &[1, 3],
379             &[1, 0, 0, 0, MAX],
380         ];
381         for odd in ODDS {
382             assert_eq!(limbs_are_even_constant_time(odd), LimbMask::False);
383         }
384     }
385 
386     static ZEROES: &[&[Limb]] = &[
387         &[],
388         &[0],
389         &[0, 0],
390         &[0, 0, 0],
391         &[0, 0, 0, 0],
392         &[0, 0, 0, 0, 0],
393         &[0, 0, 0, 0, 0, 0, 0],
394         &[0, 0, 0, 0, 0, 0, 0, 0],
395         &[0, 0, 0, 0, 0, 0, 0, 0, 0],
396     ];
397 
398     static NONZEROES: &[&[Limb]] = &[
399         &[1],
400         &[0, 1],
401         &[1, 1],
402         &[1, 0, 0, 0],
403         &[0, 1, 0, 0],
404         &[0, 0, 1, 0],
405         &[0, 0, 0, 1],
406     ];
407 
408     #[test]
test_limbs_are_zero()409     fn test_limbs_are_zero() {
410         for zero in ZEROES {
411             assert_eq!(limbs_are_zero_constant_time(zero), LimbMask::True);
412         }
413         for nonzero in NONZEROES {
414             assert_eq!(limbs_are_zero_constant_time(nonzero), LimbMask::False);
415         }
416     }
417 
418     #[test]
test_limbs_equal_limb()419     fn test_limbs_equal_limb() {
420         for zero in ZEROES {
421             assert_eq!(limbs_equal_limb_constant_time(zero, 0), LimbMask::True);
422         }
423         for nonzero in NONZEROES {
424             assert_eq!(limbs_equal_limb_constant_time(nonzero, 0), LimbMask::False);
425         }
426         static EQUAL: &[(&[Limb], Limb)] = &[
427             (&[1], 1),
428             (&[MAX], MAX),
429             (&[1, 0], 1),
430             (&[MAX, 0, 0], MAX),
431             (&[0b100], 0b100),
432             (&[0b100, 0], 0b100),
433         ];
434         for &(a, b) in EQUAL {
435             assert_eq!(limbs_equal_limb_constant_time(a, b), LimbMask::True);
436         }
437         static UNEQUAL: &[(&[Limb], Limb)] = &[
438             (&[0], 1),
439             (&[2], 1),
440             (&[3], 1),
441             (&[1, 1], 1),
442             (&[0b100, 0b100], 0b100),
443             (&[1, 0, 0b100, 0, 0, 0, 0, 0], 1),
444             (&[1, 0, 0, 0, 0, 0, 0, 0b100], 1),
445             (&[MAX, MAX], MAX),
446             (&[MAX, 1], MAX),
447         ];
448         for &(a, b) in UNEQUAL {
449             assert_eq!(limbs_equal_limb_constant_time(a, b), LimbMask::False);
450         }
451     }
452 
453     #[test]
454     #[cfg(feature = "alloc")]
test_limbs_less_than_limb_constant_time()455     fn test_limbs_less_than_limb_constant_time() {
456         static LESSER: &[(&[Limb], Limb)] = &[
457             (&[0], 1),
458             (&[0, 0], 1),
459             (&[1, 0], 2),
460             (&[2, 0], 3),
461             (&[2, 0], 3),
462             (&[MAX - 1], MAX),
463             (&[MAX - 1, 0], MAX),
464         ];
465         for &(a, b) in LESSER {
466             assert_eq!(limbs_less_than_limb_constant_time(a, b), LimbMask::True);
467         }
468         static EQUAL: &[(&[Limb], Limb)] = &[
469             (&[0], 0),
470             (&[0, 0, 0, 0], 0),
471             (&[1], 1),
472             (&[1, 0, 0, 0, 0, 0, 0], 1),
473             (&[MAX], MAX),
474         ];
475         static GREATER: &[(&[Limb], Limb)] = &[
476             (&[1], 0),
477             (&[2, 0], 1),
478             (&[3, 0, 0, 0], 1),
479             (&[0, 1, 0, 0], 1),
480             (&[0, 0, 1, 0], 1),
481             (&[0, 0, 1, 1], 1),
482             (&[MAX], MAX - 1),
483         ];
484         for &(a, b) in EQUAL.iter().chain(GREATER.iter()) {
485             assert_eq!(limbs_less_than_limb_constant_time(a, b), LimbMask::False);
486         }
487     }
488 
489     #[test]
test_parse_big_endian_and_pad_consttime()490     fn test_parse_big_endian_and_pad_consttime() {
491         const LIMBS: usize = 4;
492 
493         {
494             // Empty input.
495             let inp = untrusted::Input::from(&[]);
496             let mut result = [0; LIMBS];
497             assert!(parse_big_endian_and_pad_consttime(inp, &mut result).is_err());
498         }
499 
500         // The input is longer than will fit in the given number of limbs.
501         {
502             let inp = [1, 2, 3, 4, 5, 6, 7, 8, 9];
503             let inp = untrusted::Input::from(&inp);
504             let mut result = [0; 8 / LIMB_BYTES];
505             assert!(parse_big_endian_and_pad_consttime(inp, &mut result[..]).is_err());
506         }
507 
508         // Less than a full limb.
509         {
510             let inp = [0xfe];
511             let inp = untrusted::Input::from(&inp);
512             let mut result = [0; LIMBS];
513             assert_eq!(
514                 Ok(()),
515                 parse_big_endian_and_pad_consttime(inp, &mut result[..])
516             );
517             assert_eq!(&[0xfe, 0, 0, 0], &result);
518         }
519 
520         // A whole limb for 32-bit, half a limb for 64-bit.
521         {
522             let inp = [0xbe, 0xef, 0xf0, 0x0d];
523             let inp = untrusted::Input::from(&inp);
524             let mut result = [0; LIMBS];
525             assert_eq!(Ok(()), parse_big_endian_and_pad_consttime(inp, &mut result));
526             assert_eq!(&[0xbeeff00d, 0, 0, 0], &result);
527         }
528 
529         // XXX: This is a weak set of tests. TODO: expand it.
530     }
531 
532     #[test]
test_big_endian_from_limbs_same_length()533     fn test_big_endian_from_limbs_same_length() {
534         #[cfg(target_pointer_width = "32")]
535         let limbs = [
536             0xbccddeef, 0x89900aab, 0x45566778, 0x01122334, 0xddeeff00, 0x99aabbcc, 0x55667788,
537             0x11223344,
538         ];
539 
540         #[cfg(target_pointer_width = "64")]
541         let limbs = [
542             0x89900aab_bccddeef,
543             0x01122334_45566778,
544             0x99aabbcc_ddeeff00,
545             0x11223344_55667788,
546         ];
547 
548         let expected = [
549             0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
550             0xff, 0x00, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x90, 0x0a, 0xab,
551             0xbc, 0xcd, 0xde, 0xef,
552         ];
553 
554         let mut out = [0xabu8; 32];
555         big_endian_from_limbs(&limbs[..], &mut out);
556         assert_eq!(&out[..], &expected[..]);
557     }
558 
559     #[should_panic]
560     #[test]
test_big_endian_from_limbs_fewer_limbs()561     fn test_big_endian_from_limbs_fewer_limbs() {
562         #[cfg(target_pointer_width = "32")]
563         // Two fewer limbs.
564         let limbs = [
565             0xbccddeef, 0x89900aab, 0x45566778, 0x01122334, 0xddeeff00, 0x99aabbcc,
566         ];
567 
568         // One fewer limb.
569         #[cfg(target_pointer_width = "64")]
570         let limbs = [
571             0x89900aab_bccddeef,
572             0x01122334_45566778,
573             0x99aabbcc_ddeeff00,
574         ];
575 
576         let mut out = [0xabu8; 32];
577 
578         big_endian_from_limbs(&limbs[..], &mut out);
579     }
580 
581     #[test]
test_limbs_minimal_bits()582     fn test_limbs_minimal_bits() {
583         const ALL_ONES: Limb = LimbMask::True as Limb;
584         static CASES: &[(&[Limb], usize)] = &[
585             (&[], 0),
586             (&[0], 0),
587             (&[ALL_ONES], LIMB_BITS),
588             (&[ALL_ONES, 0], LIMB_BITS),
589             (&[ALL_ONES, 1], LIMB_BITS + 1),
590             (&[0, 0], 0),
591             (&[1, 0], 1),
592             (&[0, 1], LIMB_BITS + 1),
593             (&[0, ALL_ONES], 2 * LIMB_BITS),
594             (&[ALL_ONES, ALL_ONES], 2 * LIMB_BITS),
595             (&[ALL_ONES, ALL_ONES >> 1], 2 * LIMB_BITS - 1),
596             (&[ALL_ONES, 0b100_0000], LIMB_BITS + 7),
597             (&[ALL_ONES, 0b101_0000], LIMB_BITS + 7),
598             (&[ALL_ONES, ALL_ONES >> 1], LIMB_BITS + (LIMB_BITS) - 1),
599         ];
600         for (limbs, bits) in CASES {
601             assert_eq!(limbs_minimal_bits(limbs).as_usize_bits(), *bits);
602         }
603     }
604 }
605