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