use core::ops::{
Mul,
MulAssign,
};
use subtle::Choice;
use subtle::ConditionallySelectable;
use subtle::ConstantTimeEq;
use zeroize::Zeroize;
use crate::{
edwards::{CompressedY as CompressedEdwardsY, EdwardsPoint},
field::{FieldElement, FieldImplementation as _},
scalar::Scalar,
Error, Result,
};
#[derive(Clone, Copy, Debug, Default, Zeroize)]
pub struct MontgomeryPoint(pub FieldElement);
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).unwrap_u8() == 1u8
}
}
impl Eq for MontgomeryPoint {}
impl MontgomeryPoint {
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
pub fn to_edwards(&self, sign: u8) -> Result<EdwardsPoint> {
let u = &self.0;
let one = &FieldElement::ONE;
if u + one == FieldElement::ZERO {
return Err(Error::WrongTwist);
}
let y = &(u - one) * &(u + one).inverse();
let mut y_bytes = y.to_bytes();
y_bytes[31] ^= sign << 7;
CompressedEdwardsY(y_bytes).decompressed()
}
pub fn basepoint() -> Self {
Self(FieldElement::MONTGOMERY_BASEPOINT_U)
}
}
#[derive(Copy, Clone, Debug)]
#[allow(non_snake_case)]
struct ProjectivePoint {
pub U: FieldElement,
pub W: FieldElement,
}
impl ProjectivePoint {
fn neutral_element() -> ProjectivePoint {
ProjectivePoint {
U: FieldElement::ONE,
W: FieldElement::ZERO,
}
}
}
impl Default for ProjectivePoint {
fn default() -> ProjectivePoint {
ProjectivePoint::neutral_element()
}
}
impl ConditionallySelectable for ProjectivePoint {
fn conditional_select(
a: &ProjectivePoint,
b: &ProjectivePoint,
choice: Choice,
) -> ProjectivePoint {
ProjectivePoint {
U: FieldElement::conditional_select(&a.U, &b.U, choice),
W: FieldElement::conditional_select(&a.W, &b.W, choice),
}
}
}
impl ProjectivePoint {
pub fn to_affine(self) -> MontgomeryPoint {
let u = &self.U * &self.W.inverse();
MontgomeryPoint(u)
}
}
#[allow(non_snake_case)]
fn differential_add_and_double(
P: &mut ProjectivePoint,
Q: &mut ProjectivePoint,
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.squared(); let t5 = t1.squared();
let t6 = &t4 - &t5;
let t7 = &t0 * &t3; let t8 = &t1 * &t2;
let t9 = &t7 + &t8; let t10 = &t7 - &t8;
let t11 = t9.squared(); let t12 = t10.squared();
let t13 = &FieldElement::APLUS2_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<'a, 'b> Mul<&'b Scalar> for &'a MontgomeryPoint {
type Output = MontgomeryPoint;
fn mul(self, scalar: &'b Scalar) -> MontgomeryPoint {
let affine_u = self.0;
let mut x0 = ProjectivePoint::neutral_element();
let mut x1 = ProjectivePoint {
U: affine_u,
W: FieldElement::ONE,
};
let bits: [i8; 256] = scalar.bits();
for i in (0..255).rev() {
let choice: u8 = (bits[i + 1] ^ bits[i]) as u8;
debug_assert!(choice == 0 || choice == 1);
ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
differential_add_and_double(&mut x0, &mut x1, &affine_u);
}
ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(bits[0] as u8));
x0.to_affine()
}
}
impl<'b> MulAssign<&'b Scalar> for MontgomeryPoint {
fn mul_assign(&mut self, scalar: &'b Scalar) {
*self = (self as &MontgomeryPoint) * scalar;
}
}
impl<'a, 'b> Mul<&'b MontgomeryPoint> for &'a Scalar {
type Output = MontgomeryPoint;
fn mul(self, point: &'b MontgomeryPoint) -> MontgomeryPoint {
point * self
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn to_edwards() {
let edwards_basepoint = crate::edwards::EdwardsPoint::basepoint();
let montgomery_basepoint = MontgomeryPoint::basepoint();
assert_eq!(
edwards_basepoint,
montgomery_basepoint.to_edwards(0).unwrap()
);
let scalar = Scalar::from(123456);
assert_eq!(
&scalar * &edwards_basepoint,
(&scalar * &montgomery_basepoint).to_edwards(1).unwrap()
);
}
}