1
1
use crate :: field:: FieldElement ;
2
2
use crate :: * ;
3
+ use core:: fmt:: { Display , Formatter , LowerHex , Result as FmtResult , UpperHex } ;
3
4
use 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 } ;
6
8
7
9
/// Affine point on untwisted curve
8
10
#[ derive( Copy , Clone , Debug ) ]
@@ -69,6 +71,21 @@ impl AffinePoint {
69
71
y : FieldElement :: ONE ,
70
72
} ;
71
73
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
+
72
89
pub ( crate ) fn isogeny ( & self ) -> Self {
73
90
let x = self . x ;
74
91
let y = self . y ;
@@ -100,6 +117,33 @@ impl AffinePoint {
100
117
}
101
118
}
102
119
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
+
103
147
/// Convert to edwards extended point
104
148
pub fn to_edwards ( & self ) -> EdwardsPoint {
105
149
EdwardsPoint {
@@ -130,7 +174,7 @@ impl From<NonIdentity<AffinePoint>> for AffinePoint {
130
174
impl TryFrom < AffinePoint > for NonIdentity < AffinePoint > {
131
175
type Error = Error ;
132
176
133
- fn try_from ( affine_point : AffinePoint ) -> Result < Self > {
177
+ fn try_from ( affine_point : AffinePoint ) -> Result < Self , Error > {
134
178
NonIdentity :: new ( affine_point) . into_option ( ) . ok_or ( Error )
135
179
}
136
180
}
@@ -149,3 +193,234 @@ define_mul_variants!(
149
193
RHS = EdwardsScalar ,
150
194
Output = EdwardsPoint
151
195
) ;
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