2017-08-03 05:58:15 +00:00
|
|
|
|
// -*- mode: rust; -*-
|
|
|
|
|
|
//
|
2017-08-15 05:09:20 +00:00
|
|
|
|
// This file is part of curve25519-dalek.
|
|
|
|
|
|
// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
|
|
|
|
|
|
// See LICENSE for licensing information.
|
2017-08-03 05:58:15 +00:00
|
|
|
|
//
|
|
|
|
|
|
// Authors:
|
|
|
|
|
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
|
|
|
|
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
|
|
|
|
|
2017-09-07 20:31:06 +00:00
|
|
|
|
//! Montgomery arithmetic.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Apart from the compressed point implementation
|
|
|
|
|
|
//! (i.e. `CompressedMontgomeryU`), this module is a "clean room" implementation
|
|
|
|
|
|
//! of the Montgomery arithmetic described in the following papers:
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! * Costello, Craig, and Benjamin Smith. "Montgomery curves and their
|
|
|
|
|
|
//! arithmetic." Journal of Cryptographic Engineering (2017): 1-14.
|
|
|
|
|
|
//! [PDF](http://eprint.iacr.org/2017/212.pdf)
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! * Montgomery, Peter L. "Speeding the Pollard and elliptic curve methods of
|
|
|
|
|
|
//! factorization." Mathematics of computation 48.177 (1987): 243-264.
|
|
|
|
|
|
//! [PDF](http://www.ams.org/mcom/1987-48-177/S0025-5718-1987-0866113-7/)
|
2017-08-03 05:58:15 +00:00
|
|
|
|
|
|
|
|
|
|
// 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-09-07 20:31:06 +00:00
|
|
|
|
use core::ops::{Mul, MulAssign};
|
2017-08-03 05:58:15 +00:00
|
|
|
|
|
|
|
|
|
|
use constants;
|
|
|
|
|
|
use field::FieldElement;
|
|
|
|
|
|
use edwards::{ExtendedPoint, CompressedEdwardsY};
|
2017-09-07 20:31:06 +00:00
|
|
|
|
use scalar::Scalar;
|
2017-08-03 05:58:15 +00:00
|
|
|
|
|
2017-10-04 04:43:04 +00:00
|
|
|
|
// 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
|
2017-11-17 01:34:28 +00:00
|
|
|
|
//
|
|
|
|
|
|
// XXX I put these in a `traits` module for now - hdevalence
|
|
|
|
|
|
use traits::{Identity, ValidityCheck};
|
2017-09-07 20:31:06 +00:00
|
|
|
|
|
2017-08-03 05:58:15 +00:00
|
|
|
|
use subtle::ConditionallyAssignable;
|
2017-09-07 20:31:06 +00:00
|
|
|
|
use subtle::ConditionallySwappable;
|
|
|
|
|
|
use subtle::Equal;
|
|
|
|
|
|
use subtle::Mask;
|
2017-08-03 05:58:15 +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.
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
|
|
pub struct CompressedMontgomeryU(pub [u8; 32]);
|
|
|
|
|
|
|
|
|
|
|
|
impl CompressedMontgomeryU {
|
|
|
|
|
|
/// View this `CompressedMontgomeryU` as an array of bytes.
|
2017-09-07 20:31:06 +00:00
|
|
|
|
pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] {
|
|
|
|
|
|
&self.0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Convert this `CompressedMontgomeryU` to an array of bytes.
|
2017-08-03 05:58:15 +00:00
|
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
|
|
|
|
|
self.0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Attempt to decompress to an `ExtendedPoint`.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Note
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Since there are two curve points with the same
|
|
|
|
|
|
/// `u`-coordinate, the `u`-coordinate does not fully specify a
|
|
|
|
|
|
/// point. That is, roundtripping between an `ExtendedPoint` and
|
|
|
|
|
|
/// a `CompressedMontgomeryU` discards its sign bit.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Warning
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This function is *not* constant time.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Return
|
|
|
|
|
|
///
|
|
|
|
|
|
/// An `Option<ExtendedPoint>`, which will be `None` if either condition holds:
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `u = -1`, or
|
|
|
|
|
|
/// * `v` is not square.
|
|
|
|
|
|
//
|
|
|
|
|
|
// XXX any other exceptional points for the birational map?
|
2017-09-07 20:31:06 +00:00
|
|
|
|
pub fn decompress_edwards(&self) -> Option<ExtendedPoint> {
|
2017-08-03 05:58:15 +00:00
|
|
|
|
let u: FieldElement = FieldElement::from_bytes(&self.0);
|
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
//
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let y: FieldElement = CompressedMontgomeryU::to_edwards_y(&u); // y = (u-1)/(u+1)
|
|
|
|
|
|
|
|
|
|
|
|
// XXX this does two inversions: the above + one in .decompress()
|
|
|
|
|
|
// is it possible to do one?
|
|
|
|
|
|
CompressedEdwardsY(y.to_bytes()).decompress()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-07 20:31:06 +00:00
|
|
|
|
/// Decompress this `CompressedMontgomeryU` to a `MontgomeryPoint`.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Going from affine to projective coordinates, we have:
|
|
|
|
|
|
///
|
|
|
|
|
|
/// u → U/W
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// A projective `MontgomeryPoint` corresponding to this compressed point.
|
2017-09-14 01:54:28 +00:00
|
|
|
|
pub fn decompress(&self) -> MontgomeryPoint {
|
2017-09-07 20:31:06 +00:00
|
|
|
|
MontgomeryPoint{
|
|
|
|
|
|
U: FieldElement::from_bytes(&self.0),
|
|
|
|
|
|
W: FieldElement::one(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-03 05:58:15 +00:00
|
|
|
|
/// Given a Montgomery `u` coordinate, compute an Edwards `y` via
|
|
|
|
|
|
/// `y = (u-1)/(u+1)`.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Return
|
|
|
|
|
|
///
|
|
|
|
|
|
/// A `FieldElement` corresponding to this coordinate, but in Edwards form.
|
|
|
|
|
|
pub fn to_edwards_y(u: &FieldElement) -> FieldElement {
|
|
|
|
|
|
// Since `u = (1+y)/(1-y)` and `v = √(u(u²+Au+1))`, so `y = (u-1)/(u+1)`.
|
|
|
|
|
|
&(u - &FieldElement::one()) * &(u + &FieldElement::one()).invert()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Given a Montgomery `u` coordinate, compute the corresponding
|
|
|
|
|
|
/// Montgomery `v` coordinate by computing the right-hand side of
|
|
|
|
|
|
/// the Montgomery field equation, `v² = u(u² + Au +1)`.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Return
|
|
|
|
|
|
///
|
|
|
|
|
|
/// A tuple of (`u8`, `FieldElement`), where the `u8` is `1` if the v² was
|
|
|
|
|
|
/// actually a square and `0` if otherwise, along with a `FieldElement`: the
|
|
|
|
|
|
/// Montgomery `v` corresponding to this `u`.
|
|
|
|
|
|
pub fn to_montgomery_v(u: &FieldElement) -> (u8, FieldElement) {
|
2017-10-31 00:13:25 +00:00
|
|
|
|
let A = &constants::MONTGOMERY_A;
|
2017-08-03 05:58:15 +00:00
|
|
|
|
let one: FieldElement = FieldElement::one();
|
2017-10-31 00:13:25 +00:00
|
|
|
|
let v_squared: FieldElement = u * &(&u.square() + &(&(A * u) + &one));
|
2017-08-03 05:58:15 +00:00
|
|
|
|
|
|
|
|
|
|
let (okay, v_inv) = v_squared.invsqrt();
|
|
|
|
|
|
let v = &v_inv * &v_squared;
|
|
|
|
|
|
|
|
|
|
|
|
(okay, v)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Given Montgomery coordinates `(u, v)`, recover the Edwards `x` coordinate.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Inputs
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `u` and `v` are both `&FieldElement`s, corresponding the the `(u, v)`
|
|
|
|
|
|
/// coordinates of this `CompressedMontgomeryU`.
|
|
|
|
|
|
/// * `sign` is an &u8.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## Explanation of choice of `sign`
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ### Original Signal behaviour:
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `1u8` will leave `x` negative if it is negative, and will negate
|
|
|
|
|
|
/// `x` if it is positive, and
|
|
|
|
|
|
/// - `0u8` will leave `x` positive if it is positive, and will negate
|
|
|
|
|
|
/// `x` if it is negative.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Hence, if `sign` is `1u8`, the returned `x` will be negative.
|
|
|
|
|
|
/// Otherwise, if `sign` is `0u8`, the returned `x` will be positive.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Return
|
|
|
|
|
|
///
|
|
|
|
|
|
/// A `FieldElement`, the Edwards `x` coordinate, by using `(u, v)` to
|
|
|
|
|
|
/// convert from Montgomery to Edwards form via the right-hand side of the
|
|
|
|
|
|
/// equation: `x=(u/v)*sqrt(-A-2)`.
|
|
|
|
|
|
pub fn to_edwards_x(u: &FieldElement, v: &FieldElement, sign: &u8) -> FieldElement {
|
|
|
|
|
|
let mut x: FieldElement = &(u * &v.invert()) * &constants::SQRT_MINUS_APLUS2;
|
|
|
|
|
|
let neg_x: FieldElement = -(&x);
|
2017-10-03 00:08:00 +00:00
|
|
|
|
let current_sign: u8 = x.is_negative();
|
2017-08-03 05:58:15 +00:00
|
|
|
|
|
|
|
|
|
|
// Negate x to match the sign:
|
|
|
|
|
|
x.conditional_assign(&neg_x, current_sign ^ sign);
|
|
|
|
|
|
x
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-07 20:31:06 +00:00
|
|
|
|
/// A point on the Montgomery form of the curve, in projective 𝗣^2 coordinates.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// The transition between affine and projective is given by
|
|
|
|
|
|
///
|
|
|
|
|
|
/// u → U/W
|
|
|
|
|
|
/// v → V/W
|
|
|
|
|
|
///
|
|
|
|
|
|
/// thus the Montgomery curve equation
|
|
|
|
|
|
///
|
|
|
|
|
|
/// E_(A,B) : Bv² = u(u² + Au + 1)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// becomes
|
|
|
|
|
|
///
|
|
|
|
|
|
/// E_(A,B) : BV²W = U(U² + AUW + W²) ⊆ 𝗣^2
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 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
|
2017-10-04 04:43:04 +00:00
|
|
|
|
/// v-coordinate is superfluous for the purposes of scalar multiplication, we merely
|
2017-09-07 20:31:06 +00:00
|
|
|
|
/// use `(U:W)`.
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
|
|
#[allow(missing_docs)]
|
|
|
|
|
|
pub struct MontgomeryPoint{
|
|
|
|
|
|
pub U: FieldElement,
|
|
|
|
|
|
pub W: FieldElement,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The identity point is a unique point (the only where `W = 0`) on the curve.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// In projective coordinates, the quotient map `x : E (A,B) → E/<⦵> = 𝗣¹` is
|
|
|
|
|
|
///
|
2017-10-04 04:43:04 +00:00
|
|
|
|
/// ⎧ (x_P:1) if P = (x_P:y_P:1) ,
|
|
|
|
|
|
/// x : P ↦ ⎨
|
|
|
|
|
|
/// ⎩ (1:0) if P = O = (0:1:0) .
|
2017-09-07 20:31:06 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// We emphasize that the formula `x((U: V : W)) = (U : W)` only holds on the
|
|
|
|
|
|
/// open subset of `E_(A,B)` where `W ≠ 0`; it does not extend to the point
|
|
|
|
|
|
/// `O = (0:1:0)` at infinity, because `(0:0)` is not a projective point.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// The (exceptional) point at infinity in the Montgomery model.
|
|
|
|
|
|
impl Identity for MontgomeryPoint {
|
|
|
|
|
|
fn identity() -> MontgomeryPoint {
|
|
|
|
|
|
MontgomeryPoint {
|
|
|
|
|
|
U: FieldElement::one(),
|
|
|
|
|
|
W: FieldElement::zero(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Determine if two `MontgomeryPoint`s are equal, in constant time.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Note
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Because a compressed point on the Montgomery form of the curve doesn't
|
|
|
|
|
|
/// include the sign bit, there's two points here (if translated from the
|
|
|
|
|
|
/// Edwards form) which will equate.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// `1` if the points are equal, and `0` otherwise.
|
|
|
|
|
|
impl Equal for MontgomeryPoint {
|
|
|
|
|
|
fn ct_eq(&self, that: &MontgomeryPoint) -> u8 {
|
2017-10-05 01:32:44 +00:00
|
|
|
|
// (U_P:W_P) = (U_Q:W_Q) iff U_P * W_Q == U_Q * W_P,
|
|
|
|
|
|
// since U_P/W_P == U_Q/W_Q.
|
|
|
|
|
|
(&self.U * &that.W).ct_eq(&(&self.W * &that.U))
|
2017-09-07 20:31:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Determine if this `MontgomeryPoint` is valid.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Note
|
|
|
|
|
|
///
|
2017-10-05 01:46:42 +00:00
|
|
|
|
/// All projective points, except for `(X:W) = (0:0)`, are valid, since the
|
|
|
|
|
|
/// projective model is linear through the origin and is comprised by all `X` in
|
|
|
|
|
|
/// ℤ/(2²⁵⁵-19), thus `(0:0)` is the only element in Fₚ² which is not a
|
|
|
|
|
|
/// projective point.
|
2017-09-07 20:31:06 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// `true` if it is valid, and `false` otherwise.
|
|
|
|
|
|
impl ValidityCheck for MontgomeryPoint {
|
|
|
|
|
|
fn is_valid(&self) -> bool {
|
|
|
|
|
|
let zero = FieldElement::zero();
|
|
|
|
|
|
|
|
|
|
|
|
if (self.U.ct_eq(&zero) & self.W.ct_eq(&zero)) == 1 {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Conditionally assign another `MontgomeryPoint` to this point, in constant time.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// If `choice == 1`, assign `that` to `self`. Otherwise, leave `self`
|
|
|
|
|
|
/// unchanged.
|
|
|
|
|
|
impl ConditionallyAssignable for MontgomeryPoint {
|
|
|
|
|
|
fn conditional_assign(&mut self, that: &MontgomeryPoint, choice: Mask) {
|
|
|
|
|
|
self.U.conditional_assign(&that.U, choice);
|
|
|
|
|
|
self.W.conditional_assign(&that.W, choice);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl MontgomeryPoint {
|
|
|
|
|
|
/// Compress this point to only its u-coordinate (note: affine).
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// A `CompressedMontgomeryU`.
|
2017-09-14 01:54:28 +00:00
|
|
|
|
pub fn compress(&self) -> CompressedMontgomeryU {
|
2017-09-07 20:31:06 +00:00
|
|
|
|
let u_affine: FieldElement = &self.U * &self.W.invert();
|
|
|
|
|
|
|
|
|
|
|
|
CompressedMontgomeryU(u_affine.to_bytes())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Differential addition for single-coordinate Montgomery points.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Montgomery coordinates in projective 𝗣¹ space are odd in that 𝗣¹
|
|
|
|
|
|
/// inherits none of the group structure from E_(A,B). Hence, the mapping
|
|
|
|
|
|
/// of the group operation, `⊕`, is undefined for the pair `(x(P), x(Q))`;
|
|
|
|
|
|
/// that is, given `x(P)` and `x(Q)`, we cannot derive `x(P ⊕ Q)`. This is
|
|
|
|
|
|
/// due to the fact that, in Montgomery coordinates, `x(P)` determines `P`
|
|
|
|
|
|
/// only up to a sign, and thus we cannot differentiate `x(P ⊕ Q)` from
|
|
|
|
|
|
/// `x(P ⊖ Q)`. However, via differential addition, any three of the values
|
|
|
|
|
|
/// `{x(P), x(Q), x(P ⊕ Q), x(P ⊖ Q)}` determines the forth, so we can
|
|
|
|
|
|
/// define *pseudo-addition* for a singular coordinate.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Warning
|
|
|
|
|
|
///
|
|
|
|
|
|
/// If the `difference` is the identity point, or a two torsion point, the
|
|
|
|
|
|
/// results of this method are not correct, but instead result in `(0:0)`
|
|
|
|
|
|
/// (an invalid projective point in the Montgomery model).
|
|
|
|
|
|
///
|
2017-10-05 01:54:26 +00:00
|
|
|
|
/// The doubling case is degenerate, in that `P ⦵ Q ∉ {O,T}`, where `T` is
|
|
|
|
|
|
/// the two torsion point.
|
2017-09-07 20:31:06 +00:00
|
|
|
|
fn differential_add(&self, that: &MontgomeryPoint,
|
|
|
|
|
|
difference: &MontgomeryPoint) -> MontgomeryPoint {
|
2017-10-04 04:43:04 +00:00
|
|
|
|
// XXX Do we want these debug assertions? We would need to implement
|
|
|
|
|
|
// XXX is_two_torsion_point(). —isis
|
2017-09-07 20:31:06 +00:00
|
|
|
|
// debug_assert!(!difference.is_identity()); // P ⦵ Q ∉ {O,T}
|
|
|
|
|
|
// debug_assert!(!difference.is_two_torsion_point());
|
|
|
|
|
|
|
|
|
|
|
|
let v1: FieldElement = &(&self.U + &self.W) * &(&that.U - &that.W);
|
|
|
|
|
|
let v2: FieldElement = &(&self.U - &self.W) * &(&that.U + &that.W);
|
|
|
|
|
|
|
|
|
|
|
|
MontgomeryPoint {
|
|
|
|
|
|
U: &difference.W * &(&v1 + &v2).square(), // does reduction on square()
|
|
|
|
|
|
W: &difference.U * &(&v1 - &v2).square(), // does reduction on square()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-04 04:43:04 +00:00
|
|
|
|
/// Pseudo-doubling for single-coordinate Montgomery points.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Given a Montgomery U-coordinate of a point `P`, compute the
|
|
|
|
|
|
/// U-coordinate given by
|
2017-09-07 20:31:06 +00:00
|
|
|
|
///
|
2017-10-04 04:43:04 +00:00
|
|
|
|
/// differential_double: x(P) ⟼ x([2]P)
|
2017-09-07 20:31:06 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
2017-10-04 04:43:04 +00:00
|
|
|
|
/// A Montgomery point equal to doubling this one.
|
|
|
|
|
|
///
|
|
|
|
|
|
// XXX It seems possible that combining the differential_add() and
|
|
|
|
|
|
// XXX differential_double() methods would save a non-trivial amount of
|
|
|
|
|
|
// XXX computation in the ladder. —isis
|
2017-09-07 20:31:06 +00:00
|
|
|
|
fn differential_double(&self) -> MontgomeryPoint {
|
|
|
|
|
|
let mut v1: FieldElement;
|
|
|
|
|
|
let v2: FieldElement;
|
|
|
|
|
|
let v3: FieldElement;
|
|
|
|
|
|
|
|
|
|
|
|
v1 = (&self.U + &self.W).square();
|
|
|
|
|
|
v2 = (&self.U - &self.W).square();
|
|
|
|
|
|
|
|
|
|
|
|
let U: FieldElement = &v1 * &v2;
|
|
|
|
|
|
|
|
|
|
|
|
v1 -= &v2;
|
|
|
|
|
|
v3 = &(&constants::APLUS2_OVER_FOUR * &v1) + &v2;
|
|
|
|
|
|
|
|
|
|
|
|
let W: FieldElement = &v1 * &v3;
|
|
|
|
|
|
|
|
|
|
|
|
MontgomeryPoint{ U: U, W: W }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Multiply this `MontgomeryPoint` by a `Scalar`.
|
|
|
|
|
|
///
|
2017-10-04 04:43:04 +00:00
|
|
|
|
/// The reader is refered to §5.3 of ["Montgomery Curves and Their Arithmetic"
|
|
|
|
|
|
/// by Craig Costello and Benjamin Smith](https://eprint.iacr.org/2017/212.pdf)
|
|
|
|
|
|
/// for an overview of side-channel-free Montgomery laddering algorithms.
|
2017-09-07 20:31:06 +00:00
|
|
|
|
impl<'a, 'b> Mul<&'b Scalar> for &'a MontgomeryPoint {
|
|
|
|
|
|
type Output = MontgomeryPoint;
|
|
|
|
|
|
|
|
|
|
|
|
fn mul(self, scalar: &'b Scalar) -> MontgomeryPoint {
|
|
|
|
|
|
let mut x0: MontgomeryPoint = MontgomeryPoint::identity();
|
|
|
|
|
|
let mut x1: MontgomeryPoint = *self;
|
|
|
|
|
|
|
|
|
|
|
|
let bits: [i8; 256] = scalar.bits();
|
|
|
|
|
|
|
|
|
|
|
|
for i in (0..255).rev() {
|
|
|
|
|
|
let mask: u8 = (bits[i+1] ^ bits[i]) as u8;
|
|
|
|
|
|
|
|
|
|
|
|
debug_assert!(mask == 0 || mask == 1);
|
|
|
|
|
|
|
|
|
|
|
|
x0.conditional_swap(&mut x1, mask);
|
|
|
|
|
|
x1 = x0.differential_add(&x1, &self);
|
|
|
|
|
|
x0 = x0.differential_double();
|
|
|
|
|
|
}
|
|
|
|
|
|
x0.conditional_swap(&mut x1, bits[0] as u8);
|
|
|
|
|
|
x0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'b> MulAssign<&'b Scalar> for MontgomeryPoint {
|
|
|
|
|
|
fn mul_assign(&mut self, scalar: &'b Scalar) {
|
|
|
|
|
|
let result = (self as &MontgomeryPoint) * scalar;
|
|
|
|
|
|
*self = result;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {
|
|
|
|
|
|
type Output = MontgomeryPoint;
|
|
|
|
|
|
|
|
|
|
|
|
fn mul(self, point: &'b MontgomeryPoint) -> MontgomeryPoint {
|
|
|
|
|
|
point * &self
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-03 05:58:15 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Tests
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod test {
|
2017-09-07 20:31:06 +00:00
|
|
|
|
use constants::BASE_COMPRESSED_MONTGOMERY;
|
2017-11-17 01:34:28 +00:00
|
|
|
|
use traits::Identity;
|
2017-08-03 05:58:15 +00:00
|
|
|
|
use super::*;
|
|
|
|
|
|
|
2017-09-07 20:31:06 +00:00
|
|
|
|
use rand::OsRng;
|
2017-08-03 05:58:15 +00:00
|
|
|
|
|
|
|
|
|
|
/// Test Montgomery conversion against the X25519 basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn basepoint_to_montgomery() {
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(constants::ED25519_BASEPOINT_POINT.to_montgomery().compress(),
|
2017-09-07 20:31:06 +00:00
|
|
|
|
BASE_COMPRESSED_MONTGOMERY);
|
2017-08-03 05:58:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Test Montgomery conversion against the X25519 basepoint.
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn basepoint_from_montgomery() {
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(BASE_COMPRESSED_MONTGOMERY,
|
|
|
|
|
|
constants::BASE_CMPRSSD.decompress().unwrap().to_montgomery().compress());
|
2017-08-03 05:58:15 +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 u_minus_one_monty() {
|
|
|
|
|
|
let minus_one = FieldElement::minus_one();
|
|
|
|
|
|
let minus_one_bytes = minus_one.to_bytes();
|
|
|
|
|
|
let div_by_zero_u = CompressedMontgomeryU(minus_one_bytes);
|
2017-09-07 20:31:06 +00:00
|
|
|
|
assert!(div_by_zero_u.decompress_edwards().is_none());
|
2017-08-03 05:58:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-14 01:54:28 +00:00
|
|
|
|
/// Montgomery compression of the identity point should not fail (since the
|
|
|
|
|
|
/// mapping in `ProjectivePoint.to_montgomery()` should be valid for the
|
|
|
|
|
|
/// identity.
|
2017-08-03 05:58:15 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn identity_to_monty() {
|
|
|
|
|
|
let id = ExtendedPoint::identity();
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(id.to_montgomery().compress(), MontgomeryPoint::identity().compress());
|
2017-08-03 05:58:15 +00:00
|
|
|
|
}
|
2017-09-07 20:31:06 +00:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn projective_to_affine_roundtrips() {
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(BASE_COMPRESSED_MONTGOMERY.decompress().compress(),
|
|
|
|
|
|
BASE_COMPRESSED_MONTGOMERY);
|
2017-09-07 20:31:06 +00:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn differential_double_matches_double() {
|
|
|
|
|
|
let p: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.double();
|
2017-09-14 01:54:28 +00:00
|
|
|
|
let q: MontgomeryPoint = BASE_COMPRESSED_MONTGOMERY.decompress().differential_double();
|
2017-09-07 20:31:06 +00:00
|
|
|
|
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(p.to_montgomery().compress(), q.compress());
|
2017-09-07 20:31:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-05 01:26:15 +00:00
|
|
|
|
#[test]
|
2017-11-16 19:14:53 +00:00
|
|
|
|
#[cfg(feature="precomputed_tables")]
|
2017-10-05 01:26:15 +00:00
|
|
|
|
fn montgomery_ct_eq_ne() {
|
|
|
|
|
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
|
|
|
|
|
let s1: Scalar = Scalar::random(&mut csprng);
|
|
|
|
|
|
let s2: Scalar = Scalar::random(&mut csprng);
|
2017-11-16 19:14:53 +00:00
|
|
|
|
let p1: MontgomeryPoint = (&s1 * &constants::ED25519_BASEPOINT_TABLE).to_montgomery();
|
|
|
|
|
|
let p2: MontgomeryPoint = (&s2 * &constants::ED25519_BASEPOINT_TABLE).to_montgomery();
|
2017-10-05 01:26:15 +00:00
|
|
|
|
|
|
|
|
|
|
assert_eq!(p1.ct_eq(&p2), 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
2017-11-16 19:14:53 +00:00
|
|
|
|
#[cfg(feature="precomputed_tables")]
|
2017-10-05 01:26:15 +00:00
|
|
|
|
fn montgomery_ct_eq_eq() {
|
|
|
|
|
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
|
|
|
|
|
let s1: Scalar = Scalar::random(&mut csprng);
|
2017-11-16 19:14:53 +00:00
|
|
|
|
let p1: MontgomeryPoint = (&s1 * &constants::ED25519_BASEPOINT_TABLE).to_montgomery();
|
2017-10-05 01:26:15 +00:00
|
|
|
|
|
|
|
|
|
|
assert_eq!(p1.ct_eq(&p1), 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-07 20:31:06 +00:00
|
|
|
|
#[test]
|
2017-11-16 19:14:53 +00:00
|
|
|
|
#[cfg(feature="precomputed_tables")]
|
2017-09-07 20:31:06 +00:00
|
|
|
|
fn differential_add_matches_edwards_model() {
|
|
|
|
|
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let s1: Scalar = Scalar::random(&mut csprng);
|
|
|
|
|
|
let s2: Scalar = Scalar::random(&mut csprng);
|
|
|
|
|
|
let p1: ExtendedPoint = &constants::ED25519_BASEPOINT_TABLE * &s1;
|
|
|
|
|
|
let p2: ExtendedPoint = &constants::ED25519_BASEPOINT_TABLE * &s2;
|
|
|
|
|
|
let diff: ExtendedPoint = &p1 - &p2;
|
|
|
|
|
|
|
2017-09-14 01:54:28 +00:00
|
|
|
|
let p1m: MontgomeryPoint = p1.to_montgomery();
|
|
|
|
|
|
let p2m: MontgomeryPoint = p2.to_montgomery();
|
|
|
|
|
|
let diffm: MontgomeryPoint = diff.to_montgomery();
|
2017-09-07 20:31:06 +00:00
|
|
|
|
|
|
|
|
|
|
let result = p1m.differential_add(&p2m, &diffm);
|
|
|
|
|
|
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(result.compress(), (&p1 + &p2).to_montgomery().compress());
|
2017-09-07 20:31:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
2017-11-16 19:14:53 +00:00
|
|
|
|
#[cfg(feature="precomputed_tables")]
|
2017-09-07 20:31:06 +00:00
|
|
|
|
fn ladder_matches_scalarmult() {
|
|
|
|
|
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let s: Scalar = Scalar::random(&mut csprng);
|
|
|
|
|
|
let p_edwards: ExtendedPoint = &constants::ED25519_BASEPOINT_TABLE * &s;
|
2017-09-14 01:54:28 +00:00
|
|
|
|
let p_montgomery: MontgomeryPoint = p_edwards.to_montgomery();
|
2017-09-07 20:31:06 +00:00
|
|
|
|
|
|
|
|
|
|
let expected = &s * &p_edwards;
|
|
|
|
|
|
let result = &s * &p_montgomery;
|
|
|
|
|
|
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(result.compress(), expected.to_montgomery().compress())
|
2017-09-07 20:31:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn ladder_basepoint_times_two_matches_double() {
|
|
|
|
|
|
let two: Scalar = Scalar::from_u64(2u64);
|
2017-09-14 01:54:28 +00:00
|
|
|
|
let result: MontgomeryPoint = &BASE_COMPRESSED_MONTGOMERY.decompress() * &two;
|
|
|
|
|
|
let expected: ExtendedPoint = constants::ED25519_BASEPOINT_POINT.double();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(result.compress(), expected.to_montgomery().compress());
|
2017-09-14 02:08:11 +00:00
|
|
|
|
}
|
2017-10-05 02:15:28 +00:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
#[should_panic(expected = "assertion failed: self[31] <= 127")]
|
2017-11-16 19:14:53 +00:00
|
|
|
|
#[cfg(feature="precomputed_tables")]
|
2017-10-05 02:15:28 +00:00
|
|
|
|
fn ladder_matches_scalarmult_with_scalar_high_bit_set() {
|
|
|
|
|
|
let mut s: Scalar = Scalar::one();
|
|
|
|
|
|
|
|
|
|
|
|
s[31] = 255;
|
|
|
|
|
|
|
|
|
|
|
|
let result: MontgomeryPoint = &BASE_COMPRESSED_MONTGOMERY.decompress() * &s;
|
|
|
|
|
|
let expected: ExtendedPoint = &constants::ED25519_BASEPOINT_TABLE * &s;
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(result.compress(), expected.to_montgomery().compress())
|
|
|
|
|
|
}
|
2017-09-14 02:08:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(all(test, feature = "bench"))]
|
2017-11-16 19:14:53 +00:00
|
|
|
|
#[cfg(feature="precomputed_tables")]
|
2017-09-14 02:08:11 +00:00
|
|
|
|
mod bench {
|
|
|
|
|
|
use rand::OsRng;
|
|
|
|
|
|
use constants::ED25519_BASEPOINT_TABLE;
|
|
|
|
|
|
use constants::BASE_COMPRESSED_MONTGOMERY;
|
|
|
|
|
|
use test::Bencher;
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
2017-10-05 01:26:15 +00:00
|
|
|
|
#[bench]
|
|
|
|
|
|
fn montgomery_ct_eq(b: &mut Bencher) {
|
|
|
|
|
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
|
|
|
|
|
let s1: Scalar = Scalar::random(&mut csprng);
|
|
|
|
|
|
let s2: Scalar = Scalar::random(&mut csprng);
|
|
|
|
|
|
let p1: MontgomeryPoint = (&s1 * &ED25519_BASEPOINT_TABLE).to_montgomery();
|
|
|
|
|
|
let p2: MontgomeryPoint = (&s2 * &ED25519_BASEPOINT_TABLE).to_montgomery();
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | p1.ct_eq(&p2))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-14 02:08:11 +00:00
|
|
|
|
#[bench]
|
|
|
|
|
|
fn montgomery_decompress(b: &mut Bencher) {
|
|
|
|
|
|
b.iter(| | BASE_COMPRESSED_MONTGOMERY.decompress());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn montgomery_compress(b: &mut Bencher) {
|
|
|
|
|
|
let p: MontgomeryPoint = BASE_COMPRESSED_MONTGOMERY.decompress();
|
|
|
|
|
|
|
|
|
|
|
|
b.iter(| | p.compress());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
|
|
fn montgomery_ladder(b: &mut Bencher) {
|
|
|
|
|
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
|
|
|
|
|
let s: Scalar = Scalar::random(&mut csprng);
|
|
|
|
|
|
let p: MontgomeryPoint = (&Scalar::random(&mut csprng) * &ED25519_BASEPOINT_TABLE).to_montgomery();
|
2017-09-07 20:31:06 +00:00
|
|
|
|
|
2017-09-14 02:08:11 +00:00
|
|
|
|
b.iter(| | &s * &p);
|
2017-09-07 20:31:06 +00:00
|
|
|
|
}
|
2017-08-03 05:58:15 +00:00
|
|
|
|
}
|