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