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