@@ -604,6 +604,92 @@ impl EdwardsPoint {
604604 . expect ( "Montgomery conversion to Edwards point in Elligator failed" )
605605 . mul_by_cofactor ( )
606606 }
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+ }
607693}
608694
609695// ------------------------------------------------------------------------
@@ -2267,4 +2353,25 @@ mod test {
22672353 assert_eq ! ( point. compress( ) . to_bytes( ) , output[ ..] ) ;
22682354 }
22692355 }
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+ }
22702377}
0 commit comments