11use crate :: field:: FieldElement ;
22use crate :: * ;
3+ use core:: fmt:: { Display , Formatter , LowerHex , Result as FmtResult , UpperHex } ;
34use core:: ops:: Mul ;
4- use elliptic_curve:: { Error , Result , point:: NonIdentity , zeroize:: DefaultIsZeroes } ;
5- use subtle:: { Choice , ConditionallySelectable , ConstantTimeEq } ;
5+ use elliptic_curve:: { Error , point:: NonIdentity , zeroize:: DefaultIsZeroes } ;
6+ use rand_core:: TryRngCore ;
7+ use subtle:: { Choice , ConditionallyNegatable , ConditionallySelectable , ConstantTimeEq , CtOption } ;
68
79/// Affine point on untwisted curve
810#[ derive( Copy , Clone , Debug ) ]
@@ -69,6 +71,21 @@ impl AffinePoint {
6971 y : FieldElement :: ONE ,
7072 } ;
7173
74+ /// Generate a random [`AffinePoint`].
75+ pub fn try_from_rng < R > ( rng : & mut R ) -> Result < Self , R :: Error >
76+ where
77+ R : TryRngCore + ?Sized ,
78+ {
79+ let mut bytes = CompressedEdwardsY :: default ( ) ;
80+
81+ loop {
82+ rng. try_fill_bytes ( & mut bytes. 0 ) ?;
83+ if let Some ( point) = bytes. decompress ( ) . into ( ) {
84+ return Ok ( point) ;
85+ }
86+ }
87+ }
88+
7289 pub ( crate ) fn isogeny ( & self ) -> Self {
7390 let x = self . x ;
7491 let y = self . y ;
@@ -100,6 +117,33 @@ impl AffinePoint {
100117 }
101118 }
102119
120+ /// Standard compression; store Y and sign of X
121+ pub fn compress ( & self ) -> CompressedEdwardsY {
122+ let affine_x = self . x ;
123+ let affine_y = self . y ;
124+
125+ let mut compressed_bytes = [ 0u8 ; 57 ] ;
126+
127+ let sign = affine_x. is_negative ( ) . unwrap_u8 ( ) ;
128+
129+ let y_bytes = affine_y. to_bytes ( ) ;
130+ compressed_bytes[ ..y_bytes. len ( ) ] . copy_from_slice ( & y_bytes[ ..] ) ;
131+ * compressed_bytes. last_mut ( ) . expect ( "at least one byte" ) = sign << 7 ;
132+ CompressedEdwardsY ( compressed_bytes)
133+ }
134+
135+ /// Check if this point is on the curve
136+ pub fn is_on_curve ( & self ) -> Choice {
137+ // X^2 + Y^2 == 1 + D * X^2 * Y^2
138+
139+ let XX = self . x . square ( ) ;
140+ let YY = self . y . square ( ) ;
141+ let lhs = YY + XX ;
142+ let rhs = FieldElement :: ONE + FieldElement :: EDWARDS_D * XX * YY ;
143+
144+ lhs. ct_eq ( & rhs)
145+ }
146+
103147 /// Convert to edwards extended point
104148 pub fn to_edwards ( & self ) -> EdwardsPoint {
105149 EdwardsPoint {
@@ -130,7 +174,7 @@ impl From<NonIdentity<AffinePoint>> for AffinePoint {
130174impl TryFrom < AffinePoint > for NonIdentity < AffinePoint > {
131175 type Error = Error ;
132176
133- fn try_from ( affine_point : AffinePoint ) -> Result < Self > {
177+ fn try_from ( affine_point : AffinePoint ) -> Result < Self , Error > {
134178 NonIdentity :: new ( affine_point) . into_option ( ) . ok_or ( Error )
135179 }
136180}
@@ -149,3 +193,234 @@ define_mul_variants!(
149193 RHS = EdwardsScalar ,
150194 Output = EdwardsPoint
151195) ;
196+
197+ /// The compressed internal representation of a point on the Twisted Edwards Curve
198+ pub type PointBytes = [ u8 ; 57 ] ;
199+
200+ /// Represents a point on the Compressed Twisted Edwards Curve
201+ /// in little endian format where the most significant bit is the sign bit
202+ /// and the remaining 448 bits represent the y-coordinate
203+ #[ derive( Copy , Clone , Debug ) ]
204+ pub struct CompressedEdwardsY ( pub PointBytes ) ;
205+
206+ impl elliptic_curve:: zeroize:: Zeroize for CompressedEdwardsY {
207+ fn zeroize ( & mut self ) {
208+ self . 0 . zeroize ( )
209+ }
210+ }
211+
212+ impl Display for CompressedEdwardsY {
213+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> FmtResult {
214+ for b in & self . 0 [ ..] {
215+ write ! ( f, "{b:02x}" ) ?;
216+ }
217+ Ok ( ( ) )
218+ }
219+ }
220+
221+ impl LowerHex for CompressedEdwardsY {
222+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> FmtResult {
223+ for b in & self . 0 [ ..] {
224+ write ! ( f, "{b:02x}" ) ?;
225+ }
226+ Ok ( ( ) )
227+ }
228+ }
229+
230+ impl UpperHex for CompressedEdwardsY {
231+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> FmtResult {
232+ for b in & self . 0 [ ..] {
233+ write ! ( f, "{b:02X}" ) ?;
234+ }
235+ Ok ( ( ) )
236+ }
237+ }
238+
239+ impl Default for CompressedEdwardsY {
240+ fn default ( ) -> Self {
241+ Self ( [ 0u8 ; 57 ] )
242+ }
243+ }
244+
245+ impl ConditionallySelectable for CompressedEdwardsY {
246+ fn conditional_select ( a : & Self , b : & Self , choice : Choice ) -> Self {
247+ let mut bytes = [ 0u8 ; 57 ] ;
248+ for ( i, byte) in bytes. iter_mut ( ) . enumerate ( ) {
249+ * byte = u8:: conditional_select ( & a. 0 [ i] , & b. 0 [ i] , choice) ;
250+ }
251+ Self ( bytes)
252+ }
253+ }
254+
255+ impl ConstantTimeEq for CompressedEdwardsY {
256+ fn ct_eq ( & self , other : & Self ) -> Choice {
257+ self . 0 . ct_eq ( & other. 0 )
258+ }
259+ }
260+
261+ impl PartialEq for CompressedEdwardsY {
262+ fn eq ( & self , other : & CompressedEdwardsY ) -> bool {
263+ self . ct_eq ( other) . into ( )
264+ }
265+ }
266+
267+ impl Eq for CompressedEdwardsY { }
268+
269+ impl AsRef < [ u8 ] > for CompressedEdwardsY {
270+ fn as_ref ( & self ) -> & [ u8 ] {
271+ & self . 0 [ ..]
272+ }
273+ }
274+
275+ impl AsRef < PointBytes > for CompressedEdwardsY {
276+ fn as_ref ( & self ) -> & PointBytes {
277+ & self . 0
278+ }
279+ }
280+
281+ #[ cfg( feature = "alloc" ) ]
282+ impl From < CompressedEdwardsY > for Vec < u8 > {
283+ fn from ( value : CompressedEdwardsY ) -> Self {
284+ Self :: from ( & value)
285+ }
286+ }
287+
288+ #[ cfg( feature = "alloc" ) ]
289+ impl From < & CompressedEdwardsY > for Vec < u8 > {
290+ fn from ( value : & CompressedEdwardsY ) -> Self {
291+ value. 0 . to_vec ( )
292+ }
293+ }
294+
295+ #[ cfg( feature = "alloc" ) ]
296+ impl TryFrom < Vec < u8 > > for CompressedEdwardsY {
297+ type Error = & ' static str ;
298+
299+ fn try_from ( value : Vec < u8 > ) -> Result < Self , Self :: Error > {
300+ Self :: try_from ( & value)
301+ }
302+ }
303+
304+ #[ cfg( feature = "alloc" ) ]
305+ impl TryFrom < & Vec < u8 > > for CompressedEdwardsY {
306+ type Error = & ' static str ;
307+
308+ fn try_from ( value : & Vec < u8 > ) -> Result < Self , Self :: Error > {
309+ Self :: try_from ( value. as_slice ( ) )
310+ }
311+ }
312+
313+ impl TryFrom < & [ u8 ] > for CompressedEdwardsY {
314+ type Error = & ' static str ;
315+
316+ fn try_from ( value : & [ u8 ] ) -> Result < Self , Self :: Error > {
317+ let bytes = <PointBytes >:: try_from ( value) . map_err ( |_| "Invalid length" ) ?;
318+ Ok ( CompressedEdwardsY ( bytes) )
319+ }
320+ }
321+
322+ #[ cfg( feature = "alloc" ) ]
323+ impl TryFrom < Box < [ u8 ] > > for CompressedEdwardsY {
324+ type Error = & ' static str ;
325+
326+ fn try_from ( value : Box < [ u8 ] > ) -> Result < Self , Self :: Error > {
327+ Self :: try_from ( value. as_ref ( ) )
328+ }
329+ }
330+
331+ impl From < CompressedEdwardsY > for PointBytes {
332+ fn from ( value : CompressedEdwardsY ) -> Self {
333+ value. 0
334+ }
335+ }
336+
337+ impl From < & CompressedEdwardsY > for PointBytes {
338+ fn from ( value : & CompressedEdwardsY ) -> Self {
339+ Self :: from ( * value)
340+ }
341+ }
342+
343+ #[ cfg( feature = "serde" ) ]
344+ impl serdect:: serde:: Serialize for CompressedEdwardsY {
345+ fn serialize < S : serdect:: serde:: Serializer > ( & self , s : S ) -> Result < S :: Ok , S :: Error > {
346+ serdect:: array:: serialize_hex_lower_or_bin ( & self . 0 , s)
347+ }
348+ }
349+
350+ #[ cfg( feature = "serde" ) ]
351+ impl < ' de > serdect:: serde:: Deserialize < ' de > for CompressedEdwardsY {
352+ fn deserialize < D > ( d : D ) -> Result < Self , D :: Error >
353+ where
354+ D : serdect:: serde:: Deserializer < ' de > ,
355+ {
356+ let mut arr = [ 0u8 ; 57 ] ;
357+ serdect:: array:: deserialize_hex_or_bin ( & mut arr, d) ?;
358+ Ok ( CompressedEdwardsY ( arr) )
359+ }
360+ }
361+
362+ impl From < PointBytes > for CompressedEdwardsY {
363+ fn from ( point : PointBytes ) -> Self {
364+ Self ( point)
365+ }
366+ }
367+
368+ impl CompressedEdwardsY {
369+ /// The compressed generator point
370+ pub const GENERATOR : Self = Self ( [
371+ 20 , 250 , 48 , 242 , 91 , 121 , 8 , 152 , 173 , 200 , 215 , 78 , 44 , 19 , 189 , 253 , 196 , 57 , 124 , 230 ,
372+ 28 , 255 , 211 , 58 , 215 , 194 , 160 , 5 , 30 , 156 , 120 , 135 , 64 , 152 , 163 , 108 , 115 , 115 , 234 ,
373+ 75 , 98 , 199 , 201 , 86 , 55 , 32 , 118 , 136 , 36 , 188 , 182 , 110 , 113 , 70 , 63 , 105 , 0 ,
374+ ] ) ;
375+ /// The compressed identity point
376+ pub const IDENTITY : Self = Self ( [ 0u8 ; 57 ] ) ;
377+
378+ /// Attempt to decompress to an `AffinePoint`.
379+ ///
380+ /// Returns `None` if the input is not the \\(y\\)-coordinate of a
381+ /// curve point.
382+ pub fn decompress_unchecked ( & self ) -> CtOption < AffinePoint > {
383+ // Safe to unwrap here as the underlying data structure is a slice
384+ let ( sign, b) = self . 0 . split_last ( ) . expect ( "slice is non-empty" ) ;
385+
386+ let mut y_bytes: [ u8 ; 56 ] = [ 0 ; 56 ] ;
387+ y_bytes. copy_from_slice ( b) ;
388+
389+ // Recover x using y
390+ let y = FieldElement :: from_bytes ( & y_bytes) ;
391+ let yy = y. square ( ) ;
392+ let dyy = FieldElement :: EDWARDS_D * yy;
393+ let numerator = FieldElement :: ONE - yy;
394+ let denominator = FieldElement :: ONE - dyy;
395+
396+ let ( mut x, is_res) = FieldElement :: sqrt_ratio ( & numerator, & denominator) ;
397+
398+ // Compute correct sign of x
399+ let compressed_sign_bit = Choice :: from ( sign >> 7 ) ;
400+ let is_negative = x. is_negative ( ) ;
401+ x. conditional_negate ( compressed_sign_bit ^ is_negative) ;
402+
403+ CtOption :: new ( AffinePoint { x, y } , is_res)
404+ }
405+
406+ /// Attempt to decompress to an `AffinePoint`.
407+ ///
408+ /// Returns `None`:
409+ /// - if the input is not the \\(y\\)-coordinate of a curve point.
410+ /// - if the input point is not on the curve.
411+ /// - if the input point has nonzero torsion component.
412+ pub fn decompress ( & self ) -> CtOption < AffinePoint > {
413+ self . decompress_unchecked ( )
414+ . and_then ( |pt| CtOption :: new ( pt, pt. is_on_curve ( ) & pt. to_edwards ( ) . is_torsion_free ( ) ) )
415+ }
416+
417+ /// View this `CompressedEdwardsY` as an array of bytes.
418+ pub const fn as_bytes ( & self ) -> & PointBytes {
419+ & self . 0
420+ }
421+
422+ /// Copy this `CompressedEdwardsY` to an array of bytes.
423+ pub const fn to_bytes ( & self ) -> PointBytes {
424+ self . 0
425+ }
426+ }
0 commit comments