#![allow(non_snake_case)]
use crate::curve::edwards::extended::EdwardsPoint;
use crate::field::{FieldElement, Scalar};
use core::fmt;
use core::ops::Mul;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
const LOW_A: MontgomeryPoint = MontgomeryPoint([
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, 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,
]);
const LOW_B: MontgomeryPoint = MontgomeryPoint([
0x01, 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,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
const LOW_C: MontgomeryPoint = MontgomeryPoint([
0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
]);
#[derive(Copy, Clone)]
pub struct MontgomeryPoint(pub [u8; 56]);
impl Default for MontgomeryPoint {
fn default() -> MontgomeryPoint {
Self([0u8; 56])
}
}
#[cfg(feature = "zeroize")]
impl zeroize::DefaultIsZeroes for MontgomeryPoint {}
impl fmt::Debug for MontgomeryPoint {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.0[..].fmt(formatter)
}
}
impl ConstantTimeEq for MontgomeryPoint {
fn ct_eq(&self, other: &MontgomeryPoint) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl PartialEq for MontgomeryPoint {
fn eq(&self, other: &MontgomeryPoint) -> bool {
self.ct_eq(other).into()
}
}
impl Eq for MontgomeryPoint {}
#[derive(Copy, Clone, Debug)]
pub struct ProjectiveMontgomeryPoint {
U: FieldElement,
W: FieldElement,
}
impl Mul<&Scalar> for &MontgomeryPoint {
type Output = MontgomeryPoint;
#[allow(clippy::suspicious_arithmetic_impl)]
fn mul(self, scalar: &Scalar) -> MontgomeryPoint {
let affine_u = FieldElement::from_bytes(&self.0);
let mut x0 = ProjectiveMontgomeryPoint::identity();
let mut x1 = ProjectiveMontgomeryPoint {
U: affine_u,
W: FieldElement::ONE,
};
let bits = scalar.bits();
let mut swap = 0;
for s in (0..448).rev() {
let bit = bits[s] as u8;
let choice: u8 = swap ^ bit;
ProjectiveMontgomeryPoint::conditional_swap(&mut x0, &mut x1, Choice::from(choice));
differential_add_and_double(&mut x0, &mut x1, &affine_u);
swap = bit;
}
x0.to_affine()
}
}
impl Mul<&MontgomeryPoint> for &Scalar {
type Output = MontgomeryPoint;
fn mul(self, point: &MontgomeryPoint) -> MontgomeryPoint {
point * self
}
}
impl MontgomeryPoint {
pub const GENERATOR: Self = Self([
0x05, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
pub fn to_edwards(&self, _sign: u8) -> Option<EdwardsPoint> {
todo!()
}
pub fn is_low_order(&self) -> bool {
(*self == LOW_A) || (*self == LOW_B) || (*self == LOW_C)
}
pub fn as_bytes(&self) -> &[u8; 56] {
&self.0
}
pub fn to_projective(&self) -> ProjectiveMontgomeryPoint {
ProjectiveMontgomeryPoint {
U: FieldElement::from_bytes(&self.0),
W: FieldElement::ONE,
}
}
}
impl ConditionallySelectable for ProjectiveMontgomeryPoint {
fn conditional_select(
a: &ProjectiveMontgomeryPoint,
b: &ProjectiveMontgomeryPoint,
choice: Choice,
) -> ProjectiveMontgomeryPoint {
ProjectiveMontgomeryPoint {
U: FieldElement::conditional_select(&a.U, &b.U, choice),
W: FieldElement::conditional_select(&a.W, &b.W, choice),
}
}
}
fn differential_add_and_double(
P: &mut ProjectiveMontgomeryPoint,
Q: &mut ProjectiveMontgomeryPoint,
affine_PmQ: &FieldElement,
) {
let t0 = P.U + P.W;
let t1 = P.U - P.W;
let t2 = Q.U + Q.W;
let t3 = Q.U - Q.W;
let t4 = t0.square(); let t5 = t1.square();
let t6 = t4 - t5;
let t7 = t0 * t3; let t8 = t1 * t2;
let t9 = t7 + t8; let t10 = t7 - t8;
let t11 = t9.square(); let t12 = t10.square(); let t13 = FieldElement::A_PLUS_TWO_OVER_FOUR * t6;
let t14 = t4 * t5; let t15 = t13 + t5;
let t16 = t6 * t15; let t17 = *affine_PmQ * t12; let t18 = t11;
P.U = t14; P.W = t16; Q.U = t18; Q.W = t17; }
impl ProjectiveMontgomeryPoint {
pub fn identity() -> ProjectiveMontgomeryPoint {
ProjectiveMontgomeryPoint {
U: FieldElement::ONE,
W: FieldElement::ZERO,
}
}
pub fn to_affine(&self) -> MontgomeryPoint {
let x = self.U * self.W.invert();
MontgomeryPoint(x.to_bytes())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_montgomery_edwards() {
let scalar = Scalar::from(200u32);
use crate::GOLDILOCKS_BASE_POINT as bp;
let montgomery_bp = bp.to_montgomery();
let montgomery_res = &montgomery_bp * &scalar;
let goldilocks_point = bp.scalar_mul(&scalar);
assert_eq!(goldilocks_point.to_montgomery(), montgomery_res);
}
}