2016-12-08 05:12:00 +00:00
|
|
|
|
// -*- 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
|
|
|
|
|
|
// <http://creativecommons.org/publicdomain/zero/.0/> for full details.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Authors:
|
|
|
|
|
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
|
|
|
|
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
|
|
|
|
|
|
|
|
|
|
//! 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². <span style="float: right">(1)</span>
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! 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². <span style="float: right">(2)<span>
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! 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). <span style="float: right">(3)</span>
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! 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₀². <span style="float: right">(4)</span>
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Up to variable naming, this is exactly the curve model introduced
|
|
|
|
|
|
//! in ["Twisted Edwards Curves
|
2017-01-23 05:29:02 +00:00
|
|
|
|
//! Revisited"](https://www.iacr.org/archive/asiacrypt2008/53500329/53500329.pdf)
|
2016-12-08 05:12:00 +00:00
|
|
|
|
//! 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 <span style="float: right">(5)</span>
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! W₂/W₃ = ZY/ZT = Y/T = y, <span style="float: right">(6)</span>
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! 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
|
2017-02-25 00:47:06 +00:00
|
|
|
|
//! implementation for [Ed25519](https://ed25519.cr.yp.to/ed25519-20110926.pdf),
|
|
|
|
|
|
//! we use several different models for curve points:
|
2016-12-08 05:12:00 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! * 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:
|
|
|
|
|
|
//!
|
2017-02-25 00:47:06 +00:00
|
|
|
|
//! * AffineNielsPoint: `(y+x, y-x, 2dxy)`
|
|
|
|
|
|
//! * ProjectiveNielsPoint: `(Y+X, Y-X, Z, 2dXY)`
|
2016-12-08 05:12:00 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! [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)]
|
|
|
|
|
|
|
2017-01-14 01:43:08 +00:00
|
|
|
|
use core::fmt::Debug;
|
|
|
|
|
|
use core::iter::Iterator;
|
|
|
|
|
|
use core::ops::{Add, Sub, Neg, Index};
|
2017-02-20 01:01:42 +00:00
|
|
|
|
use core::cmp::{PartialEq, Eq};
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
|
|
|
|
|
use constants;
|
|
|
|
|
|
use field::FieldElement;
|
|
|
|
|
|
use scalar::Scalar;
|
2017-02-21 06:01:17 +00:00
|
|
|
|
use subtle::arrays_equal_ct;
|
|
|
|
|
|
use subtle::bytes_equal_ct;
|
|
|
|
|
|
use subtle::CTAssignable;
|
|
|
|
|
|
use subtle::CTEq;
|
|
|
|
|
|
use subtle::CTNegatable;
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Compressed points
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2016-12-24 01:50:24 +00:00
|
|
|
|
/// 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.
|
2016-12-08 05:12:00 +00:00
|
|
|
|
///
|
2016-12-24 01:50:24 +00:00
|
|
|
|
/// The first 255 bits of a CompressedEdwardsY represent the
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// y-coordinate. The high bit of the 32nd byte gives the sign of `x`.
|
2017-01-31 06:13:07 +00:00
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
2016-12-24 01:50:24 +00:00
|
|
|
|
pub struct CompressedEdwardsY(pub [u8; 32]);
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-01-27 02:15:07 +00:00
|
|
|
|
impl Debug for CompressedEdwardsY {
|
2017-01-14 01:43:08 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
write!(f, "CompressedPoint: {:?}", &self.0[..])
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-24 01:50:24 +00:00
|
|
|
|
impl Index<usize> for CompressedEdwardsY {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
type Output = u8;
|
|
|
|
|
|
|
|
|
|
|
|
fn index<'a>(&'a self, _index: usize) -> &'a u8 {
|
|
|
|
|
|
let ret: &'a u8 = &(self.0[_index]);
|
|
|
|
|
|
ret
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-24 01:50:24 +00:00
|
|
|
|
impl CompressedEdwardsY {
|
|
|
|
|
|
/// View this `CompressedEdwardsY` as an array of bytes.
|
2017-03-04 02:00:59 +00:00
|
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
self.0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Attempt to decompress to an `ExtendedPoint`.
|
|
|
|
|
|
///
|
2017-02-23 04:51:44 +00:00
|
|
|
|
/// Returns `None` if the input is not the `y`-coordinate of a
|
|
|
|
|
|
/// curve point.
|
2016-12-08 05:12:00 +00:00
|
|
|
|
pub fn decompress(&self) -> Option<ExtendedPoint> { // FromBytes()
|
2017-02-23 04:51:44 +00:00
|
|
|
|
let Y = FieldElement::from_bytes(&self.0);
|
|
|
|
|
|
let Z = FieldElement::one();
|
|
|
|
|
|
let YY = Y.square();
|
|
|
|
|
|
let u = &YY - &Z; // u = y²-1
|
|
|
|
|
|
let v = &(&YY * &constants::d) + &Z; // v = dy²+1
|
|
|
|
|
|
let (is_nonzero_square, mut X) = FieldElement::sqrt_ratio(&u, &v);
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-02-23 04:51:44 +00:00
|
|
|
|
if is_nonzero_square != 1u8 { return None; }
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-02-23 04:51:44 +00:00
|
|
|
|
// Flip the sign of X if it's not correct
|
|
|
|
|
|
let compressed_sign_bit = self[31] >> 7;
|
|
|
|
|
|
let current_sign_bit = X.is_negative_ed25519();
|
|
|
|
|
|
X.conditional_negate(current_sign_bit ^ compressed_sign_bit);
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-02-23 04:51:44 +00:00
|
|
|
|
Some(ExtendedPoint{ X: X, Y: Y, Z: Z, T: &X * &Y })
|
2016-12-08 05:12:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-27 17:42:49 +00:00
|
|
|
|
/// In "Montgomery u" format, as used in X25519, a point `(u,v)` on
|
|
|
|
|
|
/// the Montgomery curve
|
|
|
|
|
|
///
|
|
|
|
|
|
/// v^2 = u * (u^2 + 486662*u + 1)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// is represented just by `u`. Note that we use `(u,v)` instead of
|
|
|
|
|
|
/// `(x,y)` for Montgomery coordinates to avoid confusion with Edwards
|
|
|
|
|
|
/// coordinates. For Montgomery curves, it is possible to compute the
|
|
|
|
|
|
/// `u`-coordinate of `n(u,v)` just from `n` and `u`, so it is not
|
|
|
|
|
|
/// necessary to use `v` for a Diffie-Hellman key exchange.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// XXX add note on monty, twist security, edwards impl of x25519, rfc7748
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
|
|
pub struct CompressedMontgomeryU(pub [u8; 32]);
|
|
|
|
|
|
|
|
|
|
|
|
impl CompressedMontgomeryU {
|
|
|
|
|
|
/// View this `CompressedMontgomeryU` as an array of bytes.
|
2017-03-04 02:00:59 +00:00
|
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
2016-12-27 17:42:49 +00:00
|
|
|
|
self.0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Attempt to decompress to an `ExtendedPoint`.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Note that since there are two curve points with the same
|
|
|
|
|
|
/// `u`-coordinate, the `u`-coordinate does not fully specify a
|
|
|
|
|
|
/// point.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// XXX match behaviour in Signal specification re: sign choice
|
|
|
|
|
|
/// and rewrite this note
|
|
|
|
|
|
///
|
|
|
|
|
|
/// XXX check for div by zero: when is u = -1 ?
|
|
|
|
|
|
/// XXX exceptional points for the birational map
|
|
|
|
|
|
pub fn decompress(&self) -> Option<ExtendedPoint> {
|
|
|
|
|
|
// u = (1 + y) / (1 - y)
|
|
|
|
|
|
// v = sqrt(-486664) * u / x
|
|
|
|
|
|
//
|
|
|
|
|
|
// so
|
|
|
|
|
|
//
|
|
|
|
|
|
// y = (u - 1) / (u + 1)
|
|
|
|
|
|
|
|
|
|
|
|
let u = FieldElement::from_bytes(&self.0);
|
2017-02-28 05:13:24 +00:00
|
|
|
|
|
|
|
|
|
|
// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660.
|
|
|
|
|
|
// But 486660 is nonsquare mod p, so this is not a curve point.
|
|
|
|
|
|
//
|
|
|
|
|
|
// XXX what does Signal do here?
|
|
|
|
|
|
//
|
|
|
|
|
|
// Note: currently, without this check, u = -1 will accidentally
|
|
|
|
|
|
// decode to a valid (but incorrect) point, since 0.invert() = 0.
|
|
|
|
|
|
if u == FieldElement::minus_one() {
|
|
|
|
|
|
return None;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-27 17:42:49 +00:00
|
|
|
|
let u_plus_1_inv = (&u + &FieldElement::one()).invert();
|
|
|
|
|
|
let y = &(&u - &FieldElement::one()) * &u_plus_1_inv;
|
|
|
|
|
|
|
|
|
|
|
|
// XXX this does two inversions: the above + one in .decompress()
|
|
|
|
|
|
// is it possible to do one?
|
|
|
|
|
|
CompressedEdwardsY(y.to_bytes()).decompress()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// 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).
|
2017-02-20 00:10:45 +00:00
|
|
|
|
// 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
|
2016-12-08 05:12:00 +00:00
|
|
|
|
#[derive(Copy, Clone)]
|
2017-02-20 00:10:45 +00:00
|
|
|
|
#[allow(missing_docs)]
|
2016-12-08 05:12:00 +00:00
|
|
|
|
pub struct ExtendedPoint {
|
2017-02-20 00:10:45 +00:00
|
|
|
|
pub X: FieldElement,
|
|
|
|
|
|
pub Y: FieldElement,
|
|
|
|
|
|
pub Z: FieldElement,
|
|
|
|
|
|
pub T: FieldElement,
|
2016-12-08 05:12:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
/// 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).
|
2017-01-09 20:52:13 +00:00
|
|
|
|
// Safe to derive Eq because affine coordinates.
|
|
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
2016-12-08 05:12:00 +00:00
|
|
|
|
#[allow(missing_docs)]
|
2017-02-25 00:47:06 +00:00
|
|
|
|
pub struct AffineNielsPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
pub y_plus_x: FieldElement,
|
|
|
|
|
|
pub y_minus_x: FieldElement,
|
|
|
|
|
|
pub xy2d: FieldElement,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
/// 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).
|
2016-12-08 05:12:00 +00:00
|
|
|
|
#[derive(Copy, Clone)]
|
2017-02-25 00:47:06 +00:00
|
|
|
|
pub struct ProjectiveNielsPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
Y_plus_X: FieldElement,
|
|
|
|
|
|
Y_minus_X: FieldElement,
|
|
|
|
|
|
Z: FieldElement,
|
|
|
|
|
|
T2d: FieldElement,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Constructors
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-01-12 22:27:48 +00:00
|
|
|
|
/// Trait for curve point types which have an identity constructor.
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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() }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl Identity for ProjectiveNielsPoint {
|
|
|
|
|
|
fn identity() -> ProjectiveNielsPoint {
|
|
|
|
|
|
ProjectiveNielsPoint{ Y_plus_X: FieldElement::one(),
|
2016-12-08 05:12:00 +00:00
|
|
|
|
Y_minus_X: FieldElement::one(),
|
|
|
|
|
|
Z: FieldElement::one(),
|
|
|
|
|
|
T2d: FieldElement::zero() }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl Identity for AffineNielsPoint {
|
|
|
|
|
|
fn identity() -> AffineNielsPoint {
|
|
|
|
|
|
AffineNielsPoint{
|
2016-12-08 05:12:00 +00:00
|
|
|
|
y_plus_x: FieldElement::one(),
|
|
|
|
|
|
y_minus_x: FieldElement::one(),
|
|
|
|
|
|
xy2d: FieldElement::zero(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-30 00:19:16 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// 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::d * &(&XX * &YY));
|
|
|
|
|
|
|
|
|
|
|
|
lhs == rhs
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ValidityCheck for ExtendedPoint {
|
|
|
|
|
|
// XXX this should also check that T is correct
|
|
|
|
|
|
fn is_valid(&self) -> bool {
|
|
|
|
|
|
self.to_projective().is_valid()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Constant-time assignment
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl CTAssignable for ProjectiveNielsPoint {
|
|
|
|
|
|
fn conditional_assign(&mut self, other: &ProjectiveNielsPoint, choice: u8) {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl CTAssignable for AffineNielsPoint {
|
|
|
|
|
|
fn conditional_assign(&mut self, other: &AffineNielsPoint, choice: u8) {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
// 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-12 22:27:48 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Constant-time Equality
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
impl CTEq for ExtendedPoint {
|
|
|
|
|
|
fn ct_eq(&self, other: &ExtendedPoint) -> u8 {
|
|
|
|
|
|
arrays_equal_ct(&self.compress().0, &other.compress().0)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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<T> IsIdentity for T where T: CTEq + Identity {
|
|
|
|
|
|
fn is_identity(&self) -> bool {
|
|
|
|
|
|
let identity: T = T::identity();
|
|
|
|
|
|
|
|
|
|
|
|
if self.ct_eq(&identity) == 1u8 {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// 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,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-24 01:50:24 +00:00
|
|
|
|
/// Convert this point to a `CompressedEdwardsY`
|
|
|
|
|
|
pub fn compress(&self) -> CompressedEdwardsY {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
let recip = self.Z.invert();
|
|
|
|
|
|
let x = &self.X * &recip;
|
|
|
|
|
|
let y = &self.Y * &recip;
|
|
|
|
|
|
let mut s: [u8; 32];
|
|
|
|
|
|
|
|
|
|
|
|
s = y.to_bytes();
|
2017-02-01 01:03:29 +00:00
|
|
|
|
s[31] ^= (x.is_negative_ed25519() << 7) as u8;
|
2016-12-24 01:50:24 +00:00
|
|
|
|
CompressedEdwardsY(s)
|
2016-12-08 05:12:00 +00:00
|
|
|
|
}
|
2016-12-27 17:42:49 +00:00
|
|
|
|
|
|
|
|
|
|
/// Convert this point to a `CompressedMontgomeryU`.
|
|
|
|
|
|
/// Note that this discards the sign.
|
|
|
|
|
|
///
|
2017-02-28 05:13:24 +00:00
|
|
|
|
/// # Return
|
|
|
|
|
|
/// - `None` if `self` is the identity point;
|
|
|
|
|
|
/// - `Some(CompressedMontgomeryU)` otherwise.
|
|
|
|
|
|
///
|
|
|
|
|
|
pub fn compress_montgomery(&self) -> Option<CompressedMontgomeryU> {
|
2016-12-27 17:42:49 +00:00
|
|
|
|
// u = (1 + y) / (1 - y)
|
|
|
|
|
|
// v = sqrt(-486664) * u / x
|
|
|
|
|
|
//
|
|
|
|
|
|
// since y = Y/Z, x = X/Z,
|
|
|
|
|
|
//
|
|
|
|
|
|
// u = (1 + Y/Z) / (1 - Y/Z);
|
|
|
|
|
|
// = (Z + Y) / (Z - Y);
|
2017-02-28 05:13:24 +00:00
|
|
|
|
//
|
|
|
|
|
|
// exceptional points:
|
|
|
|
|
|
// y = 1 <=> Y/Z = 1 <=> Z - Y = 0
|
2016-12-27 17:42:49 +00:00
|
|
|
|
let Z_plus_Y = &self.Z + &self.Y;
|
|
|
|
|
|
let Z_minus_Y = &self.Z - &self.Y;
|
|
|
|
|
|
let u = &Z_plus_Y * &Z_minus_Y.invert();
|
|
|
|
|
|
|
2017-02-28 05:13:24 +00:00
|
|
|
|
if Z_minus_Y.is_zero() == 0u8 {
|
2017-02-28 07:15:39 +00:00
|
|
|
|
Some(CompressedMontgomeryU(u.to_bytes()))
|
2017-02-28 05:13:24 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
2016-12-27 17:42:49 +00:00
|
|
|
|
}
|
2016-12-08 05:12:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ExtendedPoint {
|
2017-02-25 00:47:06 +00:00
|
|
|
|
/// Convert to a ProjectiveNielsPoint
|
|
|
|
|
|
pub fn to_cached(&self) -> ProjectiveNielsPoint {
|
|
|
|
|
|
ProjectiveNielsPoint{
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-24 01:50:24 +00:00
|
|
|
|
/// Compress this point to `CompressedEdwardsY` format
|
|
|
|
|
|
pub fn compress(&self) -> CompressedEdwardsY {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
self.to_projective().compress()
|
|
|
|
|
|
}
|
2017-01-09 01:17:47 +00:00
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
/// Dehomogenize to a AffineNielsPoint.
|
2017-01-09 01:17:47 +00:00
|
|
|
|
/// Mainly for testing.
|
2017-02-25 00:47:06 +00:00
|
|
|
|
pub fn to_precomputed(&self) -> AffineNielsPoint {
|
2017-01-09 01:17:47 +00:00
|
|
|
|
let recip = self.Z.invert();
|
|
|
|
|
|
let x = &self.X * &recip;
|
|
|
|
|
|
let y = &self.Y * &recip;
|
|
|
|
|
|
let xy2d = &(&x * &y) * &constants::d2;
|
2017-02-25 00:47:06 +00:00
|
|
|
|
AffineNielsPoint{
|
2017-01-09 01:17:47 +00:00
|
|
|
|
y_plus_x: &y + &x,
|
|
|
|
|
|
y_minus_x: &y - &x,
|
|
|
|
|
|
xy2d: xy2d
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-12-27 17:42:49 +00:00
|
|
|
|
|
2017-02-28 07:15:39 +00:00
|
|
|
|
/// Convert this point to a `CompressedMontgomeryU`.
|
|
|
|
|
|
/// Note that this discards the sign.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Return
|
|
|
|
|
|
/// - `None` if `self` is the identity point;
|
|
|
|
|
|
/// - `Some(CompressedMontgomeryU)` otherwise.
|
|
|
|
|
|
///
|
|
|
|
|
|
pub fn compress_montgomery(&self) -> Option<CompressedMontgomeryU> {
|
2016-12-27 17:42:49 +00:00
|
|
|
|
self.to_projective().compress_montgomery()
|
|
|
|
|
|
}
|
2016-12-08 05:12:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl CompletedPoint {
|
2017-01-09 20:54:06 +00:00
|
|
|
|
/// Convert to a ProjectivePoint
|
|
|
|
|
|
pub fn to_projective(&self) -> ProjectivePoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
ProjectivePoint{
|
|
|
|
|
|
X: &self.X * &self.T,
|
|
|
|
|
|
Y: &self.Y * &self.Z,
|
|
|
|
|
|
Z: &self.Z * &self.T,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-09 20:54:06 +00:00
|
|
|
|
/// Convert to an ExtendedPoint
|
|
|
|
|
|
pub fn to_extended(&self) -> ExtendedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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
|
2017-01-30 00:19:39 +00:00
|
|
|
|
pub fn double(&self) -> CompletedPoint { // Double()
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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.
|
2017-01-30 00:19:39 +00:00
|
|
|
|
pub fn double(&self) -> ExtendedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
self.to_projective().double().to_extended()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Addition and Subtraction
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl<'a,'b> Add<&'b ProjectiveNielsPoint> for &'a ExtendedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
type Output = CompletedPoint;
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
fn add(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl<'a,'b> Sub<&'b ProjectiveNielsPoint> for &'a ExtendedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
type Output = CompletedPoint;
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
fn sub(self, other: &'b ProjectiveNielsPoint) -> CompletedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl<'a,'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
type Output = CompletedPoint;
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
fn add(self, other: &'b AffineNielsPoint) -> CompletedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl<'a,'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
type Output = CompletedPoint;
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
fn sub(self, other: &'b AffineNielsPoint) -> CompletedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl<'a> Neg for &'a ProjectiveNielsPoint {
|
|
|
|
|
|
type Output = ProjectiveNielsPoint;
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
fn neg(self) -> ProjectiveNielsPoint {
|
|
|
|
|
|
ProjectiveNielsPoint{
|
2016-12-08 05:12:00 +00:00
|
|
|
|
Y_plus_X: self.Y_minus_X,
|
|
|
|
|
|
Y_minus_X: self.Y_plus_X,
|
|
|
|
|
|
Z: self.Z,
|
|
|
|
|
|
T2d: -(&self.T2d),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl<'a> Neg for &'a AffineNielsPoint {
|
|
|
|
|
|
type Output = AffineNielsPoint;
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
fn neg(self) -> AffineNielsPoint {
|
|
|
|
|
|
AffineNielsPoint{
|
2016-12-08 05:12:00 +00:00
|
|
|
|
y_plus_x: self.y_minus_x,
|
|
|
|
|
|
y_minus_x: self.y_plus_x,
|
|
|
|
|
|
xy2d: -(&self.xy2d)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Scalar multiplication
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-02-20 10:04:59 +00:00
|
|
|
|
/// Trait for scalar multiplication of an arbitrary point.
|
|
|
|
|
|
pub trait ScalarMult<S> {
|
|
|
|
|
|
/// Compute `scalar * self`.
|
|
|
|
|
|
fn scalar_mult(&self, scalar: &S) -> Self;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ScalarMult<Scalar> for ExtendedPoint {
|
|
|
|
|
|
/// Scalar multiplication: compute `scalar * self`.
|
2016-12-08 05:12:00 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// Uses a window of size 4. Note: for scalar multiplication of
|
|
|
|
|
|
/// the basepoint, `basepoint_mult` is approximately 4x faster.
|
2017-02-20 10:04:59 +00:00
|
|
|
|
fn scalar_mult(&self, scalar: &Scalar) -> ExtendedPoint {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
let A = self.to_cached();
|
2017-02-25 00:47:06 +00:00
|
|
|
|
let mut As: [ProjectiveNielsPoint; 8] = [A; 8];
|
2016-12-08 05:12:00 +00:00
|
|
|
|
for i in 0..7 {
|
|
|
|
|
|
As[i+1] = (self + &As[i]).to_extended().to_cached();
|
|
|
|
|
|
}
|
2017-02-20 10:04:59 +00:00
|
|
|
|
let e = scalar.to_radix_16();
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-02-20 10:04:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Trait for scalar multiplication of a distinguished basepoint.
|
|
|
|
|
|
pub trait BasepointMult<S> {
|
|
|
|
|
|
/// Return the basepoint `B`.
|
|
|
|
|
|
fn basepoint() -> Self;
|
|
|
|
|
|
/// Compute `scalar * B`.
|
|
|
|
|
|
fn basepoint_mult(scalar: &S) -> Self;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl BasepointMult<Scalar> for ExtendedPoint {
|
|
|
|
|
|
fn basepoint() -> ExtendedPoint {
|
|
|
|
|
|
constants::BASEPOINT
|
|
|
|
|
|
}
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-02-20 10:04:59 +00:00
|
|
|
|
/// Construct an `ExtendedPoint` from a `Scalar`, `scalar`, by
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// 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.
|
2017-02-20 10:04:59 +00:00
|
|
|
|
fn basepoint_mult(scalar: &Scalar) -> ExtendedPoint { //GeScalarMultBase
|
|
|
|
|
|
let e = scalar.to_radix_16();
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
2017-02-20 10:04:59 +00:00
|
|
|
|
}
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2017-02-20 10:04:59 +00:00
|
|
|
|
impl ExtendedPoint {
|
2017-01-06 15:46:55 +00:00
|
|
|
|
/// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// 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();
|
|
|
|
|
|
}
|
2017-01-06 21:29:27 +00:00
|
|
|
|
|
|
|
|
|
|
/// Determine if this point is of small order.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// The order of the group of points on the curve Ɛ is |Ɛ| = 8q. Thus, to
|
|
|
|
|
|
/// check if a point P is of small order, we multiply by 8 and then test
|
|
|
|
|
|
/// if the result is equal to the identity.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Return
|
|
|
|
|
|
///
|
|
|
|
|
|
/// True if it is of small order; false otherwise.
|
|
|
|
|
|
pub fn is_small_order(&self) -> bool {
|
|
|
|
|
|
let p8: ExtendedPoint = self.mult_by_pow_2(3);
|
|
|
|
|
|
|
|
|
|
|
|
if p8.is_identity() {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-12-08 05:12:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 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
|
2017-02-25 00:47:06 +00:00
|
|
|
|
let mut Ai = [ProjectiveNielsPoint::identity(); 8];
|
2016-12-08 05:12:00 +00:00
|
|
|
|
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<T>(x: i8, points: &[T; 8]) -> T
|
|
|
|
|
|
where T: Identity + CTAssignable, for<'a> &'a T: Neg<Output=T>
|
|
|
|
|
|
{
|
|
|
|
|
|
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_ct(xabs as u8, j as u8));
|
|
|
|
|
|
}
|
|
|
|
|
|
// Now t == |x| * P.
|
|
|
|
|
|
|
|
|
|
|
|
let neg_mask = (xmask & 1) as u8;
|
2017-02-20 23:22:28 +00:00
|
|
|
|
t.conditional_negate(neg_mask);
|
2016-12-08 05:12:00 +00:00
|
|
|
|
// Now t == x * P.
|
|
|
|
|
|
|
|
|
|
|
|
t
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Elligator2 (uniform encoding/decoding of curve points)
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
impl ExtendedPoint {
|
|
|
|
|
|
/// Use Elligator2 to try to convert `self` to a uniformly random
|
|
|
|
|
|
/// string.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Returns `Some<[u8;32]>` if `self` is in the image of the
|
|
|
|
|
|
/// Elligator2 map. For a random point on the curve, this happens
|
|
|
|
|
|
/// with probability 1/2. Otherwise, returns `None`.
|
|
|
|
|
|
pub fn to_uniform_representative(&self) -> Option<[u8;32]> {
|
|
|
|
|
|
unimplemented!();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Use Elligator2 to convert a uniformly random string to a curve
|
|
|
|
|
|
/// point.
|
|
|
|
|
|
#[allow(unused_variables)] // REMOVE WHEN IMPLEMENTED
|
|
|
|
|
|
pub fn from_uniform_representative(bytes: &[u8;32]) -> ExtendedPoint {
|
|
|
|
|
|
unimplemented!();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Debug traits
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
impl Debug for ExtendedPoint {
|
2017-01-14 01:43:08 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
write!(f, "ExtendedPoint(\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n)",
|
|
|
|
|
|
&self.X, &self.Y, &self.Z, &self.T)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Debug for ProjectivePoint {
|
2017-01-14 01:43:08 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
write!(f, "ProjectivePoint(\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?}\n)",
|
|
|
|
|
|
&self.X, &self.Y, &self.Z)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Debug for CompletedPoint {
|
2017-01-14 01:43:08 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2016-12-08 05:12:00 +00:00
|
|
|
|
write!(f, "CompletedPoint(\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n)",
|
|
|
|
|
|
&self.X, &self.Y, &self.Z, &self.T)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl Debug for AffineNielsPoint {
|
2017-01-14 01:43:08 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2017-02-25 00:47:06 +00:00
|
|
|
|
write!(f, "AffineNielsPoint(\n\ty_plus_x: {:?},\n\ty_minus_x: {:?},\n\txy2d: {:?}\n)",
|
2016-12-08 05:12:00 +00:00
|
|
|
|
&self.y_plus_x, &self.y_minus_x, &self.xy2d)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
impl Debug for ProjectiveNielsPoint {
|
2017-01-14 01:43:08 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2017-02-25 00:47:06 +00:00
|
|
|
|
write!(f, "ProjectiveNielsPoint(\n\tY_plus_X: {:?},\n\tY_minus_X: {:?},\n\tZ: {:?},\n\tT2d: {:?}\n)",
|
2016-12-08 05:12:00 +00:00
|
|
|
|
&self.Y_plus_X, &self.Y_minus_X, &self.Z, &self.T2d)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Tests
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod test {
|
|
|
|
|
|
use test::Bencher;
|
2017-02-20 01:01:42 +00:00
|
|
|
|
use rand::OsRng;
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
use field::FieldElement;
|
|
|
|
|
|
use scalar::Scalar;
|
2017-02-21 06:01:17 +00:00
|
|
|
|
use subtle::CTAssignable;
|
2016-12-08 05:12:00 +00:00
|
|
|
|
use constants;
|
2017-01-09 01:41:39 +00:00
|
|
|
|
use constants::BASE_CMPRSSD;
|
2016-12-08 05:12:00 +00:00
|
|
|
|
use super::*;
|
|
|
|
|
|
use super::select_precomputed_point;
|
|
|
|
|
|
|
2016-12-27 17:42:49 +00:00
|
|
|
|
/// The X25519 basepoint, in compressed Montgomery form.
|
|
|
|
|
|
static BASE_CMPRSSD_MONTY: CompressedMontgomeryU =
|
|
|
|
|
|
CompressedMontgomeryU([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// X coordinate of the basepoint.
|
|
|
|
|
|
/// = 15112221349535400772501151409588531511454012693041857206046113283949847762202
|
|
|
|
|
|
static BASE_X_COORD_BYTES: [u8; 32] =
|
|
|
|
|
|
[0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69,
|
|
|
|
|
|
0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21];
|
|
|
|
|
|
|
2016-12-24 01:50:24 +00:00
|
|
|
|
static BASE2_CMPRSSD: CompressedEdwardsY =
|
|
|
|
|
|
CompressedEdwardsY([0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0xe,
|
|
|
|
|
|
0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39, 0x97,
|
|
|
|
|
|
0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d,
|
|
|
|
|
|
0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd, 0x60, 0x22]);
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
2016-12-24 01:50:24 +00:00
|
|
|
|
static BASE16_CMPRSSD: CompressedEdwardsY =
|
|
|
|
|
|
CompressedEdwardsY([0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8,
|
|
|
|
|
|
0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a, 0xb0,
|
|
|
|
|
|
0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f,
|
|
|
|
|
|
0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96, 0x96, 0x70]);
|
2016-12-08 05:12:00 +00:00
|
|
|
|
|
|
|
|
|
|
/// 4493907448824000747700850167940867464579944529806937181821189941592931634714
|
|
|
|
|
|
static A_SCALAR: Scalar = Scalar([
|
|
|
|
|
|
0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d,
|
|
|
|
|
|
0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d,
|
|
|
|
|
|
0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1,
|
|
|
|
|
|
0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09]);
|
|
|
|
|
|
|
|
|
|
|
|
/// 2506056684125797857694181776241676200180934651973138769173342316833279714961
|
|
|
|
|
|
static B_SCALAR: Scalar = Scalar([
|
|
|
|
|
|
0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b,
|
|
|
|
|
|
0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0, 0xb3, 0x2e,
|
|
|
|
|
|
0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4,
|
|
|
|
|
|
0x56, 0xa7, 0xd4, 0xaa, 0xb8, 0x60, 0x8a, 0x05]);
|
|
|
|
|
|
|
|
|
|
|
|
/// A_SCALAR * basepoint, computed with ed25519.py
|
2016-12-24 01:50:24 +00:00
|
|
|
|
static A_TIMES_BASEPOINT: CompressedEdwardsY = CompressedEdwardsY([
|
2016-12-08 05:12:00 +00:00
|
|
|
|
0xea, 0x27, 0xe2, 0x60, 0x53, 0xdf, 0x1b, 0x59,
|
|
|
|
|
|
0x56, 0xf1, 0x4d, 0x5d, 0xec, 0x3c, 0x34, 0xc3,
|
|
|
|
|
|
0x84, 0xa2, 0x69, 0xb7, 0x4c, 0xc3, 0x80, 0x3e,
|
|
|
|
|
|
0xa8, 0xe2, 0xe7, 0xc9, 0x42, 0x5e, 0x40, 0xa5]);
|
|
|
|
|
|
|
|
|
|
|
|
/// A_SCALAR * (A_TIMES_BASEPOINT) + B_SCALAR * BASEPOINT
|
2016-12-24 01:50:24 +00:00
|
|
|
|
static DOUBLE_SCALAR_MULT_RESULT: CompressedEdwardsY = CompressedEdwardsY([
|
2016-12-08 05:12:00 +00:00
|
|
|
|
0x7d, 0xfd, 0x6c, 0x45, 0xaf, 0x6d, 0x6e, 0x0e,
|
|
|
|
|
|
0xba, 0x20, 0x37, 0x1a, 0x23, 0x64, 0x59, 0xc4,
|
|
|
|
|
|
0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85,
|
|
|
|
|
|
0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13, 0x2b, 0x42]);
|
|
|
|
|
|
|
2016-12-27 17:42:49 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
/// Test that the constant for sqrt(-486664) really is a square
|
|
|
|
|
|
/// root of -486664.
|
|
|
|
|
|
/// XXX this should be a test in constants.rs ??
|
|
|
|
|
|
fn test_sqrt_minus_aplus2() {
|
|
|
|
|
|
let minus_aplus2 = FieldElement([-486664,0,0,0,0,0,0,0,0,0]);
|
|
|
|
|
|
let sqrt = constants::SQRT_MINUS_APLUS2;
|
|
|
|
|
|
let sq = &sqrt * &sqrt;
|
|
|
|
|
|
assert_eq!(sq, minus_aplus2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test Montgomery conversion against the X25519 basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_to_montgomery() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
2017-02-28 07:15:39 +00:00
|
|
|
|
let bp_monty = bp.compress_montgomery().unwrap();
|
2016-12-27 17:42:49 +00:00
|
|
|
|
assert_eq!(bp_monty, BASE_CMPRSSD_MONTY);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test Montgomery conversion against the X25519 basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_from_montgomery() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD_MONTY.decompress().unwrap();
|
|
|
|
|
|
let bp_compressed_edwards = bp.compress();
|
|
|
|
|
|
assert_eq!(bp_compressed_edwards, BASE_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-02 14:24:58 +00:00
|
|
|
|
/// If u = -1, then v^2 = u*(u^2+486662*u+1) = 486660.
|
|
|
|
|
|
/// But 486660 is nonsquare mod p, so this should fail.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// XXX what does Signal do here?
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_u_minus_one_monty() {
|
|
|
|
|
|
let mut m1 = FieldElement::zero();
|
|
|
|
|
|
m1[0] = -1;
|
|
|
|
|
|
let m1_bytes = m1.to_bytes();
|
|
|
|
|
|
let div_by_zero_u = CompressedMontgomeryU(m1_bytes);
|
2017-02-28 05:13:24 +00:00
|
|
|
|
assert!(div_by_zero_u.decompress().is_none());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Montgomery compression of the identity point should
|
|
|
|
|
|
/// fail (it's sent to infinity).
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_identity_to_monty() {
|
|
|
|
|
|
let id = ExtendedPoint::identity();
|
2017-02-28 07:15:39 +00:00
|
|
|
|
assert!(id.compress_montgomery().is_none());
|
2017-01-02 14:24:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// Test round-trip decompression for the basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_decompression_compression() {
|
|
|
|
|
|
let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES);
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let bp2 = BASE2_CMPRSSD.decompress().unwrap();
|
2017-01-30 00:19:16 +00:00
|
|
|
|
assert!( bp.is_valid());
|
|
|
|
|
|
assert!(bp2.is_valid());
|
2016-12-08 05:12:00 +00:00
|
|
|
|
let compressed = bp.compress();
|
|
|
|
|
|
let compressed2 = bp2.compress();
|
|
|
|
|
|
// Check that decompression actually gives the correct X coordinate
|
|
|
|
|
|
assert_eq!(base_X, bp.X);
|
|
|
|
|
|
assert_eq!(compressed, BASE_CMPRSSD);
|
|
|
|
|
|
assert_eq!(compressed2, BASE2_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test sign handling in decompression
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_decompression_sign_handling() {
|
|
|
|
|
|
let mut m_bp_bytes: [u8;32] = BASE_CMPRSSD.to_bytes().clone();
|
|
|
|
|
|
// Set the high bit of the last byte to flip the sign
|
|
|
|
|
|
m_bp_bytes[31] |= 1 << 7;
|
2016-12-24 01:50:24 +00:00
|
|
|
|
let m_bp = CompressedEdwardsY(m_bp_bytes).decompress().unwrap();
|
2016-12-08 05:12:00 +00:00
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
assert_eq!(m_bp.X, -(&bp.X));
|
|
|
|
|
|
assert_eq!(m_bp.Y, bp.Y);
|
|
|
|
|
|
assert_eq!(m_bp.Z, bp.Z);
|
|
|
|
|
|
assert_eq!(m_bp.T, -(&bp.T));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test that computing 1*basepoint gives the correct basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_mult_one_vs_basepoint() {
|
|
|
|
|
|
let bp = ExtendedPoint::basepoint_mult(&Scalar::one());
|
|
|
|
|
|
let compressed = bp.compress();
|
|
|
|
|
|
assert_eq!(compressed, BASE_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test `impl Add<ExtendedPoint> for ExtendedPoint`
|
|
|
|
|
|
/// using basepoint + basepoint versus the 2*basepoint constant.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_plus_basepoint() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let bp_added = &bp + &bp;
|
|
|
|
|
|
assert_eq!( bp_added.compress(), BASE2_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
/// Test `impl Add<ProjectiveNielsPoint> for ExtendedPoint`
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// using the basepoint, basepoint2 constants
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_plus_basepoint_cached() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let bp_added = (&bp + &bp.to_cached()).to_extended();
|
|
|
|
|
|
assert_eq!( bp_added.compress(), BASE2_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-25 00:47:06 +00:00
|
|
|
|
/// Test `impl Add<AffineNielsPoint> for ExtendedPoint`
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// using the basepoint, basepoint2 constants
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_plus_basepoint_precomputed() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
// on decode, Z =1, so x = X/Z = X, y = Y/Z = Y, xy = T
|
2017-02-25 00:47:06 +00:00
|
|
|
|
let bp_precomputed = AffineNielsPoint{
|
2016-12-08 05:12:00 +00:00
|
|
|
|
y_plus_x: &bp.Y + &bp.X,
|
|
|
|
|
|
y_minus_x: &bp.Y - &bp.X,
|
|
|
|
|
|
xy2d: &bp.T * &constants::d2,
|
|
|
|
|
|
};
|
|
|
|
|
|
let bp_added = (&bp + &bp_precomputed).to_extended();
|
|
|
|
|
|
assert_eq!( bp_added.compress(), BASE2_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-21 03:13:30 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_extended_point_equality() {
|
|
|
|
|
|
let two = [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
|
|
|
|
|
|
let id1 = ExtendedPoint::identity();
|
|
|
|
|
|
let id2 = ExtendedPoint{
|
|
|
|
|
|
X: FieldElement::zero(),
|
|
|
|
|
|
Y: FieldElement::from_bytes(&two),
|
|
|
|
|
|
Z: FieldElement::from_bytes(&two),
|
|
|
|
|
|
T: FieldElement::zero()};
|
|
|
|
|
|
|
|
|
|
|
|
assert!(id1.ct_eq(&id2) == 1u8);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-09 01:17:47 +00:00
|
|
|
|
/// Sanity check for conversion to precomputed points
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_convert_to_precomputed() {
|
|
|
|
|
|
// construct a point as aB so it has denominators (ie. Z != 1)
|
|
|
|
|
|
let aB = ExtendedPoint::basepoint_mult(&A_SCALAR);
|
|
|
|
|
|
let aB_pc = aB.to_precomputed();
|
|
|
|
|
|
let id = ExtendedPoint::identity();
|
|
|
|
|
|
let P = &id + &aB_pc;
|
|
|
|
|
|
assert_eq!(P.to_extended().compress(), aB.compress())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
/// Test basepoint_mult versus a known scalar multiple from ed25519.py
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_mult() {
|
|
|
|
|
|
let aB = ExtendedPoint::basepoint_mult(&A_SCALAR);
|
|
|
|
|
|
assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test scalar_mult versus a known scalar multiple from ed25519.py
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_scalar_mult() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let aB = bp.scalar_mult(&A_SCALAR);
|
|
|
|
|
|
assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test double_scalar_mult_vartime vs ed25519.py
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_double_scalar_mult_vartime() {
|
|
|
|
|
|
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
|
|
|
|
|
let result = double_scalar_mult_vartime(&A_SCALAR, &A, &B_SCALAR);
|
|
|
|
|
|
assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test basepoint.double() versus the 2*basepoint constant.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_double() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let bp_doubled = bp.double();
|
|
|
|
|
|
assert_eq!(bp_doubled.compress(), BASE2_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test that computing 2*basepoint is the same as basepoint.double()
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_scalar_mult_two_vs_double() {
|
|
|
|
|
|
// XXX this seems like a pain point: better way to construct small
|
|
|
|
|
|
// scalars?
|
|
|
|
|
|
let two = Scalar([ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]);
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let bp_doubled = bp.double();
|
|
|
|
|
|
let bp2 = ExtendedPoint::basepoint_mult(&two);
|
|
|
|
|
|
assert_eq!(bp_doubled.compress(), bp2.compress());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basepoint_projective_extended_round_trip() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let bp_roundtrip = bp.to_projective().to_extended();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(BASE_CMPRSSD, bp_roundtrip.compress());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test computing 16*basepoint vs mult_by_pow_2
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_mult_by_pow_2() {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let bp16 = bp.mult_by_pow_2(4);
|
|
|
|
|
|
assert_eq!(bp16.compress(), BASE16_CMPRSSD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The basepoint, doubled, minus the basepoint should equal the basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_ge_sub() {
|
|
|
|
|
|
let p1: ExtendedPoint = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let p2: ExtendedPoint = BASE2_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let p3: ExtendedPoint = (&p2 - &p1.to_cached()).to_extended();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(p1.compress(), p3.compress());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The basepoint plus the identity should equal the basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_ge_add() {
|
|
|
|
|
|
let p1: ExtendedPoint = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let p2: ExtendedPoint = ExtendedPoint::identity();
|
|
|
|
|
|
let p3: ExtendedPoint = (&p1 + &p2.to_cached()).to_extended();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(p1.compress(), p3.compress());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
2017-02-25 00:47:06 +00:00
|
|
|
|
fn test_AffineNielsPoint_conditional_assign() {
|
|
|
|
|
|
let id = AffineNielsPoint::identity();
|
|
|
|
|
|
let mut p1 = AffineNielsPoint::identity();
|
|
|
|
|
|
let p2: AffineNielsPoint = AffineNielsPoint{
|
2016-12-08 05:12:00 +00:00
|
|
|
|
y_plus_x: FieldElement([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
|
|
|
|
|
|
y_minus_x: FieldElement([11, 22, 33, 44, 55, 66, 77, 88, 99, 100]),
|
|
|
|
|
|
xy2d: FieldElement([10, 20, 30, 40, 50, 60, 70, 80, 90, 101]),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
p1.conditional_assign(&p2, 0);
|
|
|
|
|
|
assert_eq!(p1.y_plus_x, id.y_plus_x);
|
|
|
|
|
|
assert_eq!(p1.y_minus_x, id.y_minus_x);
|
|
|
|
|
|
assert_eq!(p1.xy2d, id.xy2d);
|
|
|
|
|
|
p1.conditional_assign(&p2, 1);
|
|
|
|
|
|
assert_eq!(p1.y_plus_x, p2.y_plus_x);
|
|
|
|
|
|
assert_eq!(p1.y_minus_x, p2.y_minus_x);
|
|
|
|
|
|
assert_eq!(p1.xy2d, p2.xy2d);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-06 21:29:27 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_is_small_order() {
|
|
|
|
|
|
let p1: ExtendedPoint = ExtendedPoint::identity();
|
|
|
|
|
|
let p2: ExtendedPoint = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
assert!(p1.is_small_order() == true);
|
|
|
|
|
|
assert!(p2.is_small_order() == false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-12 22:27:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_is_identity() {
|
|
|
|
|
|
assert!(ExtendedPoint::identity().is_identity());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-12-08 05:12:00 +00:00
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_basepoint_mult(b: &mut Bencher) {
|
|
|
|
|
|
b.iter(|| ExtendedPoint::basepoint_mult(&A_SCALAR));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_scalar_mult(b: &mut Bencher) {
|
|
|
|
|
|
let bp = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
b.iter(|| bp.scalar_mult(&A_SCALAR));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_select_precomputed_point(b: &mut Bencher) {
|
|
|
|
|
|
b.iter(|| select_precomputed_point(0, &constants::base[12]));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_double_scalar_mult_vartime(bench: &mut Bencher) {
|
|
|
|
|
|
let A = A_TIMES_BASEPOINT.decompress().unwrap();
|
|
|
|
|
|
bench.iter(|| double_scalar_mult_vartime(&A_SCALAR, &A, &B_SCALAR));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_extended_add_cached(b: &mut Bencher) {
|
|
|
|
|
|
let p1 = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let p2 = BASE2_CMPRSSD.decompress().unwrap().to_cached();
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | &p1 + &p2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_extended_add_cached_to_extended(b: &mut Bencher) {
|
|
|
|
|
|
let p1 = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let p2 = BASE2_CMPRSSD.decompress().unwrap().to_cached();
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | (&p1 + &p2).to_extended());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_extended_add_precomputed(b: &mut Bencher) {
|
|
|
|
|
|
let p1 = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let p2 = select_precomputed_point(6, &constants::base[27]);
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | &p1 + &p2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_extended_add_precomputed_to_extended(b: &mut Bencher) {
|
|
|
|
|
|
let p1 = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
let p2 = select_precomputed_point(6, &constants::base[27]);
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | (&p1 + &p2).to_extended());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_double(b: &mut Bencher) {
|
|
|
|
|
|
let p1 = BASE_CMPRSSD.decompress().unwrap().to_projective();
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | p1.double() );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_double_to_extended(b: &mut Bencher) {
|
|
|
|
|
|
let p1 = BASE_CMPRSSD.decompress().unwrap().to_projective();
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | p1.double().to_extended() );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn bench_mult_by_pow2_4(b: &mut Bencher) {
|
|
|
|
|
|
let p1 = BASE_CMPRSSD.decompress().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | p1.mult_by_pow_2(4) );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|