diff --git a/build.rs b/build.rs index fe4b1fe..6131ed5 100644 --- a/build.rs +++ b/build.rs @@ -36,12 +36,15 @@ mod edwards; mod ristretto; #[path="src/constants.rs"] mod constants; +#[path="src/traits.rs"] +mod traits; // Internal modules #[path="src/field.rs"] mod field; - +#[path="src/curve_models/mod.rs"] +mod curve_models; #[path="src/backend/mod.rs"] mod backend; @@ -65,9 +68,10 @@ use backend::u64::field::FieldElement64; #[cfg(not(feature=\"radix_51\"))] use backend::u32::field::FieldElement32; -use edwards::AffineNielsPoint; use edwards::EdwardsBasepointTable; +use curve_models::AffineNielsPoint; + /// Table containing precomputed multiples of the basepoint `B = (x,4/5)`. /// /// The table is defined so `constants::base[i][j-1] = j*(16^2i)*B`, diff --git a/src/constants.rs b/src/constants.rs index 3ba6ce4..27c06ef 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -18,7 +18,7 @@ //! //! ``` //! use curve25519_dalek::constants; -//! use curve25519_dalek::edwards::IsIdentity; +//! use curve25519_dalek::traits::IsIdentity; //! //! let B = &constants::RISTRETTO_BASEPOINT_TABLE; //! let l = &constants::BASEPOINT_ORDER; @@ -102,8 +102,7 @@ pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable #[cfg(test)] mod test { use field::FieldElement; - use edwards::IsIdentity; - use edwards::ValidityCheck; + use traits::{IsIdentity, ValidityCheck}; use constants; #[test] diff --git a/src/curve_models/mod.rs b/src/curve_models/mod.rs new file mode 100644 index 0000000..d35b9c1 --- /dev/null +++ b/src/curve_models/mod.rs @@ -0,0 +1,505 @@ +// -*- mode: rust; -*- +// +// This file is part of curve25519-dalek. +// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft +// - Henry de Valence + +//! This module contains internal curve representations which are not part +//! of the public API. +//! +//! # Curve representations +//! +//! Internally, we use several different models for the curve. Here +//! is a sketch of the relationship between the models, following [a +//! post](https://moderncrypto.org/mail-archive/curves/2016/000807.html) +//! by Ben Smith on the moderncrypto mailing list. +//! +//! Begin with the affine equation for the curve, +//! +//!     -x² + y² = 1 + dx²y².       (1) +//! +//! Next, pass to the projective closure 𝗣^1 x 𝗣^1 by setting x=X/Z, +//! y=Y/T. Clearing denominators gives the model +//! +//!     -X²T² + Y²Z² = Z²T² + dX²Y². (2) +//! +//! To map from 𝗣^1 x 𝗣^1, a product of two lines, to 𝗣^3, we use the +//! Segre embedding, +//! +//!     σ : ((X:Z),(Y:T)) ↦ (XY:XT:ZY:ZT).  (3) +//! +//! Using coordinates (W₀:W₁:W₂:W₃) for 𝗣^3, the image of σ(𝗣^1 x 𝗣^1) +//! is the surface defined by W₀W₃=W₁W₂, and under σ, equation (2) +//! becomes +//! +//!     -W₁² + W₂² = W₃² + dW₀².   (4) +//! +//! Up to variable naming, this is exactly the curve model introduced +//! in ["Twisted Edwards Curves +//! Revisited"](https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf) +//! by Hisil, Wong, Carter, and Dawson. We can map from 𝗣^3 to 𝗣² by +//! sending (W₀:W₁:W₂:W₃) to (W₁:W₂:W₃). Notice that +//! +//!     W₁/W₃ = XT/ZT = X/Z = x    (5) +//! +//!     W₂/W₃ = ZY/ZT = Y/T = y,   (6) +//! +//! so this is the same as if we had started with the affine model (1) +//! and passed to 𝗣^2 by setting `x = W₁/W₃`, `y = W₂/W₃`. Up to +//! variable naming, this is the projective representation introduced +//! in ["Twisted Edwards Curves"](https://eprint.iacr.org/2008/013). +//! +//! Following the implementation strategy in the ref10 reference +//! implementation for [Ed25519](https://ed25519.cr.yp.to/ed25519-20110926.pdf), +//! we use several different models for curve points: +//! +//! * `CompletedPoint`: points in 𝗣^1 x 𝗣^1; +//! * `ExtendedPoint`: points in 𝗣^3; +//! * `ProjectivePoint`: points in 𝗣^2. +//! +//! Finally, to accelerate additions, we use two cached point formats, +//! one for the affine model and one for the 𝗣^3 model: +//! +//! * `AffineNielsPoint`: `(y+x, y-x, 2dxy)` +//! * `ProjectiveNielsPoint`: `(Y+X, Y-X, Z, 2dXY)` +//! +//! [1]: https://moderncrypto.org/mail-archive/curves/2016/000807.html + +#![allow(non_snake_case)] + +use core::fmt::Debug; +use core::ops::{Add, Sub, Neg}; +use core::ops::Index; + +use constants; + +use field::FieldElement; + +use edwards::ExtendedPoint; +use edwards::CompressedEdwardsY; +use montgomery::MontgomeryPoint; + +use subtle::ConditionallyAssignable; + +use traits::ValidityCheck; + + +// ------------------------------------------------------------------------ +// Internal point representations +// ------------------------------------------------------------------------ + +/// A `ProjectivePoint` is a point on the curve in 𝗣²(𝔽ₚ). +/// A point (x,y) in the affine model corresponds to (x:y:1). +#[derive(Copy, Clone)] +pub struct ProjectivePoint { + pub X: FieldElement, + pub Y: FieldElement, + pub Z: FieldElement, +} + +/// A `CompletedPoint` is a point ((X:Z), (Y:T)) in 𝗣¹(𝔽ₚ)×𝗣¹(𝔽ₚ). +/// A point (x,y) in the affine model corresponds to ((x:1),(y:1)). +#[derive(Copy, Clone)] +#[allow(missing_docs)] +pub struct CompletedPoint { + pub X: FieldElement, + pub Y: FieldElement, + pub Z: FieldElement, + pub T: FieldElement, +} + +/// A pre-computed point in the affine model for the curve, represented as +/// (y+x, y-x, 2dxy). These precomputations accelerate addition and +/// subtraction, and were introduced by Niels Duif in the ed25519 paper +/// ["High-Speed High-Security Signatures"](https://ed25519.cr.yp.to/ed25519-20110926.pdf). +// Safe to derive Eq because affine coordinates. +#[derive(Copy, Clone, Eq, PartialEq)] +#[allow(missing_docs)] +pub struct AffineNielsPoint { + pub y_plus_x: FieldElement, + pub y_minus_x: FieldElement, + pub xy2d: FieldElement, +} + +/// A pre-computed point in the P³(𝔽ₚ) model for the curve, represented as +/// (Y+X, Y-X, Z, 2dXY). These precomputations accelerate addition and +/// subtraction, and were introduced by Niels Duif in the ed25519 paper +/// ["High-Speed High-Security Signatures"](https://ed25519.cr.yp.to/ed25519-20110926.pdf). +#[derive(Copy, Clone)] +pub struct ProjectiveNielsPoint { + pub Y_plus_X: FieldElement, + pub Y_minus_X: FieldElement, + pub Z: FieldElement, + pub T2d: FieldElement, +} + +// ------------------------------------------------------------------------ +// Constructors +// ------------------------------------------------------------------------ + +use traits::Identity; + +impl Identity for ProjectivePoint { + fn identity() -> ProjectivePoint { + ProjectivePoint{ X: FieldElement::zero(), + Y: FieldElement::one(), + Z: FieldElement::one() } + } +} + +impl Identity for ProjectiveNielsPoint { + fn identity() -> ProjectiveNielsPoint { + ProjectiveNielsPoint{ Y_plus_X: FieldElement::one(), + Y_minus_X: FieldElement::one(), + Z: FieldElement::one(), + T2d: FieldElement::zero() } + } +} + +impl Identity for AffineNielsPoint { + fn identity() -> AffineNielsPoint { + AffineNielsPoint{ + y_plus_x: FieldElement::one(), + y_minus_x: FieldElement::one(), + xy2d: FieldElement::zero(), + } + } +} + +// ------------------------------------------------------------------------ +// Validity checks (for debugging, not CT) +// ------------------------------------------------------------------------ + +impl ValidityCheck for ProjectivePoint { + fn is_valid(&self) -> bool { + // Curve equation is -x^2 + y^2 = 1 + d*x^2*y^2, + // homogenized as (-X^2 + Y^2)*Z^2 = Z^4 + d*X^2*Y^2 + let XX = self.X.square(); + let YY = self.Y.square(); + let ZZ = self.Z.square(); + let ZZZZ = ZZ.square(); + let lhs = &(&YY - &XX) * &ZZ; + let rhs = &ZZZZ + &(&constants::EDWARDS_D * &(&XX * &YY)); + + lhs == rhs + } +} + +// ------------------------------------------------------------------------ +// Constant-time assignment +// ------------------------------------------------------------------------ + +impl ConditionallyAssignable for ProjectiveNielsPoint { + fn conditional_assign(&mut self, other: &ProjectiveNielsPoint, choice: u8) { + self.Y_plus_X.conditional_assign(&other.Y_plus_X, choice); + self.Y_minus_X.conditional_assign(&other.Y_minus_X, choice); + self.Z.conditional_assign(&other.Z, choice); + self.T2d.conditional_assign(&other.T2d, choice); + } +} + +impl ConditionallyAssignable for AffineNielsPoint { + fn conditional_assign(&mut self, other: &AffineNielsPoint, choice: u8) { + // PreComputedGroupElementCMove() + self.y_plus_x.conditional_assign(&other.y_plus_x, choice); + self.y_minus_x.conditional_assign(&other.y_minus_x, choice); + self.xy2d.conditional_assign(&other.xy2d, choice); + } +} + +// ------------------------------------------------------------------------ +// Point conversions +// ------------------------------------------------------------------------ + +impl ProjectivePoint { + /// Convert to the extended twisted Edwards representation of this + /// point. + /// + /// From §3 in [0]: + /// + /// Given (X:Y:Z) in Ɛ, passing to Ɛₑ can be performed in 3M+1S by + /// computing (XZ,YZ,XY,Z²). (Note that in that paper, points are + /// (X:Y:T:Z) so this really does match the code below). + pub fn to_extended(&self) -> ExtendedPoint { + ExtendedPoint{ + X: &self.X * &self.Z, + Y: &self.Y * &self.Z, + Z: self.Z.square(), + T: &self.X * &self.Y, + } + } + + /// Convert this point to a `CompressedEdwardsY` + pub fn compress(&self) -> CompressedEdwardsY { + let recip = self.Z.invert(); + let x = &self.X * &recip; + let y = &self.Y * &recip; + let mut s: [u8; 32]; + + s = y.to_bytes(); + s[31] ^= (x.is_negative() << 7) as u8; + CompressedEdwardsY(s) + } + + /// Convert this projective point in the Edwards model to its equivalent + /// projective point on the Montgomery form of the curve. + /// + /// Taking the Montgomery curve equation in affine coordinates: + /// + ///     E_(A,B) = Bv² = u³ + Au² + u   (1) + /// + /// and given its relations to the coordinates of the Edwards model: + /// + ///     u = (1+y)/(1-y)        (2) + ///     v = (λu)/(x) + /// + /// Converting from affine to projective coordinates in the Montgomery + /// model, we arrive at: + /// + ///     u = (Z+Y)/(Z-Y)        (3) + ///     v = λ * ((Z+Y)/(Z-Y)) * (Z/X) + /// + /// The transition between affine and projective is given by + /// + ///     u → U/W        (4) + ///     v → V/W + /// + /// thus the Montgomery curve equation (1) becomes + /// + ///     E_(A,B) : BV²W = U³ + AU²W + UW² ⊆ 𝗣^2  (5) + /// + /// Here, again, to differentiate from points in the twisted Edwards model, we + /// call the point `(x,y)` in affine coordinates `(u,v)` and similarly in projective + /// space we use `(U:V:W)`. However, since (as per Montgomery's original work) the + /// v-coordinate is superfluous to the definition of the group law, we merely + /// use `(U:W)`. + /// + /// Therefore, the direct translation between projective Montgomery points + /// and projective twisted Edwards points is + /// + ///     (U:W) = (Z+Y:Z-Y) (6) + /// + /// Note, however, that there appears to be an exception where `Z=Y`, + /// since—from equation 2—this would imply that `y=1` (thus causing the + /// denominator to be zero). If this is the case, then it follows from the + /// twisted Edwards curve equation + /// + ///     -x² + y² = 1 + dx²y² (7) + /// + /// that + /// + ///     -x² + 1 = 1 + dx² + /// + /// and, assuming that `d ≠ -1`, + /// + ///     -x² = x² + /// x = 0 + /// + /// Therefore, the only valid point with `y=1` is the twisted Edwards + /// identity point, which correctly becomes `(1:0)`, that is, the identity, + /// in the Montgomery model. + pub fn to_montgomery(&self) -> MontgomeryPoint { + MontgomeryPoint{ + U: &self.Z + &self.Y, + W: &self.Z - &self.Y, + } + } +} + +impl CompletedPoint { + /// Convert to a ProjectivePoint + pub fn to_projective(&self) -> ProjectivePoint { + ProjectivePoint{ + X: &self.X * &self.T, + Y: &self.Y * &self.Z, + Z: &self.Z * &self.T, + } + } + + /// Convert to an ExtendedPoint + pub fn to_extended(&self) -> ExtendedPoint { + ExtendedPoint{ + X: &self.X * &self.T, + Y: &self.Y * &self.Z, + Z: &self.Z * &self.T, + T: &self.X * &self.Y, + } + } +} + +// ------------------------------------------------------------------------ +// Doubling +// ------------------------------------------------------------------------ + +impl ProjectivePoint { + /// Double this point: return self + self + pub fn double(&self) -> CompletedPoint { // Double() + let XX = self.X.square(); + let YY = self.Y.square(); + let ZZ2 = self.Z.square2(); + let X_plus_Y = &self.X + &self.Y; + let X_plus_Y_sq = X_plus_Y.square(); + let YY_plus_XX = &YY + &XX; + let YY_minus_XX = &YY - &XX; + + CompletedPoint{ + X: &X_plus_Y_sq - &YY_plus_XX, + Y: YY_plus_XX, + Z: YY_minus_XX, + T: &ZZ2 - &YY_minus_XX + } + } +} + +// ------------------------------------------------------------------------ +// Addition and Subtraction +// ------------------------------------------------------------------------ + +impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { + type Output = CompletedPoint; + + fn add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint { + let Y_plus_X = &self.Y + &self.X; + let Y_minus_X = &self.Y - &self.X; + let PP = &Y_plus_X * &other.Y_plus_X; + let MM = &Y_minus_X * &other.Y_minus_X; + let TT2d = &self.T * &other.T2d; + let ZZ = &self.Z * &other.Z; + let ZZ2 = &ZZ + &ZZ; + + CompletedPoint{ + X: &PP - &MM, + Y: &PP + &MM, + Z: &ZZ2 + &TT2d, + T: &ZZ2 - &TT2d + } + } +} + +impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { + type Output = CompletedPoint; + + fn sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint { + let Y_plus_X = &self.Y + &self.X; + let Y_minus_X = &self.Y - &self.X; + let PM = &Y_plus_X * &other.Y_minus_X; + let MP = &Y_minus_X * &other.Y_plus_X; + let TT2d = &self.T * &other.T2d; + let ZZ = &self.Z * &other.Z; + let ZZ2 = &ZZ + &ZZ; + + CompletedPoint{ + X: &PM - &MP, + Y: &PM + &MP, + Z: &ZZ2 - &TT2d, + T: &ZZ2 + &TT2d + } + } +} + +impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { + type Output = CompletedPoint; + + fn add(self, other: &'b AffineNielsPoint) -> CompletedPoint { + let Y_plus_X = &self.Y + &self.X; + let Y_minus_X = &self.Y - &self.X; + let PP = &Y_plus_X * &other.y_plus_x; + let MM = &Y_minus_X * &other.y_minus_x; + let Txy2d = &self.T * &other.xy2d; + let Z2 = &self.Z + &self.Z; + + CompletedPoint{ + X: &PP - &MM, + Y: &PP + &MM, + Z: &Z2 + &Txy2d, + T: &Z2 - &Txy2d + } + } +} + +impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { + type Output = CompletedPoint; + + fn sub(self, other: &'b AffineNielsPoint) -> CompletedPoint { + let Y_plus_X = &self.Y + &self.X; + let Y_minus_X = &self.Y - &self.X; + let PM = &Y_plus_X * &other.y_minus_x; + let MP = &Y_minus_X * &other.y_plus_x; + let Txy2d = &self.T * &other.xy2d; + let Z2 = &self.Z + &self.Z; + + CompletedPoint{ + X: &PM - &MP, + Y: &PM + &MP, + Z: &Z2 - &Txy2d, + T: &Z2 + &Txy2d + } + } +} + +// ------------------------------------------------------------------------ +// Negation +// ------------------------------------------------------------------------ + +impl<'a> Neg for &'a ProjectiveNielsPoint { + type Output = ProjectiveNielsPoint; + + fn neg(self) -> ProjectiveNielsPoint { + ProjectiveNielsPoint{ + Y_plus_X: self.Y_minus_X, + Y_minus_X: self.Y_plus_X, + Z: self.Z, + T2d: -(&self.T2d), + } + } +} + +impl<'a> Neg for &'a AffineNielsPoint { + type Output = AffineNielsPoint; + + fn neg(self) -> AffineNielsPoint { + AffineNielsPoint{ + y_plus_x: self.y_minus_x, + y_minus_x: self.y_plus_x, + xy2d: -(&self.xy2d) + } + } +} + +// ------------------------------------------------------------------------ +// Debug traits +// ------------------------------------------------------------------------ + +impl Debug for ProjectivePoint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}", + &self.X, &self.Y, &self.Z) + } +} + +impl Debug for CompletedPoint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}", + &self.X, &self.Y, &self.Z, &self.T) + } +} + +impl Debug for AffineNielsPoint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}", + &self.y_plus_x, &self.y_minus_x, &self.xy2d) + } +} + +impl Debug for ProjectiveNielsPoint { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}", + &self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d) + } +} + + diff --git a/src/edwards.rs b/src/edwards.rs index 9829341..58970e7 100644 --- a/src/edwards.rs +++ b/src/edwards.rs @@ -8,67 +8,7 @@ // - Isis Agora Lovecruft // - Henry de Valence -//! Group operations for Curve25519, in the form of the twisted -//! Edwards curve -x²+y²=1+dx²y² modulo p=2²⁵⁵-19 with -//! parameter d=-121665/121666. -//! -//! # Curve representations -//! -//! Internally, we use several different models for the curve. Here -//! is a sketch of the relationship between the models, following [a -//! post](https://moderncrypto.org/mail-archive/curves/2016/000807.html) -//! by Ben Smith on the moderncrypto mailing list. -//! -//! Begin with the affine equation for the curve, -//! -//!     -x² + y² = 1 + dx²y².       (1) -//! -//! Next, pass to the projective closure 𝗣^1 x 𝗣^1 by setting x=X/Z, -//! y=Y/T. Clearing denominators gives the model -//! -//!     -X²T² + Y²Z² = Z²T² + dX²Y². (2) -//! -//! To map from 𝗣^1 x 𝗣^1, a product of two lines, to 𝗣^3, we use the -//! Segre embedding, -//! -//!     σ : ((X:Z),(Y:T)) ↦ (XY:XT:ZY:ZT).  (3) -//! -//! Using coordinates (W₀:W₁:W₂:W₃) for 𝗣^3, the image of σ(𝗣^1 x 𝗣^1) -//! is the surface defined by W₀W₃=W₁W₂, and under σ, equation (2) -//! becomes -//! -//!     -W₁² + W₂² = W₃² + dW₀².   (4) -//! -//! Up to variable naming, this is exactly the curve model introduced -//! in ["Twisted Edwards Curves -//! Revisited"](https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf) -//! by Hisil, Wong, Carter, and Dawson. We can map from 𝗣^3 to 𝗣² by -//! sending (W₀:W₁:W₂:W₃) to (W₁:W₂:W₃). Notice that -//! -//!     W₁/W₃ = XT/ZT = X/Z = x    (5) -//! -//!     W₂/W₃ = ZY/ZT = Y/T = y,   (6) -//! -//! so this is the same as if we had started with the affine model (1) -//! and passed to 𝗣^2 by setting `x = W₁/W₃`, `y = W₂/W₃`. Up to -//! variable naming, this is the projective representation introduced -//! in ["Twisted Edwards Curves"](https://eprint.iacr.org/2008/013). -//! -//! Following the implementation strategy in the ref10 reference -//! implementation for [Ed25519](https://ed25519.cr.yp.to/ed25519-20110926.pdf), -//! we use several different models for curve points: -//! -//! * `CompletedPoint`: points in 𝗣^1 x 𝗣^1; -//! * `ExtendedPoint`: points in 𝗣^3; -//! * `ProjectivePoint`: points in 𝗣^2. -//! -//! Finally, to accelerate additions, we use two cached point formats, -//! one for the affine model and one for the 𝗣^3 model: -//! -//! * `AffineNielsPoint`: `(y+x, y-x, 2dxy)` -//! * `ProjectiveNielsPoint`: `(Y+X, Y-X, Z, 2dXY)` -//! -//! [1]: https://moderncrypto.org/mail-archive/curves/2016/000807.html +//! Group operations for Curve25519, in Edwards form. // We allow non snake_case names because coordinates in projective space are // traditionally denoted by the capitalisation of their respective @@ -86,17 +26,28 @@ use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; use core::ops::Index; -use constants; -use field::FieldElement; -use scalar::Scalar; -use montgomery::MontgomeryPoint; - use subtle::slices_equal; -use subtle::bytes_equal; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; +// XXX subtle::Equal use subtle::Equal; +use constants; + +use field::FieldElement; +use scalar::Scalar; + +use montgomery::MontgomeryPoint; +use curve_models::ProjectivePoint; +use curve_models::CompletedPoint; +use curve_models::AffineNielsPoint; +use curve_models::ProjectiveNielsPoint; + +use traits::{Identity, IsIdentity}; +use traits::ValidityCheck; + +use traits::select_precomputed_point; + // ------------------------------------------------------------------------ // Compressed points // ------------------------------------------------------------------------ @@ -210,74 +161,19 @@ impl<'de> Deserialize<'de> for ExtendedPoint { /// An `ExtendedPoint` is a point on the curve in 𝗣³(𝔽ₚ). /// A point (x,y) in the affine model corresponds to (x:y:1:xy). -// XXX members should not be public, but that's needed for the -// constants module. Fix when RFC #1422 lands: -// https://github.com/rust-lang/rust/issues/32409 #[derive(Copy, Clone)] #[allow(missing_docs)] pub struct ExtendedPoint { - pub X: FieldElement, - pub Y: FieldElement, - pub Z: FieldElement, - pub T: FieldElement, -} - -/// A `ProjectivePoint` is a point on the curve in 𝗣²(𝔽ₚ). -/// A point (x,y) in the affine model corresponds to (x:y:1). -#[derive(Copy, Clone)] -pub struct ProjectivePoint { - X: FieldElement, - Y: FieldElement, - Z: FieldElement, -} - -/// A `CompletedPoint` is a point ((X:Z), (Y:T)) in 𝗣¹(𝔽ₚ)×𝗣¹(𝔽ₚ). -/// A point (x,y) in the affine model corresponds to ((x:1),(y:1)). -#[derive(Copy, Clone)] -#[allow(missing_docs)] -pub struct CompletedPoint { - pub X: FieldElement, - pub Y: FieldElement, - pub Z: FieldElement, - pub T: FieldElement, -} - -/// A pre-computed point in the affine model for the curve, represented as -/// (y+x, y-x, 2dxy). These precomputations accelerate addition and -/// subtraction, and were introduced by Niels Duif in the ed25519 paper -/// ["High-Speed High-Security Signatures"](https://ed25519.cr.yp.to/ed25519-20110926.pdf). -// Safe to derive Eq because affine coordinates. -#[derive(Copy, Clone, Eq, PartialEq)] -#[allow(missing_docs)] -pub struct AffineNielsPoint { - pub y_plus_x: FieldElement, - pub y_minus_x: FieldElement, - pub xy2d: FieldElement, -} - -/// A pre-computed point in the P³(𝔽ₚ) model for the curve, represented as -/// (Y+X, Y-X, Z, 2dXY). These precomputations accelerate addition and -/// subtraction, and were introduced by Niels Duif in the ed25519 paper -/// ["High-Speed High-Security Signatures"](https://ed25519.cr.yp.to/ed25519-20110926.pdf). -#[derive(Copy, Clone)] -pub struct ProjectiveNielsPoint { - Y_plus_X: FieldElement, - Y_minus_X: FieldElement, - Z: FieldElement, - T2d: FieldElement, + pub(crate) X: FieldElement, + pub(crate) Y: FieldElement, + pub(crate) Z: FieldElement, + pub(crate) T: FieldElement, } // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ -/// Trait for curve point types which have an identity constructor. -pub trait Identity { - /// Returns the identity element of the curve. - /// Can be used as a constructor. - fn identity() -> Self; -} - impl Identity for CompressedEdwardsY { fn identity() -> CompressedEdwardsY { CompressedEdwardsY([1, 0, 0, 0, 0, 0, 0, 0, @@ -296,58 +192,10 @@ impl Identity for ExtendedPoint { } } -impl Identity for ProjectivePoint { - fn identity() -> ProjectivePoint { - ProjectivePoint{ X: FieldElement::zero(), - Y: FieldElement::one(), - Z: FieldElement::one() } - } -} - -impl Identity for ProjectiveNielsPoint { - fn identity() -> ProjectiveNielsPoint { - ProjectiveNielsPoint{ Y_plus_X: FieldElement::one(), - Y_minus_X: FieldElement::one(), - Z: FieldElement::one(), - T2d: FieldElement::zero() } - } -} - -impl Identity for AffineNielsPoint { - fn identity() -> AffineNielsPoint { - AffineNielsPoint{ - y_plus_x: FieldElement::one(), - y_minus_x: FieldElement::one(), - xy2d: FieldElement::zero(), - } - } -} - // ------------------------------------------------------------------------ // Validity checks (for debugging, not CT) // ------------------------------------------------------------------------ -/// Trait for checking whether a point is on the curve -pub trait ValidityCheck { - /// Checks whether the point is on the curve. Not CT. - fn is_valid(&self) -> bool; -} - -impl ValidityCheck for ProjectivePoint { - fn is_valid(&self) -> bool { - // Curve equation is -x^2 + y^2 = 1 + d*x^2*y^2, - // homogenized as (-X^2 + Y^2)*Z^2 = Z^4 + d*X^2*Y^2 - let XX = self.X.square(); - let YY = self.Y.square(); - let ZZ = self.Z.square(); - let ZZZZ = ZZ.square(); - let lhs = &(&YY - &XX) * &ZZ; - let rhs = &ZZZZ + &(&constants::EDWARDS_D * &(&XX * &YY)); - - lhs == rhs - } -} - impl ValidityCheck for ExtendedPoint { // XXX this should also check that T is correct fn is_valid(&self) -> bool { @@ -359,24 +207,6 @@ impl ValidityCheck for ExtendedPoint { // Constant-time assignment // ------------------------------------------------------------------------ -impl ConditionallyAssignable for ProjectiveNielsPoint { - fn conditional_assign(&mut self, other: &ProjectiveNielsPoint, choice: u8) { - self.Y_plus_X.conditional_assign(&other.Y_plus_X, choice); - self.Y_minus_X.conditional_assign(&other.Y_minus_X, choice); - self.Z.conditional_assign(&other.Z, choice); - self.T2d.conditional_assign(&other.T2d, choice); - } -} - -impl ConditionallyAssignable for AffineNielsPoint { - fn conditional_assign(&mut self, other: &AffineNielsPoint, choice: u8) { - // PreComputedGroupElementCMove() - self.y_plus_x.conditional_assign(&other.y_plus_x, choice); - self.y_minus_x.conditional_assign(&other.y_minus_x, choice); - self.xy2d.conditional_assign(&other.xy2d, choice); - } -} - impl ConditionallyAssignable for ExtendedPoint { fn conditional_assign(&mut self, other: &ExtendedPoint, choice: u8) { self.X.conditional_assign(&other.X, choice); @@ -397,120 +227,10 @@ impl Equal for ExtendedPoint { } } -/// Trait for testing if a curve point is equivalent to the identity point. -pub trait IsIdentity { - /// Return true if this element is the identity element of the curve. - fn is_identity(&self) -> bool; -} - -/// Implement generic identity equality testing for a point representations -/// which have constant-time equality testing and a defined identity -/// constructor. -impl IsIdentity for T where T: Equal + Identity { - fn is_identity(&self) -> bool { - self.ct_eq(&T::identity()) == 1u8 - } -} - // ------------------------------------------------------------------------ // Point conversions // ------------------------------------------------------------------------ -impl ProjectivePoint { - /// Convert to the extended twisted Edwards representation of this - /// point. - /// - /// From §3 in [0]: - /// - /// Given (X:Y:Z) in Ɛ, passing to Ɛₑ can be performed in 3M+1S by - /// computing (XZ,YZ,XY,Z²). (Note that in that paper, points are - /// (X:Y:T:Z) so this really does match the code below). - pub fn to_extended(&self) -> ExtendedPoint { - ExtendedPoint{ - X: &self.X * &self.Z, - Y: &self.Y * &self.Z, - Z: self.Z.square(), - T: &self.X * &self.Y, - } - } - - /// Convert this point to a `CompressedEdwardsY` - pub fn compress(&self) -> CompressedEdwardsY { - let recip = self.Z.invert(); - let x = &self.X * &recip; - let y = &self.Y * &recip; - let mut s: [u8; 32]; - - s = y.to_bytes(); - s[31] ^= (x.is_negative() << 7) as u8; - CompressedEdwardsY(s) - } - - /// Convert this projective point in the Edwards model to its equivalent - /// projective point on the Montgomery form of the curve. - /// - /// Taking the Montgomery curve equation in affine coordinates: - /// - ///     E_(A,B) = Bv² = u³ + Au² + u   (1) - /// - /// and given its relations to the coordinates of the Edwards model: - /// - ///     u = (1+y)/(1-y)        (2) - ///     v = (λu)/(x) - /// - /// Converting from affine to projective coordinates in the Montgomery - /// model, we arrive at: - /// - ///     u = (Z+Y)/(Z-Y)        (3) - ///     v = λ * ((Z+Y)/(Z-Y)) * (Z/X) - /// - /// The transition between affine and projective is given by - /// - ///     u → U/W        (4) - ///     v → V/W - /// - /// thus the Montgomery curve equation (1) becomes - /// - ///     E_(A,B) : BV²W = U³ + AU²W + UW² ⊆ 𝗣^2  (5) - /// - /// Here, again, to differentiate from points in the twisted Edwards model, we - /// call the point `(x,y)` in affine coordinates `(u,v)` and similarly in projective - /// space we use `(U:V:W)`. However, since (as per Montgomery's original work) the - /// v-coordinate is superfluous to the definition of the group law, we merely - /// use `(U:W)`. - /// - /// Therefore, the direct translation between projective Montgomery points - /// and projective twisted Edwards points is - /// - ///     (U:W) = (Z+Y:Z-Y) (6) - /// - /// Note, however, that there appears to be an exception where `Z=Y`, - /// since—from equation 2—this would imply that `y=1` (thus causing the - /// denominator to be zero). If this is the case, then it follows from the - /// twisted Edwards curve equation - /// - ///     -x² + y² = 1 + dx²y² (7) - /// - /// that - /// - ///     -x² + 1 = 1 + dx² - /// - /// and, assuming that `d ≠ -1`, - /// - ///     -x² = x² - /// x = 0 - /// - /// Therefore, the only valid point with `y=1` is the twisted Edwards - /// identity point, which correctly becomes `(1:0)`, that is, the identity, - /// in the Montgomery model. - pub fn to_montgomery(&self) -> MontgomeryPoint { - MontgomeryPoint{ - U: &self.Z + &self.Y, - W: &self.Z - &self.Y, - } - } -} - impl ExtendedPoint { /// Convert to a ProjectiveNielsPoint pub fn to_projective_niels(&self) -> ProjectiveNielsPoint { @@ -561,54 +281,13 @@ impl ExtendedPoint { } } -impl CompletedPoint { - /// Convert to a ProjectivePoint - pub fn to_projective(&self) -> ProjectivePoint { - ProjectivePoint{ - X: &self.X * &self.T, - Y: &self.Y * &self.Z, - Z: &self.Z * &self.T, - } - } - - /// Convert to an ExtendedPoint - pub fn to_extended(&self) -> ExtendedPoint { - ExtendedPoint{ - X: &self.X * &self.T, - Y: &self.Y * &self.Z, - Z: &self.Z * &self.T, - T: &self.X * &self.Y, - } - } -} - // ------------------------------------------------------------------------ // Doubling // ------------------------------------------------------------------------ -impl ProjectivePoint { - /// Double this point: return self + self - pub fn double(&self) -> CompletedPoint { // Double() - let XX = self.X.square(); - let YY = self.Y.square(); - let ZZ2 = self.Z.square2(); - let X_plus_Y = &self.X + &self.Y; - let X_plus_Y_sq = X_plus_Y.square(); - let YY_plus_XX = &YY + &XX; - let YY_minus_XX = &YY - &XX; - - CompletedPoint{ - X: &X_plus_Y_sq - &YY_plus_XX, - Y: YY_plus_XX, - Z: YY_minus_XX, - T: &ZZ2 - &YY_minus_XX - } - } -} - impl ExtendedPoint { /// Add this point to itself. - pub fn double(&self) -> ExtendedPoint { + pub(crate) fn double(&self) -> ExtendedPoint { self.to_projective().double().to_extended() } } @@ -617,88 +296,6 @@ impl ExtendedPoint { // Addition and Subtraction // ------------------------------------------------------------------------ -impl<'a, 'b> Add<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { - type Output = CompletedPoint; - - fn add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint { - let Y_plus_X = &self.Y + &self.X; - let Y_minus_X = &self.Y - &self.X; - let PP = &Y_plus_X * &other.Y_plus_X; - let MM = &Y_minus_X * &other.Y_minus_X; - let TT2d = &self.T * &other.T2d; - let ZZ = &self.Z * &other.Z; - let ZZ2 = &ZZ + &ZZ; - - CompletedPoint{ - X: &PP - &MM, - Y: &PP + &MM, - Z: &ZZ2 + &TT2d, - T: &ZZ2 - &TT2d - } - } -} - -impl<'a, 'b> Sub<&'b ProjectiveNielsPoint> for &'a ExtendedPoint { - type Output = CompletedPoint; - - fn sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint { - let Y_plus_X = &self.Y + &self.X; - let Y_minus_X = &self.Y - &self.X; - let PM = &Y_plus_X * &other.Y_minus_X; - let MP = &Y_minus_X * &other.Y_plus_X; - let TT2d = &self.T * &other.T2d; - let ZZ = &self.Z * &other.Z; - let ZZ2 = &ZZ + &ZZ; - - CompletedPoint{ - X: &PM - &MP, - Y: &PM + &MP, - Z: &ZZ2 - &TT2d, - T: &ZZ2 + &TT2d - } - } -} - -impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = CompletedPoint; - - fn add(self, other: &'b AffineNielsPoint) -> CompletedPoint { - let Y_plus_X = &self.Y + &self.X; - let Y_minus_X = &self.Y - &self.X; - let PP = &Y_plus_X * &other.y_plus_x; - let MM = &Y_minus_X * &other.y_minus_x; - let Txy2d = &self.T * &other.xy2d; - let Z2 = &self.Z + &self.Z; - - CompletedPoint{ - X: &PP - &MM, - Y: &PP + &MM, - Z: &Z2 + &Txy2d, - T: &Z2 - &Txy2d - } - } -} - -impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = CompletedPoint; - - fn sub(self, other: &'b AffineNielsPoint) -> CompletedPoint { - let Y_plus_X = &self.Y + &self.X; - let Y_minus_X = &self.Y - &self.X; - let PM = &Y_plus_X * &other.y_minus_x; - let MP = &Y_minus_X * &other.y_plus_x; - let Txy2d = &self.T * &other.xy2d; - let Z2 = &self.Z + &self.Z; - - CompletedPoint{ - X: &PM - &MP, - Y: &PM + &MP, - Z: &Z2 - &Txy2d, - T: &Z2 + &Txy2d - } - } -} - impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint { type Output = ExtendedPoint; fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint { @@ -742,32 +339,6 @@ impl<'a> Neg for &'a ExtendedPoint { } } -impl<'a> Neg for &'a ProjectiveNielsPoint { - type Output = ProjectiveNielsPoint; - - fn neg(self) -> ProjectiveNielsPoint { - ProjectiveNielsPoint{ - Y_plus_X: self.Y_minus_X, - Y_minus_X: self.Y_plus_X, - Z: self.Z, - T2d: -(&self.T2d), - } - } -} - - -impl<'a> Neg for &'a AffineNielsPoint { - type Output = AffineNielsPoint; - - fn neg(self) -> AffineNielsPoint { - AffineNielsPoint{ - y_plus_x: self.y_minus_x, - y_minus_x: self.y_plus_x, - xy2d: -(&self.xy2d) - } - } -} - // ------------------------------------------------------------------------ // Scalar multiplication // ------------------------------------------------------------------------ @@ -843,6 +414,8 @@ impl<'a, 'b> Mul<&'b ExtendedPoint> for &'a Scalar { /// /// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an /// error to call this function with two vectors of different lengths. +/// +/// XXX need to clear memory #[cfg(any(feature = "alloc", feature = "std"))] pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint where I: IntoIterator, @@ -889,7 +462,7 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint // mults: we perform 63 multiplications by 16 instead of 63*n // multiplications, saving 252*(n-1) doublings. let mut Q = ExtendedPoint::identity(); - // XXX this algorithm makes no effort to be cache-aware; maybe it could be improved? + // XXX this impl makes no effort to be cache-aware; maybe it could be improved? for j in (0..64).rev() { Q = Q.mult_by_pow_2(4); let it = scalar_digits_list.iter().zip(lookup_tables.iter()); @@ -904,8 +477,10 @@ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> ExtendedPoint } /// Precomputation +/// +/// XXX we should box the internals #[derive(Clone)] -pub struct EdwardsBasepointTable(pub [[AffineNielsPoint; 8]; 32]); +pub struct EdwardsBasepointTable(pub(crate) [[AffineNielsPoint; 8]; 32]); impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsBasepointTable { type Output = ExtendedPoint; @@ -963,27 +538,6 @@ impl<'a, 'b> Mul<&'a EdwardsBasepointTable> for &'b Scalar { /// a the basepoint, `B` included in a precomputed `basepoint_table`. /// /// Precondition: this scalar must be reduced. - /// - /// The computation proceeds as follows, as described on page 13 - /// of the Ed25519 paper. Write this scalar `a` in radix 16 with - /// coefficients in [-8,8), i.e., - /// - /// a = a_0 + a_1*16^1 + ... + a_63*16^63, - /// - /// with -8 ≤ a_i < 8. Then - /// - /// a*B = a_0*B + a_1*16^1*B + ... + a_63*16^63*B. - /// - /// Grouping even and odd coefficients gives - /// - /// a*B = a_0*16^0*B + a_2*16^2*B + ... + a_62*16^62*B - /// + a_1*16^1*B + a_3*16^3*B + ... + a_63*16^63*B - /// = (a_0*16^0*B + a_2*16^2*B + ... + a_62*16^62*B) - /// + 16*(a_1*16^0*B + a_3*16^2*B + ... + a_63*16^62*B). - /// - /// We then use the `select_precomputed_point` function, which - /// takes `-8 ≤ x < 8` and `[16^2i * B, ..., 8 * 16^2i * B]`, - /// and returns `x * 16^2i * B` in constant time. fn mul(self, basepoint_table: &'a EdwardsBasepointTable) -> ExtendedPoint { basepoint_table * &self } @@ -1054,38 +608,12 @@ impl ExtendedPoint { } } -/// Given precomputed points `[P, 2P, 3P, ..., 8P]`, as well as `-8 ≤ -/// x ≤ 8`, compute `x * B` in constant time, i.e., without branching -/// on x or using it as an array index. -fn select_precomputed_point(x: i8, points: &[T; 8]) -> T - where T: Identity + ConditionallyAssignable, for<'a> &'a T: Neg -{ - debug_assert!(x >= -8); debug_assert!(x <= 8); - - // Compute xabs = |x| - let xmask = x >> 7; - let xabs = (x + xmask) ^ xmask; - - // Set t = 0 * P = identity - let mut t = T::identity(); - for j in 1..9 { - // Copy `points[j-1] == j*P` onto `t` in constant time if `|x| == j`. - t.conditional_assign(&points[j-1], - bytes_equal(xabs as u8, j as u8)); - } - // Now t == |x| * P. - - let neg_mask = (xmask & 1) as u8; - t.conditional_negate(neg_mask); - // Now t == x * P. - - t -} - // ------------------------------------------------------------------------ // Elligator2 (uniform encoding/decoding of curve points) // ------------------------------------------------------------------------ +// XXX should this be in another module, with types and `From` impls, like `CompressedEdwardsY`? + impl ExtendedPoint { /// Use Elligator2 to try to convert `self` to a uniformly random /// string. @@ -1116,34 +644,6 @@ impl Debug for ExtendedPoint { } } -impl Debug for ProjectivePoint { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "ProjectivePoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n}}", - &self.X, &self.Y, &self.Z) - } -} - -impl Debug for CompletedPoint { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "CompletedPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}", - &self.X, &self.Y, &self.Z, &self.T) - } -} - -impl Debug for AffineNielsPoint { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "AffineNielsPoint{{\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n}}", - &self.y_plus_x, &self.y_minus_x, &self.xy2d) - } -} - -impl Debug for ProjectiveNielsPoint { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "ProjectiveNielsPoint{{\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n}}", - &self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d) - } -} - impl Debug for EdwardsBasepointTable { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "EdwardsBasepointTable([\n")?; @@ -1154,7 +654,6 @@ impl Debug for EdwardsBasepointTable { } } - // ------------------------------------------------------------------------ // Variable-time functions // ------------------------------------------------------------------------ diff --git a/src/lib.rs b/src/lib.rs index 1834835..98efe09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,8 @@ pub mod edwards; pub mod ristretto; // Useful constants, like the Ed25519 basepoint pub mod constants; +// External (and internal) traits. +pub mod traits; //------------------------------------------------------------------------ // curve25519-dalek internal modules @@ -95,4 +97,4 @@ pub(crate) mod field; pub(crate) mod backend; // Internal curve models which are not part of the public API. -//mod curve_models; \ No newline at end of file +pub(crate) mod curve_models; diff --git a/src/montgomery.rs b/src/montgomery.rs index e9ab436..9f42fab 100644 --- a/src/montgomery.rs +++ b/src/montgomery.rs @@ -38,7 +38,9 @@ use scalar::Scalar; // XXX Move these to a common "group" module? At the same time, we should // XXX probably make a `trait Group` once const generics are implemented in // XXX Rust. —isis -use edwards::{Identity, ValidityCheck}; +// +// XXX I put these in a `traits` module for now - hdevalence +use traits::{Identity, ValidityCheck}; use subtle::ConditionallyAssignable; use subtle::ConditionallySwappable; @@ -427,7 +429,7 @@ impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar { #[cfg(test)] mod test { use constants::BASE_COMPRESSED_MONTGOMERY; - use edwards::Identity; + use traits::Identity; use super::*; use rand::OsRng; diff --git a/src/ristretto.rs b/src/ristretto.rs index eb0c657..240ea19 100644 --- a/src/ristretto.rs +++ b/src/ristretto.rs @@ -400,18 +400,21 @@ use core::ops::{Add, Sub, Neg}; use core::ops::{AddAssign, SubAssign}; use core::ops::{Mul, MulAssign}; -use edwards; -use edwards::ExtendedPoint; -use edwards::CompletedPoint; -use edwards::EdwardsBasepointTable; -use edwards::Identity; -use scalar::Scalar; - use subtle; use subtle::ConditionallyAssignable; use subtle::ConditionallyNegatable; use subtle::Equal; +use edwards; +use edwards::ExtendedPoint; +use edwards::EdwardsBasepointTable; + +use scalar::Scalar; + +use curve_models::CompletedPoint; + +use traits::Identity; + // ------------------------------------------------------------------------ // Compressed points // ------------------------------------------------------------------------ @@ -953,7 +956,7 @@ impl ConditionallyAssignable for RistrettoPoint { /// # /// # use subtle::ConditionallyAssignable; /// # - /// # use curve25519_dalek::edwards::Identity; + /// # use curve25519_dalek::traits::Identity; /// # use curve25519_dalek::ristretto::RistrettoPoint; /// # use curve25519_dalek::constants; /// # fn main() { @@ -1032,8 +1035,7 @@ mod test { use scalar::Scalar; use constants; use edwards::CompressedEdwardsY; - use edwards::Identity; - use edwards::ValidityCheck; + use traits::{Identity, ValidityCheck}; use super::*; #[cfg(feature = "serde")] diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..aaac49a --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,87 @@ +// -*- mode: rust; -*- +// +// This file is part of curve25519-dalek. +// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence +// See LICENSE for licensing information. +// +// Authors: +// - Isis Agora Lovecruft +// - Henry de Valence + +//! Module for common traits. + +use core::ops::Neg; + +use subtle; +use subtle::ConditionallyAssignable; +use subtle::ConditionallyNegatable; + +// ------------------------------------------------------------------------ +// Public Traits +// ------------------------------------------------------------------------ + +/// Trait for getting the identity element of a point type. +pub trait Identity { + /// Returns the identity element of the curve. + /// Can be used as a constructor. + fn identity() -> Self; +} + +/// Trait for testing if a curve point is equivalent to the identity point. +pub trait IsIdentity { + /// Return true if this element is the identity element of the curve. + fn is_identity(&self) -> bool; +} + +/// Implement generic identity equality testing for a point representations +/// which have constant-time equality testing and a defined identity +/// constructor. +impl IsIdentity for T where T: subtle::Equal + Identity { + fn is_identity(&self) -> bool { + self.ct_eq(&T::identity()) == 1u8 + } +} + +// ------------------------------------------------------------------------ +// Private Traits +// ------------------------------------------------------------------------ + +/// Trait for checking whether a point is on the curve. +/// +/// This trait is only for debugging/testing, since it should be +/// impossible for a `curve25519-dalek` user to construct an invalid +/// point. +pub(crate) trait ValidityCheck { + /// Checks whether the point is on the curve. Not CT. + fn is_valid(&self) -> bool; +} + +// This isn't a trait, but it is fully generic... + +/// Given precomputed points `[P, 2P, 3P, ..., 8P]`, as well as `-8 ≤ +/// x ≤ 8`, compute `x * B` in constant time, i.e., without branching +/// on x or using it as an array index. +pub(crate) fn select_precomputed_point(x: i8, points: &[T; 8]) -> T + where T: Identity + ConditionallyAssignable, for<'a> &'a T: Neg +{ + debug_assert!(x >= -8); debug_assert!(x <= 8); + + // Compute xabs = |x| + let xmask = x >> 7; + let xabs = (x + xmask) ^ xmask; + + // Set t = 0 * P = identity + let mut t = T::identity(); + for j in 1..9 { + // Copy `points[j-1] == j*P` onto `t` in constant time if `|x| == j`. + t.conditional_assign(&points[j-1], + subtle::bytes_equal(xabs as u8, j as u8)); + } + // Now t == |x| * P. + + let neg_mask = (xmask & 1) as u8; + t.conditional_negate(neg_mask); + // Now t == x * P. + + t +}