Skip to content

Commit cf97fff

Browse files
committed
[WIP] Replace ConstCtOption with ctutils::CtOption
Analogous to what #1035 did for `ConstChoice`, this extracts the necessary methods onto `ctutils::CtOption` then replaces the type wholesale using the implementation in `ctutils`. There were a few tricky parts, namely we had type-specific implementations of `expect` and `unwrap_or`. It is actually possible to write a generic `const fn` implementation of `CtOption::expect`, avoiding the problems with destructors and the unstable `Destruct` trait, by bounding on `T: Copy` and returning a copy of the inner value. This is implemented as `CtOption::expect_copied` so we can still support `expect` for non-`Copy` types. Unfortunately there's no analogous generic `const fn` solution for `CtOption::unwrap_or`, as this needs a constant-time selector function and even function pointers can't be used from a `const fn`. So this does the only thing we can do to replace it with some level of abstraction: a `ctutils::unwrap_or!` macro. See also: RustCrypto/utils#1274 which extracts some needed methods onto `ctutils::CtOption`.
1 parent 613b88b commit cf97fff

37 files changed

Lines changed: 252 additions & 476 deletions

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,8 @@ harness = false
8686

8787
[profile.dev]
8888
opt-level = 2
89+
90+
[patch.crates-io.ctutils]
91+
#path = "../utils/ctutils"
92+
git = "https://github.com/RustCrypto/utils"
93+
branch = "ctutils/ct-option-methods-for-crypto-bigint"

src/ct.rs

Lines changed: 1 addition & 272 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
pub use ctutils::Choice as ConstChoice;
2-
use subtle::CtOption;
3-
4-
use crate::{
5-
Int, Limb, NonZero, NonZeroInt, Odd, OddInt, Uint,
6-
modular::{ConstMontyForm, ConstMontyParams, SafeGcdInverter},
7-
};
2+
pub use ctutils::CtOption as ConstCtOption;
83

