1#![allow(non_snake_case)]
12
13use crate::EdwardsScalar;
15use crate::edwards::extended::EdwardsPoint;
16use crate::field::FieldElement;
17use core::fmt;
18use core::ops::Mul;
19use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
20
21impl MontgomeryPoint {
22 pub const LOW_A: MontgomeryPoint = MontgomeryPoint([
24 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
28 ]);
29 pub const LOW_B: MontgomeryPoint = MontgomeryPoint([
31 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 ]);
36 pub const LOW_C: MontgomeryPoint = MontgomeryPoint([
38 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
39 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
40 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
41 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
42 ]);
43}
44
45#[derive(Copy, Clone)]
47pub struct MontgomeryPoint(pub [u8; 56]);
48
49impl Default for MontgomeryPoint {
50 fn default() -> MontgomeryPoint {
51 Self([0u8; 56])
52 }
53}
54
55impl elliptic_curve::zeroize::DefaultIsZeroes for MontgomeryPoint {}
56
57impl fmt::Debug for MontgomeryPoint {
58 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
59 self.0[..].fmt(formatter)
60 }
61}
62
63impl ConstantTimeEq for MontgomeryPoint {
64 fn ct_eq(&self, other: &MontgomeryPoint) -> Choice {
65 self.0.ct_eq(&other.0)
66 }
67}
68
69impl PartialEq for MontgomeryPoint {
70 fn eq(&self, other: &MontgomeryPoint) -> bool {
71 self.ct_eq(other).into()
72 }
73}
74impl Eq for MontgomeryPoint {}
75
76#[derive(Copy, Clone, Debug)]
78pub struct ProjectiveMontgomeryPoint {
79 U: FieldElement,
80 W: FieldElement,
81}
82
83impl Mul<&EdwardsScalar> for &MontgomeryPoint {
84 type Output = MontgomeryPoint;
85
86 #[allow(clippy::suspicious_arithmetic_impl)]
87 fn mul(self, scalar: &EdwardsScalar) -> MontgomeryPoint {
88 let affine_u = FieldElement::from_bytes(&self.0);
90 let mut x0 = ProjectiveMontgomeryPoint::identity();
91 let mut x1 = ProjectiveMontgomeryPoint {
92 U: affine_u,
93 W: FieldElement::ONE,
94 };
95
96 let bits = scalar.bits();
97 let mut swap = 0;
98 for s in (0..448).rev() {
99 let bit = bits[s] as u8;
100 let choice: u8 = swap ^ bit;
101
102 ProjectiveMontgomeryPoint::conditional_swap(&mut x0, &mut x1, Choice::from(choice));
103 differential_add_and_double(&mut x0, &mut x1, &affine_u);
104
105 swap = bit;
106 }
107
108 x0.to_affine()
109 }
110}
111
112impl Mul<&MontgomeryPoint> for &EdwardsScalar {
113 type Output = MontgomeryPoint;
114
115 fn mul(self, point: &MontgomeryPoint) -> MontgomeryPoint {
116 point * self
117 }
118}
119
120impl MontgomeryPoint {
121 pub const GENERATOR: Self = Self([
123 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 ]);
128
129 pub fn to_edwards(&self, _sign: u8) -> Option<EdwardsPoint> {
131 todo!()
134 }
135
136 pub fn is_low_order(&self) -> bool {
138 (*self == Self::LOW_A) || (*self == Self::LOW_B) || (*self == Self::LOW_C)
139 }
140
141 pub fn as_bytes(&self) -> &[u8; 56] {
143 &self.0
144 }
145
146 pub fn to_projective(&self) -> ProjectiveMontgomeryPoint {
148 ProjectiveMontgomeryPoint {
149 U: FieldElement::from_bytes(&self.0),
150 W: FieldElement::ONE,
151 }
152 }
153}
154
155impl ConditionallySelectable for ProjectiveMontgomeryPoint {
156 fn conditional_select(
157 a: &ProjectiveMontgomeryPoint,
158 b: &ProjectiveMontgomeryPoint,
159 choice: Choice,
160 ) -> ProjectiveMontgomeryPoint {
161 ProjectiveMontgomeryPoint {
162 U: FieldElement::conditional_select(&a.U, &b.U, choice),
163 W: FieldElement::conditional_select(&a.W, &b.W, choice),
164 }
165 }
166}
167
168fn differential_add_and_double(
169 P: &mut ProjectiveMontgomeryPoint,
170 Q: &mut ProjectiveMontgomeryPoint,
171 affine_PmQ: &FieldElement,
172) {
173 let t0 = P.U + P.W;
174 let t1 = P.U - P.W;
175 let t2 = Q.U + Q.W;
176 let t3 = Q.U - Q.W;
177
178 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; }
205
206impl ProjectiveMontgomeryPoint {
207 pub fn identity() -> ProjectiveMontgomeryPoint {
209 ProjectiveMontgomeryPoint {
210 U: FieldElement::ONE,
211 W: FieldElement::ZERO,
212 }
213 }
214
215 pub fn to_affine(&self) -> MontgomeryPoint {
217 let x = self.U * self.W.invert();
218 MontgomeryPoint(x.to_bytes())
219 }
220}
221
222#[cfg(test)]
223mod tests {
224
225 use super::*;
226
227 #[test]
228 fn test_montgomery_edwards() {
229 let scalar = EdwardsScalar::from(200u32);
230 use crate::GOLDILOCKS_BASE_POINT as bp;
231
232 let montgomery_bp = bp.to_montgomery();
234 let montgomery_res = &montgomery_bp * &scalar;
235
236 let goldilocks_point = bp.scalar_mul(&scalar);
238 assert_eq!(goldilocks_point.to_montgomery(), montgomery_res);
239 }
240}