1use super::{inv_mod2_62, iterations, jump, Matrix};
7use crate::{BoxedUint, Inverter, Limb, Odd, Word};
8use alloc::boxed::Box;
9use core::{
10 cmp::max,
11 ops::{AddAssign, Mul},
12};
13use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater, CtOption};
14
15#[derive(Clone, Debug)]
19pub struct BoxedSafeGcdInverter {
20 pub(crate) modulus: BoxedUnsatInt,
22
23 adjuster: BoxedUnsatInt,
25
26 inverse: i64,
28}
29
30impl BoxedSafeGcdInverter {
31 pub fn new(modulus: &Odd<BoxedUint>, adjuster: &BoxedUint) -> Self {
35 Self {
36 modulus: BoxedUnsatInt::from(&modulus.0),
37 adjuster: BoxedUnsatInt::from(&adjuster.widen(modulus.bits_precision())),
38 inverse: inv_mod2_62(modulus.0.as_words()),
39 }
40 }
41
42 fn norm(&self, mut value: BoxedUnsatInt, negate: Choice) -> BoxedUnsatInt {
46 value.conditional_add(&self.modulus, value.is_negative());
47 value.conditional_assign(&value.neg(), negate);
48 value.conditional_add(&self.modulus, value.is_negative());
49 value
50 }
51}
52
53impl Inverter for BoxedSafeGcdInverter {
54 type Output = BoxedUint;
55
56 fn invert(&self, value: &BoxedUint) -> CtOption<Self::Output> {
57 let mut d = BoxedUnsatInt::zero(self.modulus.nlimbs());
58 let mut g = BoxedUnsatInt::from(value).widen(d.nlimbs());
59 let f = divsteps(&mut d, &self.adjuster, &self.modulus, &mut g, self.inverse);
60
61 let antiunit = f.is_minus_one();
65 let ret = self.norm(d, antiunit);
66 let is_some = f.is_one() | antiunit;
67
68 CtOption::new(ret.to_uint(value.bits_precision()), is_some)
69 }
70
71 fn invert_vartime(&self, value: &BoxedUint) -> CtOption<Self::Output> {
72 let mut d = BoxedUnsatInt::zero(self.modulus.nlimbs());
73 let mut g = BoxedUnsatInt::from(value).widen(d.nlimbs());
74 let f = divsteps_vartime(&mut d, &self.adjuster, &self.modulus, &mut g, self.inverse);
75
76 let antiunit = f.is_minus_one();
80 let ret = self.norm(d, antiunit);
81 let is_some = f.is_one() | antiunit;
82
83 CtOption::new(ret.to_uint(value.bits_precision()), is_some)
84 }
85}
86
87fn unsat_nlimbs_for_sat_nlimbs(saturated_nlimbs: usize) -> usize {
90 let saturated_nlimbs = if Word::BITS == 32 && saturated_nlimbs == 1 {
91 2
92 } else {
93 saturated_nlimbs
94 };
95
96 safegcd_nlimbs!(saturated_nlimbs * Limb::BITS as usize)
97}
98
99pub(crate) fn gcd(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
101 let nlimbs = unsat_nlimbs_for_sat_nlimbs(max(f.nlimbs(), g.nlimbs()));
102 let bits_precision = f.bits_precision();
103
104 let inverse = inv_mod2_62(f.as_words());
105 let f = BoxedUnsatInt::from_uint_widened(f, nlimbs);
106 let mut g = BoxedUnsatInt::from_uint_widened(g, nlimbs);
107 let mut d = BoxedUnsatInt::zero(nlimbs);
108 let e = BoxedUnsatInt::one(nlimbs);
109
110 let mut f = divsteps(&mut d, &e, &f, &mut g, inverse);
111 f.conditional_negate(f.is_negative());
112 f.to_uint(bits_precision)
113}
114
115pub(crate) fn gcd_vartime(f: &BoxedUint, g: &BoxedUint) -> BoxedUint {
119 let nlimbs = unsat_nlimbs_for_sat_nlimbs(max(f.nlimbs(), g.nlimbs()));
120 let bits_precision = f.bits_precision();
121
122 let inverse = inv_mod2_62(f.as_words());
123 let f = BoxedUnsatInt::from_uint_widened(f, nlimbs);
124 let mut g = BoxedUnsatInt::from_uint_widened(g, nlimbs);
125 let mut d = BoxedUnsatInt::zero(nlimbs);
126 let e = BoxedUnsatInt::one(nlimbs);
127
128 let mut f = divsteps_vartime(&mut d, &e, &f, &mut g, inverse);
129 f.conditional_negate(f.is_negative());
130 f.to_uint(bits_precision)
131}
132
133fn divsteps(
138 d: &mut BoxedUnsatInt,
139 e: &BoxedUnsatInt,
140 f_0: &BoxedUnsatInt,
141 g: &mut BoxedUnsatInt,
142 inverse: i64,
143) -> BoxedUnsatInt {
144 debug_assert_eq!(f_0.nlimbs(), g.nlimbs());
145
146 let mut e = e.clone();
147 let mut f = f_0.clone();
148 let mut delta = 1;
149 let mut matrix;
150
151 for _ in 0..iterations(f_0.bits(), g.bits()) {
152 (delta, matrix) = jump(&f.0, &g.0, delta);
153 fg(&mut f, g, matrix);
154 de(f_0, inverse, matrix, d, &mut e);
155 }
156
157 f
158}
159
160fn divsteps_vartime(
166 d: &mut BoxedUnsatInt,
167 e: &BoxedUnsatInt,
168 f_0: &BoxedUnsatInt,
169 g: &mut BoxedUnsatInt,
170 inverse: i64,
171) -> BoxedUnsatInt {
172 debug_assert_eq!(f_0.nlimbs(), g.nlimbs());
173
174 let mut e = e.clone();
175 let mut f = f_0.clone();
176 let mut delta = 1;
177 let mut matrix;
178
179 while !bool::from(g.is_zero()) {
180 (delta, matrix) = jump(&f.0, &g.0, delta);
181 fg(&mut f, g, matrix);
182 de(f_0, inverse, matrix, d, &mut e);
183 }
184
185 f
186}
187
188fn fg(f: &mut BoxedUnsatInt, g: &mut BoxedUnsatInt, t: Matrix) {
193 let mut f2 = &*f * t[0][0];
195 f2 += &*g * t[0][1];
196 f2.shr_assign();
197
198 let mut g2 = &*f * t[1][0];
199 g2 += &*g * t[1][1];
200 g2.shr_assign();
201
202 *f = f2;
203 *g = g2;
204}
205
206fn de(
214 modulus: &BoxedUnsatInt,
215 inverse: i64,
216 t: Matrix,
217 d: &mut BoxedUnsatInt,
218 e: &mut BoxedUnsatInt,
219) {
220 let mask = BoxedUnsatInt::MASK as i64;
221 let mut md =
222 t[0][0] * d.is_negative().unwrap_u8() as i64 + t[0][1] * e.is_negative().unwrap_u8() as i64;
223 let mut me =
224 t[1][0] * d.is_negative().unwrap_u8() as i64 + t[1][1] * e.is_negative().unwrap_u8() as i64;
225
226 let cd = t[0][0]
227 .wrapping_mul(d.lowest() as i64)
228 .wrapping_add(t[0][1].wrapping_mul(e.lowest() as i64))
229 & mask;
230
231 let ce = t[1][0]
232 .wrapping_mul(d.lowest() as i64)
233 .wrapping_add(t[1][1].wrapping_mul(e.lowest() as i64))
234 & mask;
235
236 md -= (inverse.wrapping_mul(cd).wrapping_add(md)) & mask;
237 me -= (inverse.wrapping_mul(ce).wrapping_add(me)) & mask;
238
239 let mut cd = d.mul(t[0][0]);
240 cd += &e.mul(t[0][1]);
241 cd += &modulus.mul(md);
242 cd.shr_assign();
243
244 let mut ce = d.mul(t[1][0]);
245 ce += &e.mul(t[1][1]);
246 ce += &modulus.mul(me);
247 ce.shr_assign();
248
249 *d = cd;
250 *e = ce;
251}
252
253#[derive(Clone, Debug)]
258pub(crate) struct BoxedUnsatInt(Box<[u64]>);
259
260impl From<&BoxedUint> for BoxedUnsatInt {
268 fn from(input: &BoxedUint) -> BoxedUnsatInt {
269 Self::from_uint_widened(input, unsat_nlimbs_for_sat_nlimbs(input.nlimbs()))
270 }
271}
272
273impl BoxedUnsatInt {
274 pub const LIMB_BITS: usize = 62;
276
277 pub const MASK: u64 = u64::MAX >> (64 - Self::LIMB_BITS);
279
280 #[allow(trivial_numeric_casts)]
291 fn from_uint_widened(input: &BoxedUint, nlimbs: usize) -> BoxedUnsatInt {
292 debug_assert!(nlimbs >= unsat_nlimbs_for_sat_nlimbs(input.nlimbs()));
293
294 let mut tmp: [Word; 2] = [0; 2];
297
298 let input = if Word::BITS == 32 && input.nlimbs() == 1 {
299 tmp[0] = input.limbs[0].0;
300 &tmp
301 } else {
302 input.as_words()
303 };
304
305 let mut output = vec![0u64; nlimbs];
306 impl_limb_convert!(Word, Word::BITS as usize, input, u64, 62, output);
307 Self(output.into())
308 }
309
310 #[allow(trivial_numeric_casts)]
312 fn to_uint(&self, mut bits_precision: u32) -> BoxedUint {
313 if bits_precision == 32 {
315 bits_precision = 64;
316 }
317
318 debug_assert_eq!(self.nlimbs(), safegcd_nlimbs!(bits_precision as usize));
319 assert!(
320 !bool::from(self.is_negative()),
321 "can't convert negative number to BoxedUint"
322 );
323
324 let mut ret = BoxedUint {
325 limbs: vec![Limb::ZERO; nlimbs!(bits_precision)].into(),
326 };
327
328 impl_limb_convert!(
329 u64,
330 62,
331 &self.0,
332 Word,
333 Word::BITS as usize,
334 ret.as_words_mut()
335 );
336
337 ret
338 }
339
340 pub fn conditional_add(&mut self, other: &Self, choice: Choice) {
342 debug_assert_eq!(self.nlimbs(), other.nlimbs());
343 let mut carry = 0;
344
345 for i in 0..self.nlimbs() {
346 let addend = u64::conditional_select(&0, &other.0[i], choice);
347 let sum = self.0[i] + addend + carry;
348 self.0[i] = sum & Self::MASK;
349 carry = sum >> Self::LIMB_BITS;
350 }
351 }
352
353 pub fn conditional_assign(&mut self, other: &Self, choice: Choice) {
356 for i in 0..self.nlimbs() {
357 self.0[i] = u64::conditional_select(&self.0[i], &other.0[i], choice);
358 }
359 }
360
361 pub fn conditional_negate(&mut self, choice: Choice) {
364 self.conditional_assign(&self.neg(), choice);
366 }
367
368 pub fn neg(&self) -> Self {
372 let nlimbs = self.nlimbs();
375 let mut ret = Self::zero(nlimbs);
376 let mut carry = 1;
377
378 for i in 0..nlimbs {
379 let sum = (self.0[i] ^ Self::MASK) + carry;
380 ret.0[i] = sum & Self::MASK;
381 carry = sum >> Self::LIMB_BITS;
382 }
383
384 ret
385 }
386
387 pub fn shr_assign(&mut self) {
389 let is_negative = self.is_negative();
390
391 for i in 0..(self.nlimbs() - 1) {
392 self.0[i] = self.0[i + 1];
393 }
394
395 self.0[self.nlimbs() - 1] = u64::conditional_select(&0, &Self::MASK, is_negative);
396 }
397
398 pub fn zero(nlimbs: usize) -> Self {
400 Self(vec![0; nlimbs].into())
401 }
402
403 pub fn one(nlimbs: usize) -> Self {
405 let mut ret = Self::zero(nlimbs);
406 ret.0[0] = 1;
407 ret
408 }
409
410 pub fn widen(self, nlimbs: usize) -> Self {
412 debug_assert!(nlimbs >= self.nlimbs(),);
413 let mut limbs = self.0.into_vec();
414 limbs.resize(nlimbs, 0);
415 Self(limbs.into())
416 }
417
418 pub fn is_minus_one(&self) -> Choice {
420 self.0
421 .iter()
422 .fold(Choice::from(1), |acc, &limb| acc & limb.ct_eq(&Self::MASK))
423 }
424
425 pub fn is_negative(&self) -> Choice {
427 self.0[self.nlimbs() - 1].ct_gt(&(Self::MASK >> 1))
428 }
429
430 pub fn is_zero(&self) -> Choice {
432 self.0
433 .iter()
434 .fold(Choice::from(1), |acc, &limb| acc & limb.ct_eq(&0))
435 }
436
437 pub fn is_one(&self) -> Choice {
439 self.0[1..]
440 .iter()
441 .fold(self.lowest().ct_eq(&1), |acc, &limb| acc & limb.ct_eq(&0))
442 }
443
444 pub fn lowest(&self) -> u64 {
446 self.0[0]
447 }
448
449 pub fn nlimbs(&self) -> usize {
451 self.0.len()
452 }
453
454 pub fn leading_zeros(&self) -> u32 {
456 let mut count = 0;
457 let mut nonzero_limb_not_encountered = Choice::from(1);
458
459 for l in self.0.iter() {
460 let z = l.leading_zeros() - 2;
461 count += u32::conditional_select(&0, &z, nonzero_limb_not_encountered);
462 nonzero_limb_not_encountered &= !l.ct_eq(&0);
463 }
464
465 count
466 }
467
468 pub fn bits(&self) -> u32 {
470 (self.0.len() as u32 * 62) - self.leading_zeros()
471 }
472}
473
474impl AddAssign<BoxedUnsatInt> for BoxedUnsatInt {
475 fn add_assign(&mut self, rhs: BoxedUnsatInt) {
476 self.add_assign(&rhs);
477 }
478}
479
480impl AddAssign<&BoxedUnsatInt> for BoxedUnsatInt {
481 fn add_assign(&mut self, rhs: &BoxedUnsatInt) {
482 debug_assert_eq!(self.nlimbs(), rhs.nlimbs());
483 let mut carry = 0;
484
485 for i in 0..self.nlimbs() {
486 let sum = self.0[i] + rhs.0[i] + carry;
487 self.0[i] = sum & Self::MASK;
488 carry = sum >> Self::LIMB_BITS;
489 }
490 }
491}
492
493impl Mul<i64> for &BoxedUnsatInt {
494 type Output = BoxedUnsatInt;
495
496 fn mul(self, other: i64) -> BoxedUnsatInt {
497 let nlimbs = self.nlimbs();
498 let mut ret = BoxedUnsatInt::zero(nlimbs);
499
500 let (other, mut carry, mask) = if other < 0 {
514 (-other, -other as u64, BoxedUnsatInt::MASK)
515 } else {
516 (other, 0, 0)
517 };
518
519 for i in 0..nlimbs {
520 let sum = (carry as u128) + ((self.0[i] ^ mask) as u128) * (other as u128);
521 ret.0[i] = sum as u64 & BoxedUnsatInt::MASK;
522 carry = (sum >> BoxedUnsatInt::LIMB_BITS) as u64;
523 }
524
525 ret
526 }
527}
528
529#[cfg(test)]
530mod tests {
531 use super::BoxedUnsatInt;
532 use crate::{BoxedUint, Inverter, PrecomputeInverter, U256};
533 use proptest::prelude::*;
534 use subtle::ConstantTimeEq;
535
536 #[cfg(not(miri))]
537 use crate::modular::safegcd::UnsatInt;
538
539 impl PartialEq for BoxedUnsatInt {
540 fn eq(&self, other: &Self) -> bool {
541 self.0.ct_eq(&other.0).into()
542 }
543 }
544
545 #[test]
546 fn invert() {
547 let g = BoxedUint::from_be_hex(
548 "00000000CBF9350842F498CE441FC2DC23C7BF47D3DE91C327B2157C5E4EED77",
549 256,
550 )
551 .unwrap();
552 let modulus = BoxedUint::from_be_hex(
553 "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
554 256,
555 )
556 .unwrap()
557 .to_odd()
558 .unwrap();
559 let inverter = modulus.precompute_inverter();
560 let result = inverter.invert(&g).unwrap();
561 assert_eq!(
562 BoxedUint::from_be_hex(
563 "FB668F8F509790BC549B077098918604283D42901C92981062EB48BC723F617B",
564 256
565 )
566 .unwrap(),
567 result
568 );
569 }
570
571 #[test]
572 fn de() {
573 let modulus = BoxedUnsatInt(
574 vec![
575 3727233105432618321,
576 3718823987352861203,
577 4611686018427387899,
578 4611685743549481023,
579 255,
580 0,
581 ]
582 .into(),
583 );
584 let inverse = 3687945983376704433;
585 let mut d = BoxedUnsatInt(
586 vec![
587 3490544662636853909,
588 2211268325417683828,
589 992023446686701852,
590 4539270294123539695,
591 4611686018427387762,
592 4611686018427387903,
593 ]
594 .into(),
595 );
596 let mut e = BoxedUnsatInt(
597 vec![
598 4004071259428196451,
599 1262234674432503659,
600 4060414504149367846,
601 1804121722707079191,
602 4611686018427387712,
603 4611686018427387903,
604 ]
605 .into(),
606 );
607 let t = [[-45035996273704960, 409827566090715136], [-14, 25]];
608
609 super::de(&modulus, inverse, t, &mut d, &mut e);
610 assert_eq!(
611 d,
612 BoxedUnsatInt(
613 vec![
614 1211048314408256470,
615 1344008336933394898,
616 3913497193346473913,
617 2764114971089162538,
618 4,
619 0,
620 ]
621 .into()
622 )
623 );
624
625 assert_eq!(e, BoxedUnsatInt(vec![0, 0, 0, 0, 0, 0].into()));
626 }
627
628 #[test]
629 fn boxed_unsatint_is_zero() {
630 let zero = BoxedUnsatInt::from(&U256::ZERO.into());
631 assert!(bool::from(zero.is_zero()));
632
633 let one = BoxedUnsatInt::from(&U256::ONE.into());
634 assert!(!bool::from(one.is_zero()));
635 }
636
637 #[test]
638 fn boxed_unsatint_is_one() {
639 let zero = BoxedUnsatInt::from(&U256::ZERO.into());
640 assert!(!bool::from(zero.is_one()));
641
642 let one = BoxedUnsatInt::from(&U256::ONE.into());
643 assert!(bool::from(one.is_one()));
644 }
645
646 #[test]
647 fn unsatint_shr_assign() {
648 let mut n = BoxedUnsatInt(
649 vec![
650 0,
651 1211048314408256470,
652 1344008336933394898,
653 3913497193346473913,
654 2764114971089162538,
655 4,
656 ]
657 .into(),
658 );
659 n.shr_assign();
660
661 assert_eq!(
662 &*n.0,
663 &[
664 1211048314408256470,
665 1344008336933394898,
666 3913497193346473913,
667 2764114971089162538,
668 4,
669 0
670 ]
671 );
672 }
673
674 prop_compose! {
675 fn u256()(bytes in any::<[u8; 32]>()) -> U256 {
676 U256::from_le_slice(&bytes)
677 }
678 }
679
680 proptest! {
681 #[test]
682 #[cfg(not(miri))]
683 fn boxed_unsatint_add(x in u256(), y in u256()) {
684 let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
685 let y_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&y);
686 let mut x_boxed = BoxedUnsatInt::from(&x.into());
687 let y_boxed = BoxedUnsatInt::from(&y.into());
688
689 let expected = x_ref.add(&y_ref);
690 x_boxed += &y_boxed;
691 prop_assert_eq!(&expected.0, &*x_boxed.0);
692 }
693
694 #[test]
695 #[cfg(not(miri))]
696 fn boxed_unsatint_mul(x in u256(), y in any::<i64>()) {
697 let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
698 let x_boxed = BoxedUnsatInt::from(&x.into());
699
700 let expected = x_ref.mul(y);
701 let actual = &x_boxed * y;
702 prop_assert_eq!(&expected.0, &*actual.0);
703 }
704
705 #[test]
706 #[cfg(not(miri))]
707 fn boxed_unsatint_neg(x in u256()) {
708 let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
709 let x_boxed = BoxedUnsatInt::from(&x.into());
710
711 let expected = x_ref.neg();
712 let actual = x_boxed.neg();
713 prop_assert_eq!(&expected.0, &*actual.0);
714 }
715
716 #[test]
717 #[cfg(not(miri))]
718 fn boxed_unsatint_shr(x in u256()) {
719 let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
720 let mut x_boxed = BoxedUnsatInt::from(&x.into());
721 x_boxed.shr_assign();
722
723 let expected = x_ref.shr();
724 prop_assert_eq!(&expected.0, &*x_boxed.0);
725 }
726
727 #[test]
728 #[cfg(not(miri))]
729
730 fn boxed_unsatint_is_negative(x in u256()) {
731 let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
732 let x_boxed = BoxedUnsatInt::from(&x.into());
733 assert_eq!(x_ref.is_negative().to_bool_vartime(), bool::from(x_boxed.is_negative()));
734 }
735
736 #[test]
737 #[cfg(not(miri))]
738
739 fn boxed_unsatint_is_minus_one(x in u256()) {
740 let x_ref = UnsatInt::<{ safegcd_nlimbs!(256usize) }>::from_uint(&x);
741 let x_boxed = BoxedUnsatInt::from(&x.into());
742 assert!(bool::from(x_boxed.is_minus_one().ct_eq(&x_ref.eq(&UnsatInt::MINUS_ONE).into())));
743 }
744 }
745}