Skip to content

Commit f603ad3

Browse files
authored
ctutils: add Choice::to_u8_mask/to_u16_mask (#1322)
Rounds out support for mask generation for all core unsigned integer types
1 parent f66e30a commit f603ad3

1 file changed

Lines changed: 124 additions & 70 deletions

File tree

ctutils/src/choice.rs

Lines changed: 124 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -45,61 +45,8 @@ impl Choice {
4545
/// Equivalent of [`true`].
4646
pub const TRUE: Self = Self(1);
4747

48-
/// Convert `Choice` into a `bool`.
49-
///
50-
/// <div class = "warning">
51-
/// <b>Security Warning</b>
52-
///
53-
/// Using this function will introduce timing variability, since computing this at all currently
54-
/// requires a branch.
55-
///
56-
/// This is intended to be used as either the one and only branch at the end of a constant-time
57-
/// operation to e.g. differentiate between success and failure, or in contexts where
58-
/// constant-time doesn't matter, e.g. variable-time code that operates on "maybe secret" types
59-
/// which aren't secrets in a particular context.
60-
///
61-
/// If you are trying to use this in the context of a constant-time operation, be warned that
62-
/// the small amount of timing variability it introduces can potentially be exploited. Whenever
63-
/// possible, prefer fully constant-time approaches instead.
64-
/// </div>
65-
// TODO(tarcieri): `const fn` when MSRV 1.86
66-
pub fn to_bool(self) -> bool {
67-
self.to_u8() != 0
68-
}
69-
70-
/// Convert [`Choice`] to a `u8`, attempting to apply a "best effort" optimization barrier.
71-
// TODO(tarcieri): `const fn` when MSRV 1.86
72-
pub fn to_u8(self) -> u8 {
73-
// `black_box` is documented as working on a "best effort" basis. That's fine, this type is
74-
// likewise documented as only working on a "best effort" basis itself. The only way we
75-
// rely on `black_box` for correctness is it behaving as the identity function.
76-
core::hint::black_box(self.0)
77-
}
78-
79-
/// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
80-
///
81-
/// This does not apply `black_box` to the output.
82-
///
83-
/// <div class = "warning">
84-
/// <b>Security Warning</b>
85-
///
86-
/// See the security warnings for [`Choice::to_bool`].
87-
/// </div>
88-
// TODO(tarcieri): deprecate/remove this in favor of `to_bool` when MSRV is Rust 1.86
89-
pub const fn to_bool_vartime(self) -> bool {
90-
self.0 != 0
91-
}
92-
93-
/// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
94-
///
95-
/// This does not apply `black_box` to the output.
96-
// TODO(tarcieri): deprecate/remove this in favor of `to_u8` when MSRV is Rust 1.86
97-
pub const fn to_u8_vartime(self) -> u8 {
98-
self.0
99-
}
100-
10148
//
102-
// Bitwise ops
49+
// `const fn` bitwise ops
10350
//
10451

10552
/// Apply an `and` conditional to the given [`Choice`]s.
@@ -128,7 +75,7 @@ impl Choice {
12875
}
12976

13077
//
131-
// Comparison ops
78+
// `const fn` comparison ops
13279
//
13380

13481
/// `const fn` equality operation.
@@ -355,14 +302,91 @@ impl Choice {
355302
a ^ (self.to_u128_mask() & (a ^ b))
356303
}
357304

305+
//
306+
// Output conversion methods
307+
//
308+
309+
/// Convert `Choice` into a `bool`.
310+
///
311+
/// <div class = "warning">
312+
/// <b>Security Warning</b>
313+
///
314+
/// Using this function will introduce timing variability, since computing this at all currently
315+
/// requires a branch.
316+
///
317+
/// This is intended to be used as either the one and only branch at the end of a constant-time
318+
/// operation to e.g. differentiate between success and failure, or in contexts where
319+
/// constant-time doesn't matter, e.g. variable-time code that operates on "maybe secret" types
320+
/// which aren't secrets in a particular context.
321+
///
322+
/// If you are trying to use this in the context of a constant-time operation, be warned that
323+
/// the small amount of timing variability it introduces can potentially be exploited. Whenever
324+
/// possible, prefer fully constant-time approaches instead.
325+
/// </div>
326+
// TODO(tarcieri): `const fn` when MSRV 1.86
327+
pub fn to_bool(self) -> bool {
328+
self.to_u8() != 0
329+
}
330+
331+
/// Convert [`Choice`] to a `u8`, attempting to apply a "best effort" optimization barrier.
332+
// TODO(tarcieri): `const fn` when MSRV 1.86
333+
pub fn to_u8(self) -> u8 {
334+
// `black_box` is documented as working on a "best effort" basis. That's fine, this type is
335+
// likewise documented as only working on a "best effort" basis itself. The only way we
336+
// rely on `black_box` for correctness is it behaving as the identity function.
337+
core::hint::black_box(self.0)
338+
}
339+
340+
/// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
341+
///
342+
/// This does not apply `black_box` to the output.
343+
///
344+
/// <div class = "warning">
345+
/// <b>Security Warning</b>
346+
///
347+
/// See the security warnings for [`Choice::to_bool`].
348+
/// </div>
349+
// TODO(tarcieri): deprecate/remove this in favor of `to_bool` when MSRV is Rust 1.86
350+
pub const fn to_bool_vartime(self) -> bool {
351+
self.0 != 0
352+
}
353+
354+
/// HACK: workaround to allow `const fn` boolean support on Rust 1.85.
355+
///
356+
/// This does not apply `black_box` to the output.
357+
// TODO(tarcieri): deprecate/remove this in favor of `to_u8` when MSRV is Rust 1.86
358+
pub const fn to_u8_vartime(self) -> u8 {
359+
self.0
360+
}
361+
362+
/// Create a `u8` bitmask.
363+
///
364+
/// # Returns
365+
/// - `0` for `Choice::FALSE`
366+
/// - `u8::MAX` for `Choice::TRUE`
367+
#[inline]
368+
pub const fn to_u8_mask(self) -> u8 {
369+
self.0.wrapping_neg()
370+
}
371+
372+
/// Create a `u16` bitmask.
373+
///
374+
/// # Returns
375+
/// - `0` for `Choice::FALSE`
376+
/// - `u16::MAX` for `Choice::TRUE`
377+
#[inline]
378+
pub const fn to_u16_mask(self) -> u16 {
379+
(self.0 as u16).wrapping_neg()
380+
}
381+
358382
/// Create a `u32` bitmask.
359383
///
360384
/// # Returns
361385
/// - `0` for `Choice::FALSE`
362386
/// - `u32::MAX` for `Choice::TRUE`
363387
#[inline]
364388
pub const fn to_u32_mask(self) -> u32 {
365-
(self.0 as u32 & 1).wrapping_neg()
389+
(self.0 as u32).wrapping_neg()
366390
}
367391

368392
/// Create a `u64` bitmask.
@@ -372,7 +396,7 @@ impl Choice {
372396
/// - `u64::MAX` for `Choice::TRUE`
373397
#[inline]
374398
pub const fn to_u64_mask(self) -> u64 {
375-
(self.0 as u64 & 1).wrapping_neg()
399+
(self.0 as u64).wrapping_neg()
376400
}
377401

378402
/// Create a `u128` bitmask.
@@ -382,7 +406,7 @@ impl Choice {
382406
/// - `u128::MAX` for `Choice::TRUE`
383407
#[inline]
384408
pub const fn to_u128_mask(self) -> u128 {
385-
(self.0 as u128 & 1).wrapping_neg()
409+
(self.0 as u128).wrapping_neg()
386410
}
387411
}
388412

@@ -553,18 +577,6 @@ mod tests {
553577
assert_eq!(a.ct_select(&b, Choice::TRUE).to_bool(), b.to_bool());
554578
}
555579

556-
#[test]
557-
fn to_bool() {
558-
assert!(!Choice::FALSE.to_bool());
559-
assert!(Choice::TRUE.to_bool());
560-
}
561-
562-
#[test]
563-
fn to_u8() {
564-
assert_eq!(Choice::FALSE.to_u8(), 0);
565-
assert_eq!(Choice::TRUE.to_u8(), 1);
566-
}
567-
568580
#[test]
569581
fn and() {
570582
assert_eq!((Choice::FALSE & Choice::FALSE).to_u8(), 0);
@@ -813,4 +825,46 @@ mod tests {
813825
assert_eq!(Choice::TRUE.select_u128(a, b), b);
814826
assert_eq!(Choice::FALSE.select_u128(a, b), a);
815827
}
828+
829+
#[test]
830+
fn to_bool() {
831+
assert!(!Choice::FALSE.to_bool());
832+
assert!(Choice::TRUE.to_bool());
833+
}
834+
835+
#[test]
836+
fn to_u8() {
837+
assert_eq!(Choice::FALSE.to_u8(), 0);
838+
assert_eq!(Choice::TRUE.to_u8(), 1);
839+
}
840+
841+
#[test]
842+
fn to_u8_mask() {
843+
assert_eq!(Choice::FALSE.to_u8_mask(), 0);
844+
assert_eq!(Choice::TRUE.to_u8_mask(), u8::MAX);
845+
}
846+
847+
#[test]
848+
fn to_u16_mask() {
849+
assert_eq!(Choice::FALSE.to_u16_mask(), 0);
850+
assert_eq!(Choice::TRUE.to_u16_mask(), u16::MAX);
851+
}
852+
853+
#[test]
854+
fn to_u32_mask() {
855+
assert_eq!(Choice::FALSE.to_u32_mask(), 0);
856+
assert_eq!(Choice::TRUE.to_u32_mask(), u32::MAX);
857+
}
858+
859+
#[test]
860+
fn to_u64_mask() {
861+
assert_eq!(Choice::FALSE.to_u64_mask(), 0);
862+
assert_eq!(Choice::TRUE.to_u64_mask(), u64::MAX);
863+
}
864+
865+
#[test]
866+
fn to_u128_mask() {
867+
assert_eq!(Choice::FALSE.to_u128_mask(), 0);
868+
assert_eq!(Choice::TRUE.to_u128_mask(), u128::MAX);
869+
}
816870
}

0 commit comments

Comments
 (0)