diff --git a/src/uint/boxed.rs b/src/uint/boxed.rs index 2f1bf6be..1081119d 100644 --- a/src/uint/boxed.rs +++ b/src/uint/boxed.rs @@ -14,7 +14,7 @@ mod sub_mod; use crate::{Limb, Uint, Word, Zero, U128, U64}; use alloc::{boxed::Box, vec, vec::Vec}; use core::fmt; -use subtle::Choice; +use subtle::{Choice, ConditionallySelectable}; #[cfg(feature = "zeroize")] use zeroize::Zeroize; @@ -151,6 +151,23 @@ impl BoxedUint { self.limbs.len() } + /// Conditionally select `a` or `b` in constant time depending on [`Choice`]. + /// + /// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so + /// this is an inherent function instead. + /// + /// Panics if `a` and `b` don't have the same precision. + pub fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + debug_assert_eq!(a.nlimbs(), b.nlimbs()); + let mut limbs = vec![Limb::ZERO; a.nlimbs()].into_boxed_slice(); + + for i in 0..a.nlimbs() { + limbs[i] = Limb::conditional_select(&a.limbs[i], &b.limbs[i], choice); + } + + Self { limbs } + } + /// Perform a carry chain-like operation over the limbs of the inputs, /// constructing a result from the returned limbs and carry which is /// widened to the same width as the widest input. @@ -300,3 +317,18 @@ impl Zeroize for BoxedUint { self.limbs.zeroize(); } } + +#[cfg(test)] +mod tests { + use super::BoxedUint; + use subtle::Choice; + + #[test] + fn conditional_select() { + let a = BoxedUint::zero_with_precision(128); + let b = BoxedUint::max(128); + + assert_eq!(a, BoxedUint::conditional_select(&a, &b, Choice::from(0))); + assert_eq!(b, BoxedUint::conditional_select(&a, &b, Choice::from(1))); + } +}