94
/// `const` equivalent of `u32::max(a, b)`.
105
pub const fn u32_max(a: u32, b: u32) -> u32 {
@@ -16,272 +11,6 @@ pub const fn u32_min(a: u32, b: u32) -> u32 {
1611
ConstChoice::from_u32_lt(a, b).select_u32(b, a)
1712
}
1813

19-
/// An equivalent of `subtle::CtOption` usable in a `const fn` context.
20-
#[derive(Debug, Clone)]
21-
pub struct ConstCtOption<T> {
22-
value: T,
23-
is_some: ConstChoice,
24-
}
25-
26-
impl<T> ConstCtOption<T> {
27-
#[inline]
28-
pub(crate) const fn new(value: T, is_some: ConstChoice) -> Self {
29-
Self { value, is_some }
30-
}
31-
32-
#[inline]
33-
pub(crate) const fn some(value: T) -> Self {
34-
Self {
35-
value,
36-
is_some: ConstChoice::TRUE,
37-
}
38-
}
39-
40-
#[inline]
41-
pub(crate) const fn none(dummy_value: T) -> Self {
42-
Self {
43-
value: dummy_value,
44-
is_some: ConstChoice::FALSE,
45-
}
46-
}
47-
48-
/// Returns a reference to the contents of this structure.
49-
///
50-
/// **Note:** if the second element is `None`, the first value may take any value.
51-
#[inline]
52-
pub(crate) const fn components_ref(&self) -> (&T, ConstChoice) {
53-
// Since Rust is not smart enough to tell that we would be moving the value,
54-
// and hence no destructors will be called, we have to return a reference instead.
55-
// See https://github.com/rust-lang/rust/issues/66753
56-
(&self.value, self.is_some)
57-
}
58-
59-
/// Returns a true [`ConstChoice`] if this value is `Some`.
60-
#[inline]
61-
pub const fn is_some(&self) -> ConstChoice {
62-
self.is_some
63-
}
64-
65-
/// Returns a true [`ConstChoice`] if this value is `None`.
66-
#[inline]
67-
pub const fn is_none(&self) -> ConstChoice {
68-
self.is_some.not()
69-
}
70-
71-
/// This returns the underlying value but panics if it is not `Some`.
72-
#[inline]
73-
#[track_caller]
74-
pub fn unwrap(self) -> T {
75-
assert!(
76-
self.is_some.to_bool_vartime(),
77-
"called `ConstCtOption::unwrap()` on a `None` value"
78-
);
79-
self.value
80-
}
81-
82-
/// Apply an additional [`ConstChoice`] requirement to `is_some`.
83-
#[inline]
84-
pub(crate) const fn and_choice(mut self, is_some: ConstChoice) -> Self {
85-
self.is_some = self.is_some.and(is_some);
86-
self
87-
}
88-
}
89-
90-
impl<T> From<ConstCtOption<T>> for CtOption<T> {
91-
#[inline]
92-
fn from(value: ConstCtOption<T>) -> Self {
93-
CtOption::new(value.value, value.is_some.into())
94-
}
95-
}
96-
97-
impl<T> From<ConstCtOption<T>> for Option<T> {
98-
#[inline]
99-
fn from(value: ConstCtOption<T>) -> Self {
100-
if value.is_some.into() {
101-
Some(value.value)
102-
} else {
103-
None
104-
}
105-
}
106-
}
107-
108-
// Need specific implementations to work around the
109-
// "destructors cannot be evaluated at compile-time" error
110-
// See https://github.com/rust-lang/rust/issues/66753
111-
112-
impl<const LIMBS: usize> ConstCtOption<Uint<LIMBS>> {
113-
/// This returns the underlying value if it is `Some` or the provided value otherwise.
114-
#[inline]
115-
pub const fn unwrap_or(self, def: Uint<LIMBS>) -> Uint<LIMBS> {
116-
Uint::select(&def, &self.value, self.is_some)
117-
}
118-
119-
/// Returns the contained value, consuming the `self` value.
120-
///
121-
/// # Panics
122-
///
123-
/// Panics if the value is none with a custom panic message provided by
124-
/// `msg`.
125-
#[inline]
126-
#[track_caller]
127-
pub const fn expect(self, msg: &str) -> Uint<LIMBS> {
128-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
129-
self.value
130-
}
131-
132-
/// Returns the contained value, interpreting the underlying [`Uint`] value as an [`Int`].
133-
#[inline]
134-
pub const fn as_int(&self) -> ConstCtOption<Int<LIMBS>> {
135-
ConstCtOption::new(*self.value.as_int(), self.is_some)
136-
}
137-
}
138-
139-
impl<const LIMBS: usize> ConstCtOption<(Uint<LIMBS>, Uint<LIMBS>)> {
140-
/// Returns the contained value, consuming the `self` value.
141-
///
142-
/// # Panics
143-
///
144-
/// Panics if the value is none with a custom panic message provided by
145-
/// `msg`.
146-
#[inline]
147-
#[track_caller]
148-
pub const fn expect(self, msg: &str) -> (Uint<LIMBS>, Uint<LIMBS>) {
149-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
150-
self.value
151-
}
152-
}
153-
154-
impl<const LIMBS: usize> ConstCtOption<NonZero<Uint<LIMBS>>> {
155-
/// Returns the contained value, consuming the `self` value.
156-
///
157-
/// # Panics
158-
///
159-
/// Panics if the value is none with a custom panic message provided by
160-
/// `msg`.
161-
#[inline]
162-
#[track_caller]
163-
pub const fn expect(self, msg: &str) -> NonZero<Uint<LIMBS>> {
164-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
165-
self.value
166-
}
167-
}
168-
169-
impl<const LIMBS: usize> ConstCtOption<Odd<Uint<LIMBS>>> {
170-
/// Returns the contained value, consuming the `self` value.
171-
///
172-
/// # Panics
173-
///
174-
/// Panics if the value is none with a custom panic message provided by
175-
/// `msg`.
176-
#[inline]
177-
#[track_caller]
178-
pub const fn expect(self, msg: &str) -> Odd<Uint<LIMBS>> {
179-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
180-
self.value
181-
}
182-
}
183-
184-
impl<const LIMBS: usize> ConstCtOption<Int<LIMBS>> {
185-
/// This returns the underlying value if it is `Some` or the provided value otherwise.
186-
#[inline]
187-
pub const fn unwrap_or(self, def: Int<LIMBS>) -> Int<LIMBS> {
188-
Int::select(&def, &self.value, self.is_some)
189-
}
190-
191-
/// Returns the contained value, consuming the `self` value.
192-
///
193-
/// # Panics
194-
///
195-
/// Panics if the value is none with a custom panic message provided by
196-
/// `msg`.
197-
#[inline]
198-
#[track_caller]
199-
pub const fn expect(self, msg: &str) -> Int<LIMBS> {
200-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
201-
self.value
202-
}
203-
}
204-
205-
impl<const LIMBS: usize> ConstCtOption<NonZeroInt<LIMBS>> {
206-
/// Returns the contained value, consuming the `self` value.
207-
///
208-
/// # Panics
209-
///
210-
/// Panics if the value is none with a custom panic message provided by
211-
/// `msg`.
212-
#[inline]
213-
#[track_caller]
214-
pub const fn expect(self, msg: &str) -> NonZeroInt<LIMBS> {
215-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
216-
self.value
217-
}
218-
}
219-
220-
impl<const LIMBS: usize> ConstCtOption<OddInt<LIMBS>> {
221-
/// Returns the contained value, consuming the `self` value.
222-
///
223-
/// # Panics
224-
///
225-
/// Panics if the value is none with a custom panic message provided by
226-
/// `msg`.
227-
#[inline]
228-
#[track_caller]
229-
pub const fn expect(self, msg: &str) -> OddInt<LIMBS> {
230-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
231-
self.value
232-
}
233-
}
234-
235-
impl ConstCtOption<NonZero<Limb>> {
236-
/// Returns the contained value, consuming the `self` value.
237-
///
238-
/// # Panics
239-
///
240-
/// Panics if the value is none with a custom panic message provided by
241-
/// `msg`.
242-
#[inline]
243-
#[track_caller]
244-
pub const fn expect(self, msg: &str) -> NonZero<Limb> {
245-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
246-
self.value
247-
}
248-
}
249-
250-
impl<const LIMBS: usize> ConstCtOption<SafeGcdInverter<LIMBS>> {
251-
/// Returns the contained value, consuming the `self` value.
252-
///
253-
/// # Panics
254-
///
255-
/// Panics if the value is none with a custom panic message provided by
256-
/// `msg`.
257-
#[inline]
258-
#[track_caller]
259-
pub const fn expect(self, msg: &str) -> SafeGcdInverter<LIMBS> {
260-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
261-
self.value
262-
}
263-
}
264-
265-
impl<MOD: ConstMontyParams<LIMBS>, const LIMBS: usize> ConstCtOption<ConstMontyForm<MOD, LIMBS>> {
266-
/// This returns the underlying value if it is `Some` or the provided value otherwise.
267-
#[inline]
268-
pub const fn unwrap_or(self, def: ConstMontyForm<MOD, LIMBS>) -> ConstMontyForm<MOD, LIMBS> {
269-
ConstMontyForm::<MOD, LIMBS>::select(&def, &self.value, self.is_some)
270-
}
271-
272-
/// Returns the contained value, consuming the `self` value.
273-
///
274-
/// # Panics
275-
///
276-
/// Panics if the value is none with a custom panic message provided by `msg`.
277-
#[inline]
278-
#[track_caller]
279-
pub const fn expect(self, msg: &str) -> ConstMontyForm<MOD, LIMBS> {
280-
assert!(self.is_some.to_bool_vartime(), "{}", msg);
281-
self.value
282-
}
283-
}
284-
28514
#[cfg(test)]
28615
mod tests {
28716
use super::{u32_max, u32_min};

src/int.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use crate::{
55
Signed, Uint, Word, Zero,
66
};
77
use core::fmt;
8+
use ctutils::CtSelect;
89
use num_traits::{ConstOne, ConstZero};
9-
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
10+
use subtle::{Choice, ConstantTimeEq};
1011

1112
#[cfg(feature = "serde")]
1213
use crate::Encoding;
@@ -213,9 +214,17 @@ impl<const LIMBS: usize> AsMut<[Limb]> for Int<LIMBS> {
213214
}
214215
}
215216

216-
impl<const LIMBS: usize> ConditionallySelectable for Int<LIMBS> {
217+
impl<const LIMBS: usize> subtle::ConditionallySelectable for Int<LIMBS> {
218+
#[inline]
217219
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
218-
Self(Uint::conditional_select(&a.0, &b.0, choice))
220+
a.ct_select(b, choice.into())
221+
}
222+
}
223+
224+
impl<const LIMBS: usize> CtSelect for Int<LIMBS> {
225+
#[inline]
226+
fn ct_select(&self, other: &Self, choice: ConstChoice) -> Self {
227+
Self(self.0.ct_select(&other.0, choice))
219228
}
220229
}
221230

src/int/div_uint.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl<const LIMBS: usize> Int<LIMBS> {
3939
let (quotient, remainder, lhs_sgn) = self.div_rem_base_uint(rhs);
4040
(
4141
Self(quotient).wrapping_neg_if(lhs_sgn),
42-
Int::new_from_abs_sign(remainder, lhs_sgn).expect("no overflow; always fits"),
42+
Int::new_from_abs_sign(remainder, lhs_sgn).expect_copied("no overflow; always fits"),
4343
)
4444
}
4545

0 commit comments

Comments
 (0)