@@ -193,6 +193,81 @@ pub(crate) fn elligator_encode(r_0: &FieldElement) -> MontgomeryPoint {
193
193
MontgomeryPoint ( u. to_bytes ( ) )
194
194
}
195
195
196
+ /// Perform the inverse Elligator2 mapping from a Montgomery point to
197
+ /// field element. This algorithm is based on [Elligator:
198
+ /// Elliptic-curve points indistinguishable from uniform random
199
+ /// strings][elligator], Section 5.3.
200
+ ///
201
+ /// This function is a partial right inverse of `elligator_encode`: if
202
+ /// `elligator_decode(&p, sign) == Some(fe)` then `elligator_encode(&fe)
203
+ /// == p`.
204
+ ///
205
+ /// This function does _not_ operate in constant time: if `point`
206
+ /// cannot be mapped to a field element, the function exits early. In
207
+ /// typical usage this function is combined with rejection sampling
208
+ /// and called repeatedly until a mappable point is found.
209
+ ///
210
+ /// Note: the output field elements of this function are uniformly
211
+ /// distributed among the nonnegative field elements, but only if the
212
+ /// input points are also uniformly distributed among all points of
213
+ /// the curve. In particular, if the inputs are only selected from
214
+ /// members of the prime order group, then the outputs are
215
+ /// distinguishable from random.
216
+ ///
217
+ /// # Inputs
218
+ ///
219
+ /// * `point`: the \\(u\\)-coordinate of a point on the curve. Not all
220
+ /// points map to field elements.
221
+ ///
222
+ /// * `v_is_negative`: true if the \\(v\\)-coordinate of the point is negative.
223
+ ///
224
+ /// # Returns
225
+ ///
226
+ /// Either `None`, if the point couldn't be mapped, or `Some(fe)` such
227
+ /// that `elligator_encode(&fe) == point`.
228
+ ///
229
+ /// [elligator] https://elligator.cr.yp.to/elligator-20130828.pdf
230
+ ///
231
+ #[ allow( unused) ]
232
+ pub ( crate ) fn elligator_decode ( point : & MontgomeryPoint , v_is_negative : Choice ) -> Option < FieldElement > {
233
+ let one = FieldElement :: one ( ) ;
234
+ let u = FieldElement :: from_bytes ( & point. to_bytes ( ) ) ;
235
+ let u_plus_A = & u + & MONTGOMERY_A ;
236
+ let uu_plus_uA = & u * & u_plus_A;
237
+
238
+ // Condition: u is on the curve
239
+ let vv = & ( & u * & uu_plus_uA) + & u;
240
+ let ( u_is_on_curve, _v) = FieldElement :: sqrt_ratio_i ( & vv, & one) ;
241
+ if ( !bool:: from ( u_is_on_curve) ) {
242
+ return None
243
+ }
244
+
245
+ // Condition: u != A
246
+ if ( u == MONTGOMERY_A_NEG ) {
247
+ return None
248
+ }
249
+
250
+ // Condition: -2u(u+A) is a square
251
+ let minus_uu2_minus_uA2 = -& ( & uu_plus_uA + & uu_plus_uA) ;
252
+ let ( is_square, _) = FieldElement :: sqrt_ratio_i ( & minus_uu2_minus_uA2, & one) ;
253
+ if ( !bool:: from ( is_square) ) {
254
+ return None ;
255
+ }
256
+
257
+ let mut t1 = u;
258
+ let mut t2 = u_plus_A;
259
+ FieldElement :: conditional_swap ( & mut t1, & mut t2, v_is_negative) ;
260
+ t1. negate ( ) ;
261
+ t2 = & t2 + & t2;
262
+
263
+ // if !v_is_negative: r = sqrt(-u / 2(u + a))
264
+ // if v_is_negative: r = sqrt(-(u+A) / 2u)
265
+ let ( r_is_square, r) = FieldElement :: sqrt_ratio_i ( & t1, & t2) ;
266
+ debug_assert ! ( bool :: from( r_is_square) ) ;
267
+
268
+ return Some ( r) ;
269
+ }
270
+
196
271
/// A `ProjectivePoint` holds a point on the projective line
197
272
/// \\( \mathbb P(\mathbb F\_p) \\), which we identify with the Kummer
198
273
/// line of the Montgomery curve.
@@ -356,7 +431,7 @@ mod test {
356
431
use constants;
357
432
use core:: convert:: TryInto ;
358
433
359
- use rand_core:: OsRng ;
434
+ use rand_core:: { OsRng , RngCore } ;
360
435
361
436
#[ test]
362
437
fn identity_in_different_coordinates ( ) {
@@ -469,11 +544,100 @@ mod test {
469
544
assert_eq ! ( eg. to_bytes( ) , ELLIGATOR_CORRECT_OUTPUT ) ;
470
545
}
471
546
547
+ #[ test]
548
+ #[ cfg( feature = "std" ) ] // Vec
549
+ fn montgomery_elligator_decode_correct ( ) {
550
+ let bytes: std:: vec:: Vec < u8 > = ( 0u8 ..32u8 ) . collect ( ) ;
551
+ let bits_in: [ u8 ; 32 ] = ( & bytes[ ..] ) . try_into ( ) . expect ( "Range invariant broken" ) ;
552
+
553
+ let fe = FieldElement :: from_bytes ( & bits_in) ;
554
+ let eg = MontgomeryPoint ( ELLIGATOR_CORRECT_OUTPUT ) ;
555
+ let result = elligator_decode ( & eg, 0 . into ( ) ) ;
556
+ assert_eq ! ( result, Some ( fe) ) ;
557
+ }
558
+
559
+ #[ test]
560
+ fn montgomery_elligator_encode_decode ( ) {
561
+ for _i in 0 ..4096 {
562
+ let mut bits = [ 0u8 ; 32 ] ;
563
+ OsRng . fill_bytes ( & mut bits) ;
564
+
565
+ let fe = FieldElement :: from_bytes ( & bits) ;
566
+ let eg = elligator_encode ( & fe) ;
567
+
568
+ // Up to four different field values may encode to a
569
+ // single MontgomeryPoint. Firstly, the MontgomeryPoint
570
+ // loses the v-coordinate, so it may represent two
571
+ // different curve points, (u, v) and (u, -v). The
572
+ // elligator_decode function is given a sign argument to
573
+ // indicate which one is meant.
574
+ //
575
+ // Second, for each curve point (except zero), two
576
+ // distinct field elements encode to it, r and -r. The
577
+ // elligator_decode function returns the nonnegative one.
578
+ //
579
+ // We check here that one of these four values is equal to
580
+ // the original input to elligator_encode.
581
+
582
+ let mut found = false ;
583
+
584
+ for i in 0 ..=1 {
585
+ let decoded = elligator_decode ( & eg, i. into ( ) )
586
+ . expect ( "Elligator decode failed" ) ;
587
+ for j in & [ fe, -& fe] {
588
+ found |= decoded == * j;
589
+ }
590
+ }
591
+
592
+ assert ! ( found) ;
593
+ }
594
+ }
595
+
596
+ #[ test]
597
+ fn montgomery_elligator_decode_encode ( ) {
598
+ for i in 0 ..4096 {
599
+ let mut bits = [ 0u8 ; 32 ] ;
600
+ OsRng . fill_bytes ( & mut bits) ;
601
+
602
+ let point = MontgomeryPoint ( bits) ;
603
+
604
+ let result = elligator_decode ( & point, ( ( i % 2 ) as u8 ) . into ( ) ) ;
605
+
606
+ if let Some ( fe) = result {
607
+ let encoded = elligator_encode ( & fe) ;
608
+
609
+ assert_eq ! ( encoded, point) ;
610
+ }
611
+ }
612
+ }
613
+
614
+ /// Test that Elligator decoding will fail on a point that is not on the curve.
615
+ #[ test]
616
+ fn montgomery_elligator_decode_noncurve ( ) {
617
+ let one = FieldElement :: one ( ) ;
618
+
619
+ // u = 2 corresponds to a point on the twist.
620
+ let two = MontgomeryPoint ( ( & one+& one) . to_bytes ( ) ) ;
621
+
622
+ for i in 0 ..=1 {
623
+ let result = elligator_decode ( & two, i. into ( ) ) ;
624
+ assert_eq ! ( None , result) ;
625
+ }
626
+ }
627
+
472
628
#[ test]
473
629
fn montgomery_elligator_zero_zero ( ) {
474
630
let zero = [ 0u8 ; 32 ] ;
475
631
let fe = FieldElement :: from_bytes ( & zero) ;
476
632
let eg = elligator_encode ( & fe) ;
477
633
assert_eq ! ( eg. to_bytes( ) , zero) ;
478
634
}
635
+
636
+ #[ test]
637
+ fn montgomery_elligator_decode_zero_zero ( ) {
638
+ let zero = [ 0u8 ; 32 ] ;
639
+ let eg = MontgomeryPoint ( zero) ;
640
+ let fe = elligator_decode ( & eg, 0 . into ( ) ) . expect ( "Elligator decode failed" ) ;
641
+ assert_eq ! ( fe. to_bytes( ) , zero) ;
642
+ }
479
643
}
0 commit comments