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,34 @@ impl AffinePoint {
100
117
}
101
118
}
102
119
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
+
103
148
/// Convert to edwards extended point
104
149
pub fn to_edwards ( & self ) -> EdwardsPoint {
105
150
EdwardsPoint {
@@ -130,7 +175,7 @@ impl From<NonIdentity<AffinePoint>> for AffinePoint {
130
175
impl TryFrom < AffinePoint > for NonIdentity < AffinePoint > {
131
176
type Error = Error ;
132
177
133
- fn try_from ( affine_point : AffinePoint ) -> Result < Self > {
178
+ fn try_from ( affine_point : AffinePoint ) -> Result < Self , Error > {
134
179
NonIdentity :: new ( affine_point) . into_option ( ) . ok_or ( Error )
135
180
}
136
181
}
@@ -149,3 +194,234 @@ define_mul_variants!(
149
194
RHS = EdwardsScalar ,
150
195
Output = EdwardsPoint
151
196
) ;
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