// -*- mode: rust; -*-
//
// To the extent possible under law, the authors have waived all copyright and
// related or neighboring rights to curve25519-dalek, using the Creative
// Commons "CC0" public domain dedication. See
// for full details.
//
// Authors:
// - 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, 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:
//!
//! * PreComputedPoint: `(y+x, y-x, 2dxy)`
//! * CachedPoint: `(Y+X, Y-X, Z, 2dXY)`
//!
//! [1]: https://moderncrypto.org/mail-archive/curves/2016/000807.html
// We allow non snake_case names because coordinates in projective space are
// traditionally denoted by the capitalisation of their respective
// counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my
// affine and projective cakes and eat both of them too.
#![allow(non_snake_case)]
use core::fmt::Debug;
use core::iter::Iterator;
use core::ops::{Add, Sub, Neg, Index};
use core::cmp::{PartialEq, Eq};
use constants;
use field::FieldElement;
use scalar::Scalar;
use util::bytes_equal_ct;
use util::CTAssignable;
// ------------------------------------------------------------------------
// Compressed points
// ------------------------------------------------------------------------
/// In "Edwards y" format, the point `(x,y)` on the curve is
/// determined by the `y`-coordinate and the sign of `x`, marshalled
/// into a 32-byte array.
///
/// The first 255 bits of a CompressedEdwardsY represent the
/// y-coordinate. The high bit of the 32nd byte gives the sign of `x`.
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct CompressedEdwardsY(pub [u8; 32]);
impl Debug for CompressedEdwardsY {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "CompressedPoint: {:?}", &self.0[..])
}
}
impl Index for CompressedEdwardsY {
type Output = u8;
fn index<'a>(&'a self, _index: usize) -> &'a u8 {
let ret: &'a u8 = &(self.0[_index]);
ret
}
}
impl CompressedEdwardsY {
/// View this `CompressedEdwardsY` as an array of bytes.
pub fn to_bytes(&self) -> [u8;32] {
self.0
}
/// Attempt to decompress to an `ExtendedPoint`.
///
/// # Warning
///
/// This function will fail and return None if both vx²-u=0 and vx²+u=0.
pub fn decompress(&self) -> Option { // FromBytes()
let mut u: FieldElement;
let mut v: FieldElement;
let v3: FieldElement;
let vxx: FieldElement;
let mut X: FieldElement;
let Y: FieldElement;
let Z: FieldElement;
let T: FieldElement;
Y = FieldElement::from_bytes(&self.0);
Z = FieldElement::one();
u = Y.square();
v = &u * &constants::d;
u -= &Z; // u = y²-1
v += &Z; // v = dy²+1
v3 = &v.square() * &v; // v3 = v³
X = (&v3.square() * &(&v * &u)).pow_p58(); // x = (uv⁷)^((q-5)/8)
X *= &(&u * &v3); // x = (uv³)(uv⁷)^((q-5)/8)
vxx = &v * &X.square();
if (&vxx - &u).is_nonzero() == 1 { // vx²-u
if (&vxx + &u).is_nonzero() == 1 { // vx²+u
return None;
}
X *= &constants::SQRT_M1;
}
if X.is_negative() != (self[31] >> 7) as i32 {
X = X.neg();
}
T = &X * &Y;
Some(ExtendedPoint{ X: X, Y: Y, Z: Z, T: T })
}
}
// ------------------------------------------------------------------------
// Internal point representations
// ------------------------------------------------------------------------
/// An `ExtendedPoint` is a point on the curve in 𝗣³(𝔽ₚ).
/// A point (x,y) in the affine model corresponds to (x:y:1:xy).
#[derive(Copy, Clone)]
pub struct ExtendedPoint {
X: FieldElement,
Y: FieldElement,
Z: FieldElement,
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)]
pub struct CompletedPoint {
X: FieldElement,
Y: FieldElement,
Z: FieldElement,
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.
// Safe to derive Eq because affine coordinates.
#[derive(Copy, Clone, Eq, PartialEq)]
#[allow(missing_docs)]
pub struct PreComputedPoint {
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.
#[derive(Copy, Clone)]
pub struct CachedPoint {
Y_plus_X: FieldElement,
Y_minus_X: FieldElement,
Z: FieldElement,
T2d: FieldElement,
}
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/// Trait for curve point types that 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 ExtendedPoint {
fn identity() -> ExtendedPoint {
ExtendedPoint{ X: FieldElement::zero(),
Y: FieldElement::one(),
Z: FieldElement::one(),
T: FieldElement::zero() }
}
}
impl Identity for ProjectivePoint {
fn identity() -> ProjectivePoint {
ProjectivePoint{ X: FieldElement::zero(),
Y: FieldElement::one(),
Z: FieldElement::one() }
}
}
impl Identity for CachedPoint {
fn identity() -> CachedPoint {
CachedPoint{ Y_plus_X: FieldElement::one(),
Y_minus_X: FieldElement::one(),
Z: FieldElement::one(),
T2d: FieldElement::zero() }
}
}
impl Identity for PreComputedPoint {
fn identity() -> PreComputedPoint {
PreComputedPoint{
y_plus_x: FieldElement::one(),
y_minus_x: FieldElement::one(),
xy2d: FieldElement::zero(),
}
}
}
// ------------------------------------------------------------------------
// Constant-time assignment
// ------------------------------------------------------------------------
impl CTAssignable for CachedPoint {
fn conditional_assign(&mut self, other: &CachedPoint, 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 CTAssignable for PreComputedPoint {
fn conditional_assign(&mut self, other: &PreComputedPoint, 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).
#[allow(dead_code)] // rustc complains this is unused even when it's used
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)
}
}
impl ExtendedPoint {
/// Convert to a CachedPoint
pub fn to_cached(&self) -> CachedPoint {
CachedPoint{
Y_plus_X: &self.Y + &self.X,
Y_minus_X: &self.Y - &self.X,
Z: self.Z,
T2d: &self.T * &constants::d2,
}
}
/// Convert the representation of this point from extended Twisted Edwards
/// coodinates to projective coordinates.
///
/// Given a point in Ɛₑ, we can convert to projective coordinates
/// cost-free by simply ignoring T.
fn to_projective(&self) -> ProjectivePoint {
ProjectivePoint{
X: self.X,
Y: self.Y,
Z: self.Z,
}
}
/// Compress this point to `CompressedEdwardsY` format
pub fn compress(&self) -> CompressedEdwardsY {
self.to_projective().compress()
}
/// Dehomogenize to a PreComputedPoint.
/// Mainly for testing.
pub fn to_precomputed(&self) -> PreComputedPoint {
let recip = self.Z.invert();
let x = &self.X * &recip;
let y = &self.Y * &recip;
let xy2d = &(&x * &y) * &constants::d2;
PreComputedPoint{
y_plus_x: &y + &x,
y_minus_x: &y - &x,
xy2d: xy2d
}
}
}
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
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.
fn double(&self) -> ExtendedPoint {
self.to_projective().double().to_extended()
}
}
// ------------------------------------------------------------------------
// Addition and Subtraction
// ------------------------------------------------------------------------
impl<'a,'b> Add<&'b CachedPoint> for &'a ExtendedPoint {
type Output = CompletedPoint;
fn add(self, other: &'b CachedPoint) -> 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 CachedPoint> for &'a ExtendedPoint {
type Output = CompletedPoint;
fn sub(self, other: &'b CachedPoint) -> 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 PreComputedPoint> for &'a ExtendedPoint {
type Output = CompletedPoint;
fn add(self, other: &'b PreComputedPoint) -> 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 PreComputedPoint> for &'a ExtendedPoint {
type Output = CompletedPoint;
fn sub(self, other: &'b PreComputedPoint) -> 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 {
(self + &other.to_cached()).to_extended()
}
}
impl<'a,'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint {
type Output = ExtendedPoint;
fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint {
(self - &other.to_cached()).to_extended()
}
}
impl<'a> Neg for &'a ExtendedPoint {
type Output = ExtendedPoint;
fn neg(self) -> ExtendedPoint {
ExtendedPoint{
X: -(&self.X),
Y: self.Y,
Z: self.Z,
T: -(&self.T),
}
}
}
impl<'a> Neg for &'a CachedPoint {
type Output = CachedPoint;
fn neg(self) -> CachedPoint {
CachedPoint{
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 PreComputedPoint {
type Output = PreComputedPoint;
fn neg(self) -> PreComputedPoint {
PreComputedPoint{
y_plus_x: self.y_minus_x,
y_minus_x: self.y_plus_x,
xy2d: -(&self.xy2d)
}
}
}
// ------------------------------------------------------------------------
// Scalar multiplication
// ------------------------------------------------------------------------
impl ExtendedPoint {
/// Scalar multiplication: compute `a * self`.
///
/// Uses a window of size 4. Note: for scalar multiplication of
/// the basepoint, `basepoint_mult` is approximately 4x faster.
pub fn scalar_mult(&self, a: &Scalar) -> ExtendedPoint {
let A = self.to_cached();
let mut As: [CachedPoint; 8] = [A; 8];
for i in 0..7 {
As[i+1] = (self + &As[i]).to_extended().to_cached();
}
let e = a.to_radix_16();
let mut h = ExtendedPoint::identity();
let mut t: CompletedPoint;
for i in (0..64).rev() {
h = h.mult_by_pow_2(4);
t = &h + &select_precomputed_point(e[i], &As);
h = t.to_extended();
}
h
}
/// Construct an `ExtendedPoint` from a `Scalar`, `a`, by
/// computing the multiple `aB` of the basepoint `B`.
///
/// Precondition: the scalar must be reduced.
///
/// The computation proceeds as follows, as described on page 13
/// of the Ed25519 paper. Write the 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.
pub fn basepoint_mult(a: &Scalar) -> ExtendedPoint { //GeScalarMultBase
let e = a.to_radix_16();
let mut h = ExtendedPoint::identity();
let mut t: CompletedPoint;
for i in (0..64).filter(|x| x % 2 == 1) {
t = &h + &select_precomputed_point(e[i], &constants::base[i/2]);
h = t.to_extended();
}
h = h.mult_by_pow_2(4);
for i in (0..64).filter(|x| x % 2 == 0) {
t = &h + &select_precomputed_point(e[i], &constants::base[i/2]);
h = t.to_extended();
}
h
}
/// Multiply by the cofactor: compute `8 * self`.
///
/// Convenience wrapper around `mult_by_pow_2`.
#[inline]
pub fn mult_by_cofactor(&self) -> ExtendedPoint {
self.mult_by_pow_2(3)
}
/// Compute `2^k * self` by successive doublings.
/// Requires `k > 0`.
#[inline]
pub fn mult_by_pow_2(&self, k: u32) -> ExtendedPoint {
let mut r: CompletedPoint;
let mut s = self.to_projective();
for _ in 0..(k-1) {
r = s.double(); s = r.to_projective();
}
// Unroll last iteration so we can go directly to_extended()
r = s.double();
return r.to_extended();
}
}
/// Given a point `A` and scalars `a` and `b`, compute the point
/// `aA+bB`, where `B` is the Ed25519 basepoint (i.e., `B = (x,4/5)`
/// with x positive).
///
/// # Warning
///
/// This function is *not* constant time, hence its name.
// XXX should return ExtendedPoint?
pub fn double_scalar_mult_vartime(a: &Scalar, A: &ExtendedPoint, b: &Scalar) -> ProjectivePoint {
let a_naf = a.non_adjacent_form();
let b_naf = b.non_adjacent_form();
// Build a lookup table of odd multiples of A
let mut Ai = [CachedPoint::identity(); 8];
let A2 = A.double();
Ai[0] = A.to_cached();
for i in 0..7 {
Ai[i+1] = (&A2 + &Ai[i]).to_extended().to_cached();
}
// Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A]
// Find starting index
let mut i: usize = 255;
for j in (0..255).rev() {
i = j;
if a_naf[i] != 0 || b_naf[i] != 0 {
break;
}
}
let mut r = ProjectivePoint::identity();
loop {
let mut t = r.double();
if a_naf[i] > 0 {
t = &t.to_extended() + &Ai[( a_naf[i]/2) as usize];
} else if a_naf[i] < 0 {
t = &t.to_extended() - &Ai[(-a_naf[i]/2) as usize];
}
if b_naf[i] > 0 {
t = &t.to_extended() + &constants::bi[( b_naf[i]/2) as usize];
} else if b_naf[i] < 0 {
t = &t.to_extended() - &constants::bi[(-b_naf[i]/2) as usize];
}
r = t.to_projective();
if i == 0 {
break;
}
i -= 1;
}
r
}
/// 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 + CTAssignable, for<'a> &'a T: Neg