1use core::fmt;
32use fp::field_ops::{FieldOps, FieldRandom};
33
34use crate::curve_ops::Curve;
35use crate::point_edwards::EdwardsPoint;
36
37#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct EdwardsCurve<F: FieldOps> {
43 pub d1: F,
45 pub d2: F,
47}
48
49impl<F> fmt::Display for EdwardsCurve<F>
50where
51 F: FieldOps + fmt::Display,
52{
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 if F::characteristic()[0] != 2 {
55 if f.alternate() {
56 write!(
57 f,
58 "EdwardsCurve {{\n x^2 + y^2 = 1 + d x^2 y^2\n d = {}\n}}",
59 self.d2
60 )
61 } else {
62 write!(f, "x^2 + y^2 = 1 + ({})x^2y^2", self.d2)
63 }
64 } else {
65 if f.alternate() {
66 write!(
67 f,
68 "EdwardsCurve {{\n d1(x+y) + d2(x^2+y^2) = xy + xy(x+y) + x^2y^2\n d1 = {}\n d2 = {}\n}}",
69 self.d1, self.d2
70 )
71 } else {
72 write!(
73 f,
74 "{}(x+y) + {}(x^2+y^2) = xy + xy(x+y) + x^2y^2",
75 self.d1, self.d2
76 )
77 }
78 }
79 }
80}
81
82impl<F: FieldOps + FieldRandom> EdwardsCurve<F> {
83 pub fn new(d: F) -> Self {
87 assert!(F::characteristic()[0] != 2, "use new_binary() for char 2");
88 assert!(d != F::zero(), "d must be nonzero");
89 assert!(d != F::one(), "d must not be 1");
90 Self {
91 d1: F::zero(),
92 d2: d,
93 }
94 }
95
96 pub fn new_binary(d1: F, d2: F) -> Self {
99 assert_eq!(F::characteristic()[0], 2, "use new() for odd char");
100 assert!(d1 != F::zero(), "d1 must be nonzero");
101 let d1_sq = <F as FieldOps>::square(&d1);
102 assert!(d2 != d1_sq + d1, "d2 must differ from d1² + d1");
103 Self { d1, d2 }
104 }
105
106 pub fn d(&self) -> F {
108 self.d2
109 }
110
111 pub fn contains(&self, x: &F, y: &F) -> bool {
113 if F::characteristic()[0] != 2 {
114 let x2 = <F as FieldOps>::square(x);
116 let y2 = <F as FieldOps>::square(y);
117 x2 + y2 == F::one() + self.d2 * x2 * y2
118 } else {
119 let x2 = <F as FieldOps>::square(x);
121 let y2 = <F as FieldOps>::square(y);
122 let xy = *x * *y;
123 let xpy = *x + *y;
124 self.d1 * xpy + self.d2 * (x2 + y2) == xy + xy * xpy + x2 * y2
125 }
126 }
127
128 pub fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> EdwardsPoint<F> {
134 assert!(
135 F::characteristic()[0] != 2,
136 "random_point currently implemented only for odd characteristic"
137 );
138
139 loop {
140 let x = F::random(rng);
141 let x2 = <F as FieldOps>::square(&x);
142 let denom = F::one() - self.d2 * x2;
143
144 if bool::from(denom.is_zero()) {
145 continue;
146 }
147
148 let rhs = (F::one() - x2) * denom.invert().unwrap();
149
150 if let Some(y) = rhs.sqrt().into_option() {
151 let p = EdwardsPoint::new(x, y);
152 debug_assert!(self.is_on_curve(&p));
153 return p;
154 }
155 }
156 }
157}
158
159impl<F: FieldOps + FieldRandom> Curve for EdwardsCurve<F> {
160 type BaseField = F;
161 type Point = EdwardsPoint<F>;
162
163 fn is_on_curve(&self, point: &Self::Point) -> bool {
164 self.contains(&point.x, &point.y)
165 }
166
167 fn random_point(&self, rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self::Point {
168 EdwardsCurve::random_point(self, rng)
169 }
170
171 fn j_invariant(&self) -> F {
172 if F::characteristic()[0] != 2 {
173 let d = self.d2;
175 let d2 = <F as FieldOps>::square(&d);
176 let two = <F as FieldOps>::double(&F::one());
177 let four = <F as FieldOps>::double(&two);
178 let eight = <F as FieldOps>::double(&four);
179 let fourteen = eight + four + two;
180 let sixteen = <F as FieldOps>::double(&eight);
181
182 let inner = F::one() + fourteen * d + d2;
183 let inner_cubed = inner * <F as FieldOps>::square(&inner);
184 let numer = sixteen * inner_cubed;
185
186 let one_minus_d = F::one() - d;
187 let omd2 = <F as FieldOps>::square(&one_minus_d);
188 let omd4 = <F as FieldOps>::square(&omd2);
189 let denom = d * omd4;
190
191 numer
192 * denom
193 .invert()
194 .into_option()
195 .expect("d(1-d)^4 must be invertible")
196 } else {
197 let d1_sq = <F as FieldOps>::square(&self.d1);
199 let d1_4 = <F as FieldOps>::square(&d1_sq);
200 let d2_sq = <F as FieldOps>::square(&self.d2);
201 let denom = d1_4 * (d1_4 + d1_sq + d2_sq);
202 denom
203 .invert()
204 .into_option()
205 .expect("j-invariant denominator must be invertible")
206 }
207 }
208
209 fn a_invariants(&self) -> Vec<Self::BaseField> {
210 if F::characteristic()[0] != 2 {
211 vec![self.d2]
212 } else {
213 vec![self.d1, self.d2]
214 }
215 }
216}