Skip to content

Commit adbc47d

Browse files
committed
[WIP] ctutils: CtOption methods for crypto-bigint
Adds the methods needed to replace `ConstCtOption` in `crypto-bigint` with `ctutils::CtOption`.
1 parent 93870ce commit adbc47d

1 file changed

Lines changed: 113 additions & 2 deletions

File tree

ctutils/src/ct_option.rs

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
use crate::{Choice, CtEq, CtSelect};
22
use core::ops::{Deref, DerefMut};
33

4+
/// Helper macro for providing behavior like the `unwrap_or` combinator that works in a `const fn`
5+
/// context.
6+
///
7+
/// Requires a provided selector function to do the constant-time selection.
8+
#[macro_export]
9+
macro_rules! unwrap_or {
10+
($opt:expr, $default:expr, $select:path) => {
11+
$select(&$default, $opt.as_inner_unchecked(), $opt.is_some())
12+
};
13+
}
14+
415
/// Equivalent of [`Option`] but predicated on a [`Choice`] with combinators that allow for
516
/// constant-time operations which always perform the same sequence of instructions regardless of
617
/// the value of `is_some`.
@@ -23,6 +34,21 @@ impl<T> CtOption<T> {
2334
Self { value, is_some }
2435
}
2536

37+
/// Construct a new [`CtOption`] where `self.is_some()` is [`Choice::TRUE`].
38+
#[inline]
39+
pub const fn some(value: T) -> CtOption<T> {
40+
Self::new(value, Choice::TRUE)
41+
}
42+
43+
/// Construct a new [`CtOption`] with the [`Default`] value, and where `self.is_some()` is
44+
/// [`Choice::FALSE`].
45+
pub fn none() -> CtOption<T>
46+
where
47+
T: Default,
48+
{
49+
Self::new(Default::default(), Choice::FALSE)
50+
}
51+
2652
/// Convert from a `&mut CtOption<T>` to `CtOption<&mut T>`.
2753
#[inline]
2854
pub const fn as_mut(&mut self) -> CtOption<&mut T> {
@@ -80,7 +106,7 @@ impl<T> CtOption<T> {
80106
// (needs `const_precise_live_drops`)
81107
#[inline]
82108
#[track_caller]
83-
pub const fn expect_copied(&self, msg: &str) -> T
109+
pub const fn expect_copied(self, msg: &str) -> T
84110
where
85111
T: Copy,
86112
{
@@ -99,7 +125,7 @@ impl<T> CtOption<T> {
99125
pub const fn expect_ref(&self, msg: &str) -> &T {
100126
// TODO(tarcieri): use `self.is_some().to_bool()` when MSRV is 1.86
101127
assert!(self.is_some.to_bool_vartime(), "{}", msg);
102-
&self.value
128+
self.as_inner_unchecked()
103129
}
104130

105131
/// Convert the [`CtOption`] wrapper into an [`Option`], depending on whether
@@ -124,6 +150,29 @@ impl<T> CtOption<T> {
124150
}
125151
}
126152

153+
/// Convert the [`CtOption`] wrapper into an [`Option`] in a `const fn`-friendly manner.
154+
///
155+
/// This is the equivalent of [`CtOption::into_option`] but is `const fn`-friendly by only
156+
/// allowing `Copy` types which are implicitly `!Drop` and don't run into problems with
157+
/// `const fn` and destructors.
158+
///
159+
/// <div class="warning">
160+
/// This implementation doesn't intend to be constant-time nor try to protect the leakage of the
161+
/// `T` value since the [`Option`] will do it anyway.
162+
/// </div>
163+
#[inline]
164+
pub const fn into_option_copied(self) -> Option<T>
165+
where
166+
T: Copy,
167+
{
168+
// TODO(tarcieri): use `self.is_some().to_bool()` when MSRV is 1.86
169+
if self.is_some.to_bool_vartime() {
170+
Some(self.value)
171+
} else {
172+
None
173+
}
174+
}
175+
127176
/// Returns [`Choice::TRUE`] if the option is the equivalent of a `Some`.
128177
#[inline]
129178
#[must_use]
@@ -146,6 +195,13 @@ impl<T> CtOption<T> {
146195
optb
147196
}
148197

198+
/// Apply an additional [`Choice`] requirement to `is_some`.
199+
#[inline]
200+
pub const fn and_choice(mut self, is_some: Choice) -> Self {
201+
self.is_some = self.is_some.and(is_some);
202+
self
203+
}
204+
149205
/// Calls the provided callback with the wrapped inner value, returning the resulting
150206
/// [`CtOption`] value in the event that `self.is_some()` is [`Choice::TRUE`], or if not
151207
/// returns a [`CtOption`] with `self.is_none()`.
@@ -164,6 +220,34 @@ impl<T> CtOption<T> {
164220
ret
165221
}
166222

223+
/// Obtain a reference to the inner value without first checking that `self.is_some()` is
224+
/// [`Choice::TRUE`].
225+
///
226+
/// This method is primarily intended for use in `const fn` scenarios where it's not yet
227+
/// possible to use the safe combinator methods, and returns a reference to avoid issues with
228+
/// `const fn` destructors.
229+
///
230+
/// <div class="warning">
231+
/// <b>Use with care!</b>
232+
///
233+
/// This method does not ensure the `value` is actually valid. Callers of this method should
234+
/// take great care to ensure that `self.is_some()` is checked elsewhere.
235+
/// </div>
236+
#[inline]
237+
pub const fn as_inner_unchecked(&self) -> &T {
238+
&self.value
239+
}
240+
241+
/// DEPRECATED: legacy compatibility method for `crypto-bigint`
242+
#[inline]
243+
#[deprecated(since = "0.1.1", note = "use `as_inner_unchecked` instead")]
244+
pub const fn components_ref(&self) -> (&T, Choice) {
245+
// Since Rust is not smart enough to tell that we would be moving the value,
246+
// and hence no destructors will be called, we have to return a reference instead.
247+
// See https://github.com/rust-lang/rust/issues/66753
248+
(&self.value, self.is_some)
249+
}
250+
167251
/// Calls the provided callback with the wrapped inner value, which computes a [`Choice`],
168252
/// and updates `self.is_some()`.
169253
///
@@ -247,6 +331,27 @@ impl<T> CtOption<T> {
247331
}
248332
}
249333

334+
/// Obtain a copy of the inner value without first checking that `self.is_some()` is
335+
/// [`Choice::TRUE`].
336+
///
337+
/// This method is primarily intended for use in `const fn` scenarios where it's not yet
338+
/// possible to use the safe combinator methods, and uses a `Copy` bound to avoid issues with
339+
/// `const fn` destructors.
340+
///
341+
/// <div class="warning">
342+
/// <b>Use with care!</b>
343+
///
344+
/// This method does not ensure the `value` is actually valid. Callers of this method should
345+
/// take great care to ensure that `self.is_some()` is checked elsewhere.
346+
/// </div>
347+
#[inline]
348+
pub const fn to_inner_unchecked(self) -> T
349+
where
350+
T: Copy,
351+
{
352+
self.value
353+
}
354+
250355
/// Return the contained value, consuming the `self` value.
251356
///
252357
/// Use of this function is discouraged due to panic potential. Instead, prefer non-panicking
@@ -400,6 +505,12 @@ impl<T: CtSelect> CtSelect for CtOption<T> {
400505
}
401506
}
402507

508+
impl<T: Default> Default for CtOption<T> {
509+
fn default() -> Self {
510+
Self::none()
511+
}
512+
}
513+
403514
/// Convert the [`CtOption`] wrapper into an [`Option`], depending on whether
404515
/// [`CtOption::is_some`] is a truthy or falsy [`Choice`].
405516
///

0 commit comments

Comments
 (0)