Rename Maybe to CtOption, and do not expose submodule.

This commit is contained in:
Sean Bowe 2019-03-26 21:04:51 -06:00
parent 26de2362db
commit 390aa23db2
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
4 changed files with 57 additions and 57 deletions

View File

@ -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. //! alternative for APIs that want to return optional values.
//! Ideally, this would be merged into upstream `subtle` at some //! Ideally, this would be merged into upstream `subtle` at some
//! point. //! point.
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; 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 /// [`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 /// `Some` or `None`, but unlike `Option<T>` these variants are
/// not exposed. The `is_some()` method is used to determine if the /// not exposed. The `is_some()` method is used to determine if the
/// value is `Some`, and `unwrap_or`/`unwrap_or_else` methods are /// 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 /// about the result in constant time, and returning an incorrect
/// value burdens the caller and increases the chance of bugs. /// value burdens the caller and increases the chance of bugs.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Maybe<T> { pub struct CtOption<T> {
value: T, value: T,
is_some: Choice, is_some: Choice,
} }
impl<T> Maybe<T> { impl<T> CtOption<T> {
/// This method is used to construct a new `Maybe<T>` and takes /// This method is used to construct a new `CtOption<T>` and takes
/// a value of type `T`, and a `Choice` that determines whether /// a value of type `T`, and a `Choice` that determines whether
/// the optional value should be `Some` or not. If `is_some` is /// the optional value should be `Some` or not. If `is_some` is
/// false, the value will still be stored but its value is never /// false, the value will still be stored but its value is never
/// exposed. /// exposed.
#[inline] #[inline]
pub fn new(value: T, is_some: Choice) -> Maybe<T> { pub fn new(value: T, is_some: Choice) -> CtOption<T> {
Maybe { value, is_some } CtOption { value, is_some }
} }
/// This returns the underlying value but panics if it /// 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 `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 /// The closure is given the enclosed value or, if the option is
/// `None`, it is provided a dummy value computed using /// `None`, it is provided a dummy value computed using
/// `Default::default()`. /// `Default::default()`.
@ -88,12 +88,12 @@ impl<T> Maybe<T> {
/// This operates in constant time, because the provided closure /// This operates in constant time, because the provided closure
/// is always called. /// is always called.
#[inline] #[inline]
pub fn map<U, F>(self, f: F) -> Maybe<U> pub fn map<U, F>(self, f: F) -> CtOption<U>
where where
T: Default + ConditionallySelectable, T: Default + ConditionallySelectable,
F: FnOnce(T) -> U, F: FnOnce(T) -> U,
{ {
Maybe::new( CtOption::new(
f(T::conditional_select( f(T::conditional_select(
&T::default(), &T::default(),
&self.value, &self.value,
@ -111,10 +111,10 @@ impl<T> Maybe<T> {
/// This operates in constant time, because the provided closure /// This operates in constant time, because the provided closure
/// is always called. /// is always called.
#[inline] #[inline]
pub fn and_then<U, F>(self, f: F) -> Maybe<U> pub fn and_then<U, F>(self, f: F) -> CtOption<U>
where where
T: Default + ConditionallySelectable, T: Default + ConditionallySelectable,
F: FnOnce(T) -> Maybe<U>, F: FnOnce(T) -> CtOption<U>,
{ {
let mut tmp = f(T::conditional_select( let mut tmp = f(T::conditional_select(
&T::default(), &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 { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Maybe::new( CtOption::new(
T::conditional_select(&a.value, &b.value, choice), T::conditional_select(&a.value, &b.value, choice),
// TODO: subtle crate currently doesn't implement ConditionallySelectable // TODO: subtle crate currently doesn't implement ConditionallySelectable
// for Choice so we must unwrap these manually. // 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> { impl<T: ConstantTimeEq> ConstantTimeEq for CtOption<T> {
/// Two `Maybe<T>`s are equal if they are both `Some` and /// Two `CtOption<T>`s are equal if they are both `Some` and
/// their values are equal, or both `None`. /// their values are equal, or both `None`.
#[inline] #[inline]
fn ct_eq(&self, rhs: &Maybe<T>) -> Choice { fn ct_eq(&self, rhs: &CtOption<T>) -> Choice {
let a = self.is_some(); let a = self.is_some();
let b = rhs.is_some(); let b = rhs.is_some();
@ -155,11 +155,11 @@ impl<T: ConstantTimeEq> ConstantTimeEq for Maybe<T> {
} }
#[test] #[test]
fn test_maybe() { fn test_ctoption() {
let a = Maybe::new(10, Choice::from(1)); let a = CtOption::new(10, Choice::from(1));
let b = Maybe::new(9, Choice::from(1)); let b = CtOption::new(9, Choice::from(1));
let c = Maybe::new(10, Choice::from(0)); let c = CtOption::new(10, Choice::from(0));
let d = Maybe::new(9, Choice::from(0)); let d = CtOption::new(9, Choice::from(0));
// Test is_some / is_none // Test is_some / is_none
assert!(bool::from(a.is_some())); assert!(bool::from(a.is_some()));
@ -186,16 +186,16 @@ fn test_maybe() {
assert!(bool::from(c.ct_eq(&d))); assert!(bool::from(c.ct_eq(&d)));
// Test unwrap_or // Test unwrap_or
assert_eq!(Maybe::new(1, Choice::from(1)).unwrap_or(2), 1); assert_eq!(CtOption::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(0)).unwrap_or(2), 2);
// Test unwrap_or_else // Test unwrap_or_else
assert_eq!(Maybe::new(1, Choice::from(1)).unwrap_or_else(|| 2), 1); assert_eq!(CtOption::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(0)).unwrap_or_else(|| 2), 2);
// Test map // Test map
assert_eq!( assert_eq!(
Maybe::new(1, Choice::from(1)) CtOption::new(1, Choice::from(1))
.map(|v| { .map(|v| {
assert_eq!(v, 1); assert_eq!(v, 1);
2 2
@ -204,7 +204,7 @@ fn test_maybe() {
2 2
); );
assert_eq!( assert_eq!(
Maybe::new(1, Choice::from(0)) CtOption::new(1, Choice::from(0))
.map(|_| 2) .map(|_| 2)
.is_none() .is_none()
.unwrap_u8(), .unwrap_u8(),
@ -213,35 +213,35 @@ fn test_maybe() {
// Test and_then // Test and_then
assert_eq!( assert_eq!(
Maybe::new(1, Choice::from(1)) CtOption::new(1, Choice::from(1))
.and_then(|v| { .and_then(|v| {
assert_eq!(v, 1); assert_eq!(v, 1);
Maybe::new(2, Choice::from(0)) CtOption::new(2, Choice::from(0))
}) })
.is_none() .is_none()
.unwrap_u8(), .unwrap_u8(),
1 1
); );
assert_eq!( assert_eq!(
Maybe::new(1, Choice::from(1)) CtOption::new(1, Choice::from(1))
.and_then(|v| { .and_then(|v| {
assert_eq!(v, 1); assert_eq!(v, 1);
Maybe::new(2, Choice::from(1)) CtOption::new(2, Choice::from(1))
}) })
.unwrap(), .unwrap(),
2 2
); );
assert_eq!( assert_eq!(
Maybe::new(1, Choice::from(0)) CtOption::new(1, Choice::from(0))
.and_then(|_| Maybe::new(2, Choice::from(0))) .and_then(|_| CtOption::new(2, Choice::from(0)))
.is_none() .is_none()
.unwrap_u8(), .unwrap_u8(),
1 1
); );
assert_eq!( assert_eq!(
Maybe::new(1, Choice::from(0)) CtOption::new(1, Choice::from(0))
.and_then(|_| Maybe::new(2, Choice::from(1))) .and_then(|_| CtOption::new(2, Choice::from(1)))
.is_none() .is_none()
.unwrap_u8(), .unwrap_u8(),
1 1
@ -250,8 +250,8 @@ fn test_maybe() {
#[test] #[test]
#[should_panic] #[should_panic]
fn unwrap_none_maybe() { fn unwrap_none_ctoption() {
// This test might fail (in release mode?) if the // This test might fail (in release mode?) if the
// compiler decides to optimize it away. // compiler decides to optimize it away.
Maybe::new(10, Choice::from(0)).unwrap(); CtOption::new(10, Choice::from(0)).unwrap();
} }

View File

@ -4,8 +4,8 @@ use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
use crate::maybe::Maybe;
use crate::util::{adc, mac, sbb}; use crate::util::{adc, mac, sbb};
use crate::CtOption;
/// Represents an element of `GF(q)`. /// Represents an element of `GF(q)`.
// The internal representation of this type is four 64-bit unsigned // 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 /// Attempts to convert a little-endian byte representation of
/// a field element into an element of `Fq`, failing if the input /// a field element into an element of `Fq`, failing if the input
/// is not canonical (is not smaller than q). /// 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]); let mut tmp = Fq([0, 0, 0, 0]);
tmp.0[0] = LittleEndian::read_u64(&bytes[0..8]); tmp.0[0] = LittleEndian::read_u64(&bytes[0..8]);
@ -231,7 +231,7 @@ impl Fq {
// (a.R^{-1} * R^2) / R = a.R // (a.R^{-1} * R^2) / R = a.R
tmp *= &R2; 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 /// 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. /// 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 // Tonelli-Shank's algorithm for q mod 16 = 1
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
@ -366,7 +366,7 @@ impl Fq {
v = k; v = k;
} }
Maybe::new( CtOption::new(
x, x,
(&x * &x).ct_eq(self), // Only return Some if it's the square root. (&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, /// Computes the multiplicative inverse of this element,
/// failing if the element is zero. /// failing if the element is zero.
pub fn invert(&self) -> Maybe<Self> { pub fn invert(&self) -> CtOption<Self> {
#[inline(always)] #[inline(always)]
fn square_assign_multi(n: &mut Fq, num_times: usize) { fn square_assign_multi(n: &mut Fq, num_times: usize) {
for _ in 0..num_times { for _ in 0..num_times {
@ -504,7 +504,7 @@ impl Fq {
square_assign_multi(&mut t0, 5); square_assign_multi(&mut t0, 5);
t0.mul_assign(&t1); t0.mul_assign(&t1);
Maybe::new(t0, !self.ct_eq(&Self::zero())) CtOption::new(t0, !self.ct_eq(&Self::zero()))
} }
#[inline] #[inline]

View File

@ -4,8 +4,8 @@ use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
use crate::maybe::Maybe;
use crate::util::{adc, mac, sbb}; use crate::util::{adc, mac, sbb};
use crate::CtOption;
/// Represents an element of `GF(r)`. /// Represents an element of `GF(r)`.
// The internal representation of this type is four 64-bit unsigned // 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 /// Attempts to convert a little-endian byte representation of
/// a field element into an element of `Fr`, failing if the input /// a field element into an element of `Fr`, failing if the input
/// is not canonical (is not smaller than r). /// 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]); let mut tmp = Fr([0, 0, 0, 0]);
tmp.0[0] = LittleEndian::read_u64(&bytes[0..8]); tmp.0[0] = LittleEndian::read_u64(&bytes[0..8]);
@ -213,7 +213,7 @@ impl Fr {
// (a.R^{-1} * R^2) / R = a.R // (a.R^{-1} * R^2) / R = a.R
tmp *= &R2; 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 /// 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. /// 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) // Because r = 3 (mod 4)
// sqrt can be done with only one exponentiation, // sqrt can be done with only one exponentiation,
// via the computation of self^((r + 1) // 4) (mod r) // via the computation of self^((r + 1) // 4) (mod r)
@ -317,7 +317,7 @@ impl Fr {
0x039f6d3a994cebea, 0x039f6d3a994cebea,
]); ]);
Maybe::new( CtOption::new(
sqrt, sqrt,
(&sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. (&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, /// Computes the multiplicative inverse of this element,
/// failing if the element is zero. /// failing if the element is zero.
pub fn invert(&self) -> Maybe<Self> { pub fn invert(&self) -> CtOption<Self> {
#[inline(always)] #[inline(always)]
fn square_assign_multi(n: &mut Fr, num_times: usize) { fn square_assign_multi(n: &mut Fr, num_times: usize) {
for _ in 0..num_times { for _ in 0..num_times {
@ -462,7 +462,7 @@ impl Fr {
square_assign_multi(&mut t0, 7); square_assign_multi(&mut t0, 7);
t0.mul_assign(&t1); t0.mul_assign(&t1);
Maybe::new(t0, !self.ct_eq(&Self::zero())) CtOption::new(t0, !self.ct_eq(&Self::zero()))
} }
#[inline] #[inline]

View File

@ -41,8 +41,8 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
#[macro_use] #[macro_use]
mod util; mod util;
pub mod maybe; mod ctoption;
use maybe::Maybe; pub use ctoption::CtOption;
mod fq; mod fq;
mod fr; mod fr;
@ -318,7 +318,7 @@ impl AffinePoint {
/// Attempts to interpret a byte representation of an /// Attempts to interpret a byte representation of an
/// affine point, failing if the element is not on /// affine point, failing if the element is not on
/// the curve or non-canonical. /// 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 // Grab the sign bit from the representation
let sign = b[31] >> 7; let sign = b[31] >> 7;
@ -348,7 +348,7 @@ impl AffinePoint {
let u_negated = -u; let u_negated = -u;
let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); 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))
}) })
}) })
} }