1use crate::{c, error};
22
23#[cfg(feature = "alloc")]
24use crate::bits;
25
26#[cfg(feature = "alloc")]
27use core::num::Wrapping;
28
29#[cfg(target_pointer_width = "64")]
31pub type Limb = u64;
32#[cfg(target_pointer_width = "32")]
33pub type Limb = u32;
34#[cfg(target_pointer_width = "64")]
35pub const LIMB_BITS: usize = 64;
36#[cfg(target_pointer_width = "32")]
37pub const LIMB_BITS: usize = 32;
38
39#[cfg(target_pointer_width = "64")]
40#[derive(Debug, PartialEq)]
41#[repr(u64)]
42pub 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)]
50pub enum LimbMask {
51 True = 0xffff_ffff,
52 False = 0,
53}
54
55pub const LIMB_BYTES: usize = (LIMB_BITS + 7) / 8;
56
57#[inline]
58pub 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]
68pub 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]
74pub 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")]
80pub 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]
85pub 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]
91pub 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]
97pub 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#[cfg(feature = "alloc")]
109pub 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 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 bits::BitLength::from_usize_bits(0)
129}
130
131#[inline]
133pub 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)]
139pub enum AllowZero {
140 No,
141 Yes,
142}
143
144pub 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
166pub 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
192pub 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 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 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
241pub 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")]
255pub type Window = Limb;
256
257#[cfg(feature = "alloc")]
270pub 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 window_low_bit.0 -= WINDOW_BITS;
326 acc = fold(acc, window);
327 }
328 window_low_bit.0 += Wrapping(LIMB_BITS); acc
331 })
332}
333
334extern "C" {
335 #[cfg(feature = "alloc")]
336 fn LIMB_shr(a: Limb, shift: c::size_t) -> Limb;
337
338 #[cfg(feature = "alloc")]
339 fn LIMBS_are_even(a: *const Limb, num_limbs: c::size_t) -> LimbMask;
340 fn LIMBS_are_zero(a: *const Limb, num_limbs: c::size_t) -> LimbMask;
341 #[cfg(feature = "alloc")]
342 fn LIMBS_equal_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask;
343 fn LIMBS_less_than(a: *const Limb, b: *const Limb, num_limbs: c::size_t) -> LimbMask;
344 #[cfg(feature = "alloc")]
345 fn LIMBS_less_than_limb(a: *const Limb, b: Limb, num_limbs: c::size_t) -> LimbMask;
346 fn LIMBS_reduce_once(r: *mut Limb, m: *const Limb, num_limbs: c::size_t);
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 const MAX: Limb = LimbMask::True as Limb;
354
355 #[test]
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]
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]
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")]
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]
490 fn test_parse_big_endian_and_pad_consttime() {
491 const LIMBS: usize = 4;
492
493 {
494 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 {
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 {
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 {
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 }
531
532 #[test]
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 0x8990_0aab_bccd_deef,
543 0x0112_2334_4556_6778,
544 0x99aa_bbcc_ddee_ff00,
545 0x1122_3344_5566_7788,
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]
561 fn test_big_endian_from_limbs_fewer_limbs() {
562 #[cfg(target_pointer_width = "32")]
563 let limbs = [
565 0xbccddeef, 0x89900aab, 0x45566778, 0x01122334, 0xddeeff00, 0x99aabbcc,
566 ];
567
568 #[cfg(target_pointer_width = "64")]
570 let limbs = [
571 0x8990_0aab_bccd_deef,
572 0x0112_2334_4556_6778,
573 0x99aa_bbcc_ddee_ff00,
574 ];
575
576 let mut out = [0xabu8; 32];
577
578 big_endian_from_limbs(&limbs[..], &mut out);
579 }
580
581 #[test]
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}