@@ -604,6 +604,92 @@ impl EdwardsPoint {
604
604
. expect ( "Montgomery conversion to Edwards point in Elligator failed" )
605
605
. mul_by_cofactor ( )
606
606
}
607
+
608
+ #[ cfg( feature = "group" ) ]
609
+ /// Maps the input bytes to the curve. This implements the spec for
610
+ /// [`hash_to_curve`](https://datatracker.ietf.org/doc/rfc9380/) according to sections
611
+ /// 8.5 and J.5
612
+ pub fn hash_to_curve < X > ( msg : & [ u8 ] , dst : & [ u8 ] ) -> Self
613
+ where
614
+ X : for < ' a > elliptic_curve:: hash2curve:: ExpandMsg < ' a > ,
615
+ {
616
+ use elliptic_curve:: {
617
+ bigint:: { ArrayEncoding , Encoding , NonZero , U384 } ,
618
+ hash2curve:: Expander ,
619
+ } ;
620
+
621
+ let dst = [ dst] ;
622
+ let mut random_bytes = [ 0u8 ; 96 ] ;
623
+ let mut expander =
624
+ X :: expand_message ( & [ msg] , & dst, random_bytes. len ( ) ) . expect ( "expand_message failed" ) ;
625
+ expander. fill_bytes ( & mut random_bytes) ;
626
+
627
+ let p = NonZero :: new ( U384 :: from_be_hex ( "000000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed" ) ) . expect ( "NonZero::new failed" ) ;
628
+ let u0 = U384 :: from_be_bytes (
629
+ <[ u8 ; 48 ] >:: try_from ( & random_bytes[ ..48 ] ) . expect ( "try_from failed" ) ,
630
+ ) % p;
631
+ let u1 = U384 :: from_be_bytes (
632
+ <[ u8 ; 48 ] >:: try_from ( & random_bytes[ 48 ..] ) . expect ( "try_from failed" ) ,
633
+ ) % p;
634
+
635
+ let mut arr = [ 0u8 ; 32 ] ;
636
+ arr. copy_from_slice ( & u0. to_le_byte_array ( ) [ ..32 ] ) ;
637
+ let u0 = FieldElement :: from_bytes ( & arr) ;
638
+ arr. copy_from_slice ( & u1. to_le_byte_array ( ) [ ..32 ] ) ;
639
+ let u1 = FieldElement :: from_bytes ( & arr) ;
640
+
641
+ let q0 = map_to_edwards ( u0) ;
642
+ let q1 = map_to_edwards ( u1) ;
643
+ let p = q0 + q1;
644
+ p. mul_by_cofactor ( )
645
+ }
646
+ }
647
+
648
+ fn map_to_edwards ( e : FieldElement ) -> EdwardsPoint {
649
+ let ( u, v) = elligator_encode ( e) ;
650
+ let ( x, y) = montgomery_to_edwards ( u, v) ;
651
+ affine_to_edwards ( x, y)
652
+ }
653
+
654
+ fn elligator_encode ( e : FieldElement ) -> ( FieldElement , FieldElement ) {
655
+ let mut t1 = & ( & FieldElement :: ONE + & FieldElement :: ONE ) * & e. square ( ) ; // 2u^2
656
+ let e1 = t1. ct_eq ( & FieldElement :: MINUS_ONE ) ;
657
+ t1. conditional_assign ( & FieldElement :: ZERO , e1) ; // if 2u^2 == -1, t1 = 0
658
+ let x1 = & ( & t1 + & FieldElement :: ONE ) . invert ( ) * & FieldElement :: EDWARDS_MINUS_ELL_A ; // -A / t1 + 1
659
+ let min_x1 = -( & x1) ;
660
+
661
+ let gx1 = & ( & ( & ( & x1 + & FieldElement :: EDWARDS_ELL_A ) * & x1) + & FieldElement :: ONE ) * & x1; // x1 * (x1 * (x1 + A) + 1)
662
+ let x2 = & min_x1 - & FieldElement :: EDWARDS_ELL_A ; // -x1 - A
663
+ let gx2 = & t1 * & gx1;
664
+ let ( is_square, root1) = FieldElement :: sqrt_ratio_i ( & gx1, & FieldElement :: ONE ) ;
665
+ let neg_root1 = -( & root1) ;
666
+ let ( _, root2) = FieldElement :: sqrt_ratio_i ( & gx2, & FieldElement :: ONE ) ;
667
+
668
+ let x = FieldElement :: conditional_select ( & x2, & x1, is_square) ;
669
+ let y = FieldElement :: conditional_select ( & root2, & neg_root1, is_square) ;
670
+ ( x, y)
671
+ }
672
+
673
+ fn montgomery_to_edwards ( u : FieldElement , v : FieldElement ) -> ( FieldElement , FieldElement ) {
674
+ let inv_sqr_d = FieldElement :: from_bytes ( & [
675
+ 6 , 126 , 69 , 255 , 170 , 4 , 110 , 204 , 130 , 26 , 125 , 75 , 209 , 211 , 161 , 197 , 126 , 79 , 252 , 3 ,
676
+ 220 , 8 , 123 , 210 , 187 , 6 , 160 , 96 , 244 , 237 , 38 , 15 ,
677
+ ] ) ;
678
+ let x = & ( & v. invert ( ) * & u) * & inv_sqr_d;
679
+ let u1 = & u - & FieldElement :: ONE ;
680
+ let u2 = & u + & FieldElement :: ONE ;
681
+ let y = & u1 * & u2. invert ( ) ;
682
+ ( x, y)
683
+ }
684
+
685
+ fn affine_to_edwards ( x : FieldElement , y : FieldElement ) -> EdwardsPoint {
686
+ let t = & x * & y;
687
+ EdwardsPoint {
688
+ X : x,
689
+ Y : y,
690
+ Z : FieldElement :: ONE ,
691
+ T : t,
692
+ }
607
693
}
608
694
609
695
// ------------------------------------------------------------------------
@@ -2267,4 +2353,25 @@ mod test {
2267
2353
assert_eq ! ( point. compress( ) . to_bytes( ) , output[ ..] ) ;
2268
2354
}
2269
2355
}
2356
+
2357
+ #[ cfg( feature = "group" ) ]
2358
+ #[ test]
2359
+ fn hash_to_curve ( ) {
2360
+ const DST : & [ u8 ] = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_" ;
2361
+ let msgs: [ ( & [ u8 ] , & str ) ; 5 ] = [
2362
+ ( b"" , "09a6c8561a0b22bef63124c588ce4c62ea83a3c899763af26d795302e115dc21" ) ,
2363
+ ( b"abc" , "9a8395b88338f22e435bbd301183e7f20a5f9de643f11882fb237f88268a5531" ) ,
2364
+ ( b"abcdef0123456789" , "53060a3d140e7fbcda641ed3cf42c88a75411e648a1add71217f70ea8ec561a6" ) ,
2365
+ ( b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" , "2eca15e355fcfa39d2982f67ddb0eea138e2994f5956ed37b7f72eea5e89d2f7" ) ,
2366
+ ( b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" , "6dc2fc04f266c5c27f236a80b14f92ccd051ef1ff027f26a07f8c0f327d8f995" ) ,
2367
+ ] ;
2368
+ for ( input, expected_hex) in msgs {
2369
+ let pt = EdwardsPoint :: hash_to_curve :: <
2370
+ elliptic_curve:: hash2curve:: ExpandMsgXmd < sha2:: Sha512 > ,
2371
+ > ( input, DST ) ;
2372
+ let mut expected_bytes = hex:: decode ( expected_hex) . unwrap ( ) ;
2373
+ expected_bytes. reverse ( ) ;
2374
+ assert_eq ! ( expected_bytes, pt. to_bytes( ) ) ;
2375
+ }
2376
+ }
2270
2377
}
0 commit comments