github.com/consensys/gnark-crypto@v0.14.0/internal/generator/ecc/template/hash_to_curve.go.tmpl (about) 1 {{$isogenyNeeded := notNil .Isogeny}} 2 {{$CoordType := .Point.CoordType}} 3 {{$CurveName := .Point.PointName}} 4 {{$CurveTitle := toTitle $CurveName}} 5 {{$TowerDegree := .Field.Degree}} 6 {{$AffineType := print $CurveTitle "Affine"}} 7 {{$JacType := print $CurveTitle "Jac"}} 8 {{$IsG1 := eq $CurveTitle "G1"}} 9 {{$CurveIndex := "2"}} 10 {{if $IsG1}}{{$CurveIndex = "1"}}{{end}} 11 12 import( 13 "github.com/consensys/gnark-crypto/ecc/{{.Name}}/fp" 14 {{- if not (eq $TowerDegree 1) }} 15 "github.com/consensys/gnark-crypto/ecc/{{.Name}}/internal/fptower" 16 {{- end}} 17 18 {{if eq $.MappingAlgorithm "SSWU"}} 19 {{template "sswu" .}} 20 {{end}} 21 {{if eq $.MappingAlgorithm "SVDW"}} 22 {{template "svdw" .}} 23 {{end}} 24 25 // {{$CurveName}}Sgn0 is an algebraic substitute for the notion of sign in ordered fields 26 // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign 27 // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function 28 // The sign of an element is not obviously related to that of its Montgomery form 29 func {{$CurveName}}Sgn0(z *{{$CoordType}}) uint64 { 30 31 nonMont := z.Bits() 32 33 {{if eq $TowerDegree 1}} // m == 1 34 return nonMont[0]%2 35 {{else}} 36 sign := uint64(0) // 1. sign = 0 37 zero := uint64(1) // 2. zero = 1 38 var signI uint64 39 var zeroI uint64 40 {{ range $i := interval 0 $TowerDegree}} 41 // 3. i = {{add $i 1}} 42 signI = nonMont.{{$.FieldCoordName}}{{$i}}[0] % 2 // 4. sign_i = x_i mod 2 43 {{- $notLast := not (eq $i (sub $TowerDegree 1))}} 44 {{- if $notLast}} 45 zeroI = g1NotZero(&nonMont.{{$.FieldCoordName}}{{$i}}) 46 zeroI = 1 ^ (zeroI|-zeroI)>>63 // 5. zero_i = x_i == 0 47 {{- else}} 48 // 5. zero_i = x_i == 0 49 {{- end}} 50 sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops 51 {{- if $notLast}} 52 zero = zero & zeroI // 7. zero = zero AND zero_i 53 {{- else}} 54 // 7. zero = zero AND zero_i 55 {{- end}} 56 {{- end}} 57 return sign 58 {{end}} 59 } 60 61 62 // MapTo{{$CurveTitle}} invokes the {{.MappingAlgorithm}} map, and guarantees that the result is in {{$CurveName}} 63 func MapTo{{$CurveTitle}}(u {{$CoordType}}) {{$AffineType}} { 64 res := MapToCurve{{$CurveIndex}}(&u) 65 {{- if $isogenyNeeded }} 66 //this is in an isogenous curve 67 {{$CurveName}}Isogeny(&res) 68 {{- end }} 69 {{- if .Point.CofactorCleaning}} 70 res.ClearCofactor(&res) 71 {{- end }} 72 return res 73 } 74 75 // EncodeTo{{$CurveTitle}} hashes a message to a point on the {{$CurveTitle}} curve using the {{.MappingAlgorithm}} map. 76 // It is faster than HashTo{{$CurveTitle}}, but the result is not uniformly distributed. Unsuitable as a random oracle. 77 // dst stands for "domain separation tag", a string unique to the construction using the hash function 78 //https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap 79 func EncodeTo{{$CurveTitle}}(msg, dst []byte) ({{$AffineType}}, error) { 80 81 var res {{$AffineType}} 82 u, err := fp.Hash(msg, dst, {{$TowerDegree}}) 83 if err != nil { 84 return res, err 85 } 86 87 {{if eq $TowerDegree 1}} 88 res = MapToCurve{{$CurveIndex}}(&u[0]) 89 {{else}} 90 res = MapToCurve{{$CurveIndex}}( &{{$CoordType}} { 91 {{range $i := interval 0 $TowerDegree }} {{if eq $TowerDegree 2}}A{{end}}{{$i}}: u[{{$i}}], 92 {{end}} }) 93 {{end}} 94 95 {{- if $isogenyNeeded }} 96 //this is in an isogenous curve 97 {{$CurveName}}Isogeny(&res) 98 {{- end }} 99 {{- if .Point.CofactorCleaning}} 100 res.ClearCofactor(&res) 101 {{- end }} 102 return res, nil 103 } 104 105 // HashTo{{$CurveTitle}} hashes a message to a point on the {{$CurveTitle}} curve using the {{.MappingAlgorithm}} map. 106 // Slower than EncodeTo{{$CurveTitle}}, but usable as a random oracle. 107 // dst stands for "domain separation tag", a string unique to the construction using the hash function 108 //https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap 109 func HashTo{{$CurveTitle}}(msg, dst []byte) ({{$AffineType}}, error) { 110 u, err := fp.Hash(msg, dst, 2 * {{$TowerDegree}}) 111 if err != nil { 112 return {{$AffineType}}{}, err 113 } 114 115 {{if eq $TowerDegree 1}} 116 Q0 := MapToCurve{{$CurveIndex}}(&u[0]) 117 Q1 := MapToCurve{{$CurveIndex}}(&u[1]) 118 {{else}} 119 Q0 := MapToCurve{{$CurveIndex}}( &{{$CoordType}} { 120 {{range $i := interval 0 $TowerDegree }} {{if eq $TowerDegree 2}}A{{end}}{{$i}}: u[{{$i}}], 121 {{end}} }) 122 Q1 := MapToCurve{{$CurveIndex}}( &{{$CoordType}} { 123 {{range $i := interval 0 $TowerDegree }} {{if eq $TowerDegree 2}}A{{end}}{{$i}}: u[{{$TowerDegree}} + {{$i}}], 124 {{end}} }) 125 {{end}} 126 127 {{ if $isogenyNeeded }} 128 //TODO (perf): Add in E' first, then apply isogeny 129 {{$CurveName}}Isogeny(&Q0) 130 {{$CurveName}}Isogeny(&Q1) 131 {{ end }} 132 133 var _Q0, _Q1 {{$JacType}} 134 _Q0.FromAffine(&Q0) 135 _Q1.FromAffine(&Q1).AddAssign(&_Q0) 136 {{ if .Point.CofactorCleaning}} 137 _Q1.ClearCofactor(&_Q1) 138 {{ end }} 139 140 Q1.FromJacobian(&_Q1) 141 return Q1, nil 142 } 143 144 func {{$CurveName}}NotZero(x *{{$CoordType}}) uint64 { 145 {{if eq $TowerDegree 1}} 146 return x[0] {{ range $i := $.Field.Base.NbWordsIndexesNoZero}} | x[{{$i}}] {{ end}} 147 {{else}} //Assuming G1 is over Fp and that if hashing is available for G2, it also is for G1 148 return g1NotZero(&x.A0) {{ range $i := interval 1 $TowerDegree }} | g1NotZero(&x.{{$.FieldCoordName}}{{$i}}) {{end}} 149 {{end}} 150 }