Rename Maybe to CtOption, and do not expose submodule.
This commit is contained in:
parent
26de2362db
commit
390aa23db2
|
@ -1,13 +1,13 @@
|
|||
//! This module provides a "Maybe" abstraction as a constant-time
|
||||
//! This module provides a "CtOption" abstraction as a constant-time
|
||||
//! alternative for APIs that want to return optional values.
|
||||
//! Ideally, this would be merged into upstream `subtle` at some
|
||||
//! point.
|
||||
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
/// The `Maybe<T>` type represents an optional value similar to the
|
||||
/// The `CtOption<T>` type represents an optional value similar to the
|
||||
/// [`Option<T>`](core::option::Option) type but is intended for
|
||||
/// use in constant time APIs. Any given `Maybe<T>` is either
|
||||
/// use in constant time APIs. Any given `CtOption<T>` is either
|
||||
/// `Some` or `None`, but unlike `Option<T>` these variants are
|
||||
/// not exposed. The `is_some()` method is used to determine if the
|
||||
/// value is `Some`, and `unwrap_or`/`unwrap_or_else` methods are
|
||||
|
@ -21,20 +21,20 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
|||
/// about the result in constant time, and returning an incorrect
|
||||
/// value burdens the caller and increases the chance of bugs.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Maybe<T> {
|
||||
pub struct CtOption<T> {
|
||||
value: T,
|
||||
is_some: Choice,
|
||||
}
|
||||
|
||||
impl<T> Maybe<T> {
|
||||
/// This method is used to construct a new `Maybe<T>` and takes
|
||||
impl<T> CtOption<T> {
|
||||
/// This method is used to construct a new `CtOption<T>` and takes
|
||||
/// a value of type `T`, and a `Choice` that determines whether
|
||||
/// the optional value should be `Some` or not. If `is_some` is
|
||||
/// false, the value will still be stored but its value is never
|
||||
/// exposed.
|
||||
#[inline]
|
||||
pub fn new(value: T, is_some: Choice) -> Maybe<T> {
|
||||
Maybe { value, is_some }
|
||||
pub fn new(value: T, is_some: Choice) -> CtOption<T> {
|
||||
CtOption { value, is_some }
|
||||
}
|
||||
|
||||
/// This returns the underlying value but panics if it
|
||||
|
@ -80,7 +80,7 @@ impl<T> Maybe<T> {
|
|||
}
|
||||
|
||||
/// Returns a `None` value if the option is `None`, otherwise
|
||||
/// returns a `Maybe` enclosing the value of the provided closure.
|
||||
/// returns a `CtOption` enclosing the value of the provided closure.
|
||||
/// The closure is given the enclosed value or, if the option is
|
||||
/// `None`, it is provided a dummy value computed using
|
||||
/// `Default::default()`.
|
||||
|
@ -88,12 +88,12 @@ impl<T> Maybe<T> {
|
|||
/// This operates in constant time, because the provided closure
|
||||
/// is always called.
|
||||
#[inline]
|
||||
pub fn map<U, F>(self, f: F) -> Maybe<U>
|
||||
pub fn map<U, F>(self, f: F) -> CtOption<U>
|
||||
where
|
||||
T: Default + ConditionallySelectable,
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
Maybe::new(
|
||||
CtOption::new(
|
||||
f(T::conditional_select(
|
||||
&T::default(),
|
||||
&self.value,
|
||||
|
@ -111,10 +111,10 @@ impl<T> Maybe<T> {
|
|||
/// This operates in constant time, because the provided closure
|
||||
/// is always called.
|
||||
#[inline]
|
||||
pub fn and_then<U, F>(self, f: F) -> Maybe<U>
|
||||
pub fn and_then<U, F>(self, f: F) -> CtOption<U>
|
||||
where
|
||||
T: Default + ConditionallySelectable,
|
||||
F: FnOnce(T) -> Maybe<U>,
|
||||
F: FnOnce(T) -> CtOption<U>,
|
||||
{
|
||||
let mut tmp = f(T::conditional_select(
|
||||
&T::default(),
|
||||
|
@ -127,9 +127,9 @@ impl<T> Maybe<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ConditionallySelectable> ConditionallySelectable for Maybe<T> {
|
||||
impl<T: ConditionallySelectable> ConditionallySelectable for CtOption<T> {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Maybe::new(
|
||||
CtOption::new(
|
||||
T::conditional_select(&a.value, &b.value, choice),
|
||||
// TODO: subtle crate currently doesn't implement ConditionallySelectable
|
||||
// for Choice so we must unwrap these manually.
|
||||
|
@ -142,11 +142,11 @@ impl<T: ConditionallySelectable> ConditionallySelectable for Maybe<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ConstantTimeEq> ConstantTimeEq for Maybe<T> {
|
||||
/// Two `Maybe<T>`s are equal if they are both `Some` and
|
||||
impl<T: ConstantTimeEq> ConstantTimeEq for CtOption<T> {
|
||||
/// Two `CtOption<T>`s are equal if they are both `Some` and
|
||||
/// their values are equal, or both `None`.
|
||||
#[inline]
|
||||
fn ct_eq(&self, rhs: &Maybe<T>) -> Choice {
|
||||
fn ct_eq(&self, rhs: &CtOption<T>) -> Choice {
|
||||
let a = self.is_some();
|
||||
let b = rhs.is_some();
|
||||
|
||||
|
@ -155,11 +155,11 @@ impl<T: ConstantTimeEq> ConstantTimeEq for Maybe<T> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_maybe() {
|
||||
let a = Maybe::new(10, Choice::from(1));
|
||||
let b = Maybe::new(9, Choice::from(1));
|
||||
let c = Maybe::new(10, Choice::from(0));
|
||||
let d = Maybe::new(9, Choice::from(0));
|
||||
fn test_ctoption() {
|
||||
let a = CtOption::new(10, Choice::from(1));
|
||||
let b = CtOption::new(9, Choice::from(1));
|
||||
let c = CtOption::new(10, Choice::from(0));
|
||||
let d = CtOption::new(9, Choice::from(0));
|
||||
|
||||
// Test is_some / is_none
|
||||
assert!(bool::from(a.is_some()));
|
||||
|
@ -186,16 +186,16 @@ fn test_maybe() {
|
|||
assert!(bool::from(c.ct_eq(&d)));
|
||||
|
||||
// Test unwrap_or
|
||||
assert_eq!(Maybe::new(1, Choice::from(1)).unwrap_or(2), 1);
|
||||
assert_eq!(Maybe::new(1, Choice::from(0)).unwrap_or(2), 2);
|
||||
assert_eq!(CtOption::new(1, Choice::from(1)).unwrap_or(2), 1);
|
||||
assert_eq!(CtOption::new(1, Choice::from(0)).unwrap_or(2), 2);
|
||||
|
||||
// Test unwrap_or_else
|
||||
assert_eq!(Maybe::new(1, Choice::from(1)).unwrap_or_else(|| 2), 1);
|
||||
assert_eq!(Maybe::new(1, Choice::from(0)).unwrap_or_else(|| 2), 2);
|
||||
assert_eq!(CtOption::new(1, Choice::from(1)).unwrap_or_else(|| 2), 1);
|
||||
assert_eq!(CtOption::new(1, Choice::from(0)).unwrap_or_else(|| 2), 2);
|
||||
|
||||
// Test map
|
||||
assert_eq!(
|
||||
Maybe::new(1, Choice::from(1))
|
||||
CtOption::new(1, Choice::from(1))
|
||||
.map(|v| {
|
||||
assert_eq!(v, 1);
|
||||
2
|
||||
|
@ -204,7 +204,7 @@ fn test_maybe() {
|
|||
2
|
||||
);
|
||||
assert_eq!(
|
||||
Maybe::new(1, Choice::from(0))
|
||||
CtOption::new(1, Choice::from(0))
|
||||
.map(|_| 2)
|
||||
.is_none()
|
||||
.unwrap_u8(),
|
||||
|
@ -213,35 +213,35 @@ fn test_maybe() {
|
|||
|
||||
// Test and_then
|
||||
assert_eq!(
|
||||
Maybe::new(1, Choice::from(1))
|
||||
CtOption::new(1, Choice::from(1))
|
||||
.and_then(|v| {
|
||||
assert_eq!(v, 1);
|
||||
Maybe::new(2, Choice::from(0))
|
||||
CtOption::new(2, Choice::from(0))
|
||||
})
|
||||
.is_none()
|
||||
.unwrap_u8(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
Maybe::new(1, Choice::from(1))
|
||||
CtOption::new(1, Choice::from(1))
|
||||
.and_then(|v| {
|
||||
assert_eq!(v, 1);
|
||||
Maybe::new(2, Choice::from(1))
|
||||
CtOption::new(2, Choice::from(1))
|
||||
})
|
||||
.unwrap(),
|
||||
2
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Maybe::new(1, Choice::from(0))
|
||||
.and_then(|_| Maybe::new(2, Choice::from(0)))
|
||||
CtOption::new(1, Choice::from(0))
|
||||
.and_then(|_| CtOption::new(2, Choice::from(0)))
|
||||
.is_none()
|
||||
.unwrap_u8(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
Maybe::new(1, Choice::from(0))
|
||||
.and_then(|_| Maybe::new(2, Choice::from(1)))
|
||||
CtOption::new(1, Choice::from(0))
|
||||
.and_then(|_| CtOption::new(2, Choice::from(1)))
|
||||
.is_none()
|
||||
.unwrap_u8(),
|
||||
1
|
||||
|
@ -250,8 +250,8 @@ fn test_maybe() {
|
|||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn unwrap_none_maybe() {
|
||||
fn unwrap_none_ctoption() {
|
||||
// This test might fail (in release mode?) if the
|
||||
// compiler decides to optimize it away.
|
||||
Maybe::new(10, Choice::from(0)).unwrap();
|
||||
CtOption::new(10, Choice::from(0)).unwrap();
|
||||
}
|
14
src/fq.rs
14
src/fq.rs
|
@ -4,8 +4,8 @@ use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
|||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
use crate::maybe::Maybe;
|
||||
use crate::util::{adc, mac, sbb};
|
||||
use crate::CtOption;
|
||||
|
||||
/// Represents an element of `GF(q)`.
|
||||
// The internal representation of this type is four 64-bit unsigned
|
||||
|
@ -208,7 +208,7 @@ impl Fq {
|
|||
/// Attempts to convert a little-endian byte representation of
|
||||
/// a field element into an element of `Fq`, failing if the input
|
||||
/// is not canonical (is not smaller than q).
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> Maybe<Fq> {
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Fq> {
|
||||
let mut tmp = Fq([0, 0, 0, 0]);
|
||||
|
||||
tmp.0[0] = LittleEndian::read_u64(&bytes[0..8]);
|
||||
|
@ -231,7 +231,7 @@ impl Fq {
|
|||
// (a.R^{-1} * R^2) / R = a.R
|
||||
tmp *= &R2;
|
||||
|
||||
Maybe::new(tmp, Choice::from(is_some))
|
||||
CtOption::new(tmp, Choice::from(is_some))
|
||||
}
|
||||
|
||||
/// Converts an element of `Fq` into a byte representation in
|
||||
|
@ -324,7 +324,7 @@ impl Fq {
|
|||
}
|
||||
|
||||
/// Computes the square root of this element, if it exists.
|
||||
pub fn sqrt(&self) -> Maybe<Self> {
|
||||
pub fn sqrt(&self) -> CtOption<Self> {
|
||||
// Tonelli-Shank's algorithm for q mod 16 = 1
|
||||
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
|
||||
|
||||
|
@ -366,7 +366,7 @@ impl Fq {
|
|||
v = k;
|
||||
}
|
||||
|
||||
Maybe::new(
|
||||
CtOption::new(
|
||||
x,
|
||||
(&x * &x).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
|
@ -409,7 +409,7 @@ impl Fq {
|
|||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
pub fn invert(&self) -> Maybe<Self> {
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
#[inline(always)]
|
||||
fn square_assign_multi(n: &mut Fq, num_times: usize) {
|
||||
for _ in 0..num_times {
|
||||
|
@ -504,7 +504,7 @@ impl Fq {
|
|||
square_assign_multi(&mut t0, 5);
|
||||
t0.mul_assign(&t1);
|
||||
|
||||
Maybe::new(t0, !self.ct_eq(&Self::zero()))
|
||||
CtOption::new(t0, !self.ct_eq(&Self::zero()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
14
src/fr.rs
14
src/fr.rs
|
@ -4,8 +4,8 @@ use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
|||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
|
||||
use crate::maybe::Maybe;
|
||||
use crate::util::{adc, mac, sbb};
|
||||
use crate::CtOption;
|
||||
|
||||
/// Represents an element of `GF(r)`.
|
||||
// The internal representation of this type is four 64-bit unsigned
|
||||
|
@ -190,7 +190,7 @@ impl Fr {
|
|||
/// Attempts to convert a little-endian byte representation of
|
||||
/// a field element into an element of `Fr`, failing if the input
|
||||
/// is not canonical (is not smaller than r).
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> Maybe<Fr> {
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Fr> {
|
||||
let mut tmp = Fr([0, 0, 0, 0]);
|
||||
|
||||
tmp.0[0] = LittleEndian::read_u64(&bytes[0..8]);
|
||||
|
@ -213,7 +213,7 @@ impl Fr {
|
|||
// (a.R^{-1} * R^2) / R = a.R
|
||||
tmp *= &R2;
|
||||
|
||||
Maybe::new(tmp, Choice::from(is_some))
|
||||
CtOption::new(tmp, Choice::from(is_some))
|
||||
}
|
||||
|
||||
/// Converts an element of `Fr` into a byte representation in
|
||||
|
@ -306,7 +306,7 @@ impl Fr {
|
|||
}
|
||||
|
||||
/// Computes the square root of this element, if it exists.
|
||||
pub fn sqrt(&self) -> Maybe<Self> {
|
||||
pub fn sqrt(&self) -> CtOption<Self> {
|
||||
// Because r = 3 (mod 4)
|
||||
// sqrt can be done with only one exponentiation,
|
||||
// via the computation of self^((r + 1) // 4) (mod r)
|
||||
|
@ -317,7 +317,7 @@ impl Fr {
|
|||
0x039f6d3a994cebea,
|
||||
]);
|
||||
|
||||
Maybe::new(
|
||||
CtOption::new(
|
||||
sqrt,
|
||||
(&sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root.
|
||||
)
|
||||
|
@ -360,7 +360,7 @@ impl Fr {
|
|||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
pub fn invert(&self) -> Maybe<Self> {
|
||||
pub fn invert(&self) -> CtOption<Self> {
|
||||
#[inline(always)]
|
||||
fn square_assign_multi(n: &mut Fr, num_times: usize) {
|
||||
for _ in 0..num_times {
|
||||
|
@ -462,7 +462,7 @@ impl Fr {
|
|||
square_assign_multi(&mut t0, 7);
|
||||
t0.mul_assign(&t1);
|
||||
|
||||
Maybe::new(t0, !self.ct_eq(&Self::zero()))
|
||||
CtOption::new(t0, !self.ct_eq(&Self::zero()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -41,8 +41,8 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
|||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
pub mod maybe;
|
||||
use maybe::Maybe;
|
||||
mod ctoption;
|
||||
pub use ctoption::CtOption;
|
||||
|
||||
mod fq;
|
||||
mod fr;
|
||||
|
@ -318,7 +318,7 @@ impl AffinePoint {
|
|||
/// Attempts to interpret a byte representation of an
|
||||
/// affine point, failing if the element is not on
|
||||
/// the curve or non-canonical.
|
||||
pub fn from_bytes(mut b: [u8; 32]) -> Maybe<Self> {
|
||||
pub fn from_bytes(mut b: [u8; 32]) -> CtOption<Self> {
|
||||
// Grab the sign bit from the representation
|
||||
let sign = b[31] >> 7;
|
||||
|
||||
|
@ -348,7 +348,7 @@ impl AffinePoint {
|
|||
let u_negated = -u;
|
||||
let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
|
||||
|
||||
Maybe::new(AffinePoint { u: final_u, v }, Choice::from(1u8))
|
||||
CtOption::new(AffinePoint { u: final_u, v }, Choice::from(1u8))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue