@@ -11,10 +11,10 @@ use std::{
1111use num:: { bigint:: BigUint , Num , One } ;
1212use plonky2:: {
1313 field:: {
14- extension:: { quintic:: QuinticExtension , Extendable , FieldExtension } ,
14+ extension:: { quintic:: QuinticExtension , Extendable , FieldExtension , Frobenius } ,
1515 goldilocks_field:: GoldilocksField ,
1616 ops:: Square ,
17- types:: { Field , PrimeField } ,
17+ types:: { Field , Field64 , PrimeField } ,
1818 } ,
1919 hash:: poseidon:: PoseidonHash ,
2020 iop:: { generator:: SimpleGenerator , target:: BoolTarget , witness:: WitnessWrite } ,
@@ -35,6 +35,30 @@ use crate::backends::plonky2::{
3535
3636type ECField = QuinticExtension < GoldilocksField > ;
3737
38+ /// Computes sqrt in ECField as sqrt(x) = sqrt(x^r)/x^((r-1)/2) with r
39+ /// = 1 + p + ... + p^4, where the numerator involves a sqrt in
40+ /// GoldilocksField, cf.
41+ /// https://github.com/pornin/ecgfp5/blob/ce059c6d1e1662db437aecbf3db6bb67fe63c716/rust/src/field.rs#L1041
42+ pub fn ec_field_sqrt ( x : & ECField ) -> Option < ECField > {
43+ // Compute x^r.
44+ let x_to_the_r = ( 0 ..5 )
45+ . map ( |i| x. repeated_frobenius ( i) )
46+ . reduce ( |a, b| a * b)
47+ . expect ( "Iterator should be nonempty." ) ;
48+ let num = QuinticExtension ( [
49+ x_to_the_r. 0 [ 0 ] . sqrt ( ) ?,
50+ GoldilocksField :: ZERO ,
51+ GoldilocksField :: ZERO ,
52+ GoldilocksField :: ZERO ,
53+ GoldilocksField :: ZERO ,
54+ ] ) ;
55+ // Compute x^((r-1)/2) = x^(p*((1+p)/2)*(1+p^2))
56+ let x1 = x. frobenius ( ) ;
57+ let x2 = x1. exp_u64 ( ( 1 + GoldilocksField :: ORDER ) / 2 ) ;
58+ let den = x2 * x2. repeated_frobenius ( 2 ) ;
59+ Some ( num / den)
60+ }
61+
3862fn ec_field_to_bytes ( x : & ECField ) -> Vec < u8 > {
3963 x. 0 . iter ( )
4064 . flat_map ( |f| {
@@ -78,14 +102,39 @@ impl Point {
78102 pub fn as_fields ( & self ) -> Vec < crate :: middleware:: F > {
79103 self . x . 0 . iter ( ) . chain ( self . u . 0 . iter ( ) ) . cloned ( ) . collect ( )
80104 }
81- pub fn as_bytes ( & self ) -> Vec < u8 > {
82- [ ec_field_to_bytes ( & self . x ) , ec_field_to_bytes ( & self . u ) ] . concat ( )
105+ pub fn compress_from_subgroup ( & self ) -> Result < ECField , Error > {
106+ match self . is_in_subgroup ( ) {
107+ true => Ok ( self . u ) ,
108+ false => Err ( Error :: custom ( format ! (
109+ "Point must lie in EC subgroup: ({}, {})" ,
110+ self . x, self . u
111+ ) ) ) ,
112+ }
113+ }
114+ pub fn decompress_into_subgroup ( u : & ECField ) -> Result < Self , Error > {
115+ if u == & ECField :: ZERO {
116+ return Ok ( Self :: ZERO ) ;
117+ }
118+ // Figure out x.
119+ let b = ECField :: TWO - ECField :: ONE / ( u. square ( ) ) ;
120+ let d = b. square ( ) - ECField :: TWO . square ( ) * Self :: b ( ) ;
121+ let alpha = ECField :: NEG_ONE * b / ECField :: TWO ;
122+ let beta = ec_field_sqrt ( & d)
123+ . ok_or ( Error :: custom ( format ! ( "Not a quadratic residue: {}" , d) ) ) ?
124+ / ECField :: TWO ;
125+ let mut points = [ ECField :: ONE , ECField :: NEG_ONE ] . into_iter ( ) . map ( |s| Point {
126+ x : alpha + s * beta,
127+ u : * u,
128+ } ) ;
129+ points. find ( |p| p. is_in_subgroup ( ) ) . ok_or ( Error :: custom (
130+ "One of the points must lie in the EC subgroup." . into ( ) ,
131+ ) )
132+ }
133+ pub fn as_bytes_from_subgroup ( & self ) -> Result < Vec < u8 > , Error > {
134+ self . compress_from_subgroup ( ) . map ( |u| ec_field_to_bytes ( & u) )
83135 }
84- pub fn from_bytes ( b : & [ u8 ] ) -> Result < Self , Error > {
85- let x_bytes = & b[ ..40 ] ;
86- let u_bytes = & b[ 40 ..] ;
87- ec_field_from_bytes ( x_bytes)
88- . and_then ( |x| ec_field_from_bytes ( u_bytes) . map ( |u| Self { x, u } ) )
136+ pub fn from_bytes_into_subgroup ( b : & [ u8 ] ) -> Result < Self , Error > {
137+ ec_field_from_bytes ( b) . and_then ( |u| Self :: decompress_into_subgroup ( & u) )
89138 }
90139}
91140
@@ -648,7 +697,12 @@ mod test {
648697 use num:: { BigUint , FromPrimitive } ;
649698 use num_bigint:: RandBigInt ;
650699 use plonky2:: {
651- field:: { goldilocks_field:: GoldilocksField , types:: Field } ,
700+ field:: {
701+ extension:: quintic:: QuinticExtension ,
702+ goldilocks_field:: GoldilocksField ,
703+ ops:: Square ,
704+ types:: { Field , Sample } ,
705+ } ,
652706 iop:: witness:: PartialWitness ,
653707 plonk:: {
654708 circuit_builder:: CircuitBuilder , circuit_data:: CircuitConfig ,
@@ -659,7 +713,9 @@ mod test {
659713
660714 use crate :: backends:: plonky2:: primitives:: ec:: {
661715 bits:: CircuitBuilderBits ,
662- curve:: { CircuitBuilderElliptic , ECField , Point , WitnessWriteCurve , GROUP_ORDER } ,
716+ curve:: {
717+ ec_field_sqrt, CircuitBuilderElliptic , ECField , Point , WitnessWriteCurve , GROUP_ORDER ,
718+ } ,
663719 } ;
664720
665721 #[ test]
@@ -688,6 +744,13 @@ mod test {
688744 assert_eq ! ( p2, p3) ;
689745 }
690746
747+ #[ test]
748+ fn test_sqrt ( ) {
749+ let x = QuinticExtension :: rand ( ) . square ( ) ;
750+ let y = ec_field_sqrt ( & x) ;
751+ assert_eq ! ( y. map( |a| a. square( ) ) , Some ( x) ) ;
752+ }
753+
691754 #[ test]
692755 fn test_associativity ( ) {
693756 let g = Point :: generator ( ) ;
0 commit comments