Skip to content

Commit 22aa6dd

Browse files
authored
BoxedUint: move subtle support into ct module (#396)
Groups all the `subtle`-based constant time code together under `uint::boxed::ct`. This is mostly the `ConditionallySelectable`-alike methods, but we can't actually impl that trait due to its `Copy` bound.
1 parent 9043edb commit 22aa6dd

File tree

2 files changed

+61
-55
lines changed

2 files changed

+61
-55
lines changed

src/uint/boxed.rs

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mod rand;
2727
use crate::{Integer, Limb, NonZero, Uint, Word, Zero, U128, U64};
2828
use alloc::{boxed::Box, vec, vec::Vec};
2929
use core::{fmt, mem};
30-
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
30+
use subtle::{Choice, ConstantTimeEq};
3131

3232
#[cfg(feature = "zeroize")]
3333
use zeroize::Zeroize;
@@ -188,46 +188,6 @@ impl BoxedUint {
188188
self.limbs.len()
189189
}
190190

191-
/// Conditionally select `a` or `b` in constant time depending on [`Choice`].
192-
///
193-
/// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so
194-
/// this is an inherent function instead.
195-
///
196-
/// Panics if `a` and `b` don't have the same precision.
197-
pub fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
198-
debug_assert_eq!(a.bits_precision(), b.bits_precision());
199-
let mut limbs = vec![Limb::ZERO; a.nlimbs()].into_boxed_slice();
200-
201-
for i in 0..a.nlimbs() {
202-
limbs[i] = Limb::conditional_select(&a.limbs[i], &b.limbs[i], choice);
203-
}
204-
205-
Self { limbs }
206-
}
207-
208-
/// Conditionally assign `other` to `self`, according to `choice`.
209-
///
210-
/// This function should execute in constant time.
211-
#[inline]
212-
pub fn conditional_assign(&mut self, other: &Self, choice: Choice) {
213-
debug_assert_eq!(self.bits_precision(), other.bits_precision());
214-
215-
for i in 0..self.nlimbs() {
216-
self.limbs[i] = Limb::conditional_select(&self.limbs[i], &other.limbs[i], choice);
217-
}
218-
}
219-
220-
/// Conditionally swap `self` and `other` if `choice == 1`; otherwise,
221-
/// reassign both unto themselves.
222-
///
223-
/// This function should execute in constant time.
224-
#[inline]
225-
fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
226-
let t = a.clone();
227-
a.conditional_assign(b, choice);
228-
b.conditional_assign(&t, choice);
229-
}
230-
231191
/// Widen this type's precision to the given number of bits.
232192
///
233193
/// Panics if `at_least_bits_precision` is smaller than the current precision.
@@ -507,16 +467,6 @@ mod tests {
507467
use super::BoxedUint;
508468
use crate::Word;
509469
use alloc::vec::Vec;
510-
use subtle::Choice;
511-
512-
#[test]
513-
fn conditional_select() {
514-
let a = BoxedUint::zero_with_precision(128);
515-
let b = BoxedUint::max(128);
516-
517-
assert_eq!(a, BoxedUint::conditional_select(&a, &b, Choice::from(0)));
518-
assert_eq!(b, BoxedUint::conditional_select(&a, &b, Choice::from(1)));
519-
}
520470

521471
#[test]
522472
fn from_word_vec() {

src/uint/boxed/ct.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,58 @@
11
//! Constant-time helper functions.
2-
//!
3-
//! These largely exist as a workaround for the `Copy` bound on [`subtle::ConditionallySelectable`].
42
53
use super::BoxedUint;
6-
use subtle::CtOption;
4+
use crate::Limb;
5+
use subtle::{Choice, ConditionallySelectable, CtOption};
76

87
impl BoxedUint {
8+
/// Conditionally select `a` or `b` in constant time depending on [`Choice`].
9+
///
10+
/// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so
11+
/// this is an inherent function instead.
12+
///
13+
/// Panics if `a` and `b` don't have the same precision.
14+
pub fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
15+
debug_assert_eq!(a.bits_precision(), b.bits_precision());
16+
let mut limbs = vec![Limb::ZERO; a.nlimbs()].into_boxed_slice();
17+
18+
for i in 0..a.nlimbs() {
19+
limbs[i] = Limb::conditional_select(&a.limbs[i], &b.limbs[i], choice);
20+
}
21+
22+
Self { limbs }
23+
}
24+
25+
/// Conditionally assign `other` to `self`, according to `choice`.
26+
///
27+
/// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so
28+
/// this is an inherent function instead.
29+
///
30+
/// Panics if `a` and `b` don't have the same precision.
31+
#[inline]
32+
pub fn conditional_assign(&mut self, other: &Self, choice: Choice) {
33+
debug_assert_eq!(self.bits_precision(), other.bits_precision());
34+
35+
for i in 0..self.nlimbs() {
36+
self.limbs[i].conditional_assign(&other.limbs[i], choice);
37+
}
38+
}
39+
40+
/// Conditionally swap `self` and `other` if `choice == 1`; otherwise,
41+
/// reassign both unto themselves.
42+
///
43+
/// NOTE: can't impl `subtle`'s [`ConditionallySelectable`] trait due to its `Copy` bound, so
44+
/// this is an inherent function instead.
45+
///
46+
/// Panics if `a` and `b` don't have the same precision.
47+
#[inline]
48+
pub fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
49+
debug_assert_eq!(a.bits_precision(), b.bits_precision());
50+
51+
for i in 0..a.nlimbs() {
52+
Limb::conditional_swap(&mut a.limbs[i], &mut b.limbs[i], choice);
53+
}
54+
}
55+
956
/// Conditional `map`: workaround which provides a [`CtOption::map`]-like API.
1057
///
1158
/// Ensures both functions are called regardless of whether the first returns some/none with an
@@ -62,7 +109,16 @@ impl BoxedUint {
62109
#[cfg(test)]
63110
mod tests {
64111
use super::BoxedUint;
65-
use subtle::CtOption;
112+
use subtle::{Choice, CtOption};
113+
114+
#[test]
115+
fn conditional_select() {
116+
let a = BoxedUint::zero_with_precision(128);
117+
let b = BoxedUint::max(128);
118+
119+
assert_eq!(a, BoxedUint::conditional_select(&a, &b, Choice::from(0)));
120+
assert_eq!(b, BoxedUint::conditional_select(&a, &b, Choice::from(1)));
121+
}
66122

67123
#[test]
68124
fn cond_map_some() {

0 commit comments

Comments
 (0)