github.com/consensys/gnark-crypto@v0.14.0/internal/generator/ecc/template/tests/hash_to_curve.go.tmpl (about) 1 {{$CoordType := .Point.CoordType}} 2 {{$CurveName := .Point.PointName}} 3 {{$CurveTitle := toTitle $CurveName}} 4 {{$CurveIndex := "2"}} 5 {{if eq $CurveTitle "G1" }}{{$CurveIndex = "1"}}{{end}} 6 {{$TowerDegree := .Field.Degree}} 7 {{$isogenyNeeded := notNil .Isogeny}} 8 {{$sswu := eq .MappingAlgorithm "SSWU"}} 9 10 import ( 11 "github.com/consensys/gnark-crypto/ecc/{{.Name}}/fp" 12 {{- if ne $TowerDegree 1}} 13 "github.com/consensys/gnark-crypto/ecc/{{.Name}}/internal/fptower" 14 "strings" 15 {{- end}} 16 "testing" 17 "github.com/leanovate/gopter" 18 "github.com/leanovate/gopter/prop" 19 "math/rand" 20 ) 21 22 {{$fuzzer := "GenFp()"}} 23 {{if eq $CoordType "fptower.E2" }} 24 {{$fuzzer = "GenE2()"}} 25 {{- else if eq $CoordType "fptower.E4" }} 26 {{$fuzzer = "GenE4()"}} 27 {{- end}} 28 29 {{if $sswu}} 30 func Test{{$CurveTitle}}SqrtRatio(t *testing.T) { 31 t.Parallel() 32 parameters := gopter.DefaultTestParameters() 33 if testing.Short() { 34 parameters.MinSuccessfulTests = nbFuzzShort 35 } else { 36 parameters.MinSuccessfulTests = nbFuzz 37 } 38 39 40 properties := gopter.NewProperties(parameters) 41 42 gen := {{$fuzzer}} 43 44 45 properties.Property("{{$CurveTitle}}SqrtRatio must square back to the right value", prop.ForAll( 46 func(u {{$CoordType}}, v {{$CoordType}}) bool { 47 48 var seen {{$CoordType}} 49 qr := {{$CurveName}}SqrtRatio(&seen, &u, &v) == 0 50 51 seen. 52 Square(&seen). 53 Mul(&seen, &v) 54 55 var ref {{$CoordType}} 56 if qr { 57 ref = u 58 } else { 59 {{$CurveName}}MulByZ(&ref, &u) 60 } 61 62 return seen.Equal(&ref) 63 }, gen, gen)) 64 65 properties.TestingRun(t, gopter.ConsoleReporter(false)) 66 } 67 {{end}} 68 69 func TestHashToFp{{$CurveTitle}}(t *testing.T) { 70 for _, c := range encodeTo{{$CurveTitle}}Vector.cases { 71 elems, err := fp.Hash([]byte(c.msg), encodeTo{{$CurveTitle}}Vector.dst, {{$TowerDegree}}) 72 if err != nil { 73 t.Error(err) 74 } 75 {{$CurveName}}TestMatchCoord(t, "u", c.msg, c.u, {{$CurveName}}CoordAt(elems, 0)) 76 } 77 78 for _, c := range hashTo{{$CurveTitle}}Vector.cases { 79 elems, err := fp.Hash([]byte(c.msg), hashTo{{$CurveTitle}}Vector.dst, 2 * {{$TowerDegree}}) 80 if err != nil { 81 t.Error(err) 82 } 83 {{$CurveName}}TestMatchCoord(t, "u0", c.msg, c.u0, {{$CurveName}}CoordAt(elems, 0)) 84 {{$CurveName}}TestMatchCoord(t, "u1", c.msg, c.u1, {{$CurveName}}CoordAt(elems, 1)) 85 } 86 } 87 88 func TestMapToCurve{{$CurveIndex}}(t *testing.T) { 89 t.Parallel() 90 parameters := gopter.DefaultTestParameters() 91 if testing.Short() { 92 parameters.MinSuccessfulTests = nbFuzzShort 93 } else { 94 parameters.MinSuccessfulTests = nbFuzz 95 } 96 97 properties := gopter.NewProperties(parameters) 98 99 properties.Property("[{{$CurveTitle}}] mapping output must be on curve", prop.ForAll( 100 func(a {{$CoordType}}) bool { 101 102 g := MapToCurve{{$CurveIndex}}(&a) 103 104 {{if $isogenyNeeded}} 105 if !isOnE{{$CurveIndex}}Prime(g) { 106 t.Log("Mapping output not on E' curve") 107 return false 108 } 109 {{$CurveName}}Isogeny(&g) 110 {{end}} 111 112 if !g.IsOnCurve() { 113 t.Log("{{select $isogenyNeeded "" "Isogeny∘"}}{{.MappingAlgorithm}} output not on curve") 114 return false 115 } 116 117 return true 118 }, 119 {{$fuzzer}}, 120 )) 121 122 properties.TestingRun(t, gopter.ConsoleReporter(false)) 123 124 {{$runIsogeny := select $isogenyNeeded "" (print $CurveName "Isogeny(&q)\n")}} 125 126 for _, c := range encodeTo{{$CurveTitle}}Vector.cases { 127 var u {{$CoordType}} 128 {{$CurveName}}CoordSetString(&u, c.u) 129 q := MapToCurve{{$CurveIndex}}(&u) 130 {{$runIsogeny}}{{$CurveName}}TestMatchPoint(t, "Q", c.msg, c.Q, &q) 131 } 132 133 for _, c := range hashTo{{$CurveTitle}}Vector.cases { 134 var u {{$CoordType}} 135 {{$CurveName}}CoordSetString(&u, c.u0) 136 q := MapToCurve{{$CurveIndex}}(&u) 137 {{$runIsogeny}}{{$CurveName}}TestMatchPoint(t, "Q0", c.msg, c.Q0, &q) 138 139 {{$CurveName}}CoordSetString(&u, c.u1) 140 q = MapToCurve{{$CurveIndex}}(&u) 141 {{$runIsogeny}}{{$CurveName}}TestMatchPoint(t, "Q1", c.msg, c.Q1, &q) 142 } 143 } 144 145 func TestMapTo{{$CurveTitle}}(t *testing.T) { 146 t.Parallel() 147 parameters := gopter.DefaultTestParameters() 148 if testing.Short() { 149 parameters.MinSuccessfulTests = nbFuzzShort 150 } else { 151 parameters.MinSuccessfulTests = nbFuzz 152 } 153 154 properties := gopter.NewProperties(parameters) 155 156 properties.Property("[{{$CurveTitle}}] mapping to curve should output point on the curve", prop.ForAll( 157 func(a {{ $CoordType}}) bool { 158 g := MapTo{{ $CurveTitle}}(a) 159 return g.IsInSubGroup() 160 }, 161 {{$fuzzer}}, 162 )) 163 164 properties.Property("[{{$CurveTitle}}] mapping to curve should be deterministic", prop.ForAll( 165 func(a {{ $CoordType}}) bool { 166 g1 := MapTo{{$CurveTitle}}(a) 167 g2 := MapTo{{$CurveTitle}}(a) 168 return g1.Equal(&g2) 169 }, 170 {{$fuzzer}}, 171 )) 172 173 properties.TestingRun(t, gopter.ConsoleReporter(false)) 174 } 175 176 func TestEncodeTo{{$CurveTitle}}(t *testing.T) { 177 t.Parallel() 178 for _, c := range encodeTo{{$CurveTitle}}Vector.cases { 179 p, err := EncodeTo{{$CurveTitle}}([]byte(c.msg), encodeTo{{$CurveTitle}}Vector.dst) 180 if err != nil { 181 t.Fatal(err) 182 } 183 {{$CurveName}}TestMatchPoint(t, "P", c.msg, c.P, &p) 184 } 185 } 186 187 func TestHashTo{{$CurveTitle}}(t *testing.T) { 188 t.Parallel() 189 for _, c := range hashTo{{$CurveTitle}}Vector.cases { 190 p, err := HashTo{{$CurveTitle}}([]byte(c.msg), hashTo{{$CurveTitle}}Vector.dst) 191 if err != nil { 192 t.Fatal(err) 193 } 194 {{$CurveName}}TestMatchPoint(t, "P", c.msg, c.P, &p) 195 } 196 } 197 198 199 200 func BenchmarkEncodeTo{{$CurveTitle}}(b *testing.B) { 201 const size = 54 202 bytes := make([]byte, size) 203 dst := encodeTo{{$CurveTitle}}Vector.dst 204 b.ResetTimer() 205 206 for i := 0; i < b.N; i++ { 207 208 bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here 209 210 if _, err := EncodeTo{{$CurveTitle}}(bytes, dst); err != nil { 211 b.Fail() 212 } 213 } 214 } 215 216 func BenchmarkHashTo{{$CurveTitle}}(b *testing.B) { 217 const size = 54 218 bytes := make([]byte, size) 219 dst := hashTo{{$CurveTitle}}Vector.dst 220 b.ResetTimer() 221 222 for i := 0; i < b.N; i++ { 223 224 bytes[rand.Int()%size] = byte(rand.Int()) //#nosec G404 weak rng is fine here 225 226 if _, err := HashTo{{$CurveTitle}}(bytes, dst); err != nil { 227 b.Fail() 228 } 229 } 230 } 231 232 {{/* tmplfunc */}} 233 {{ define "setString x array"}} 234 {{.x}}.SetString( 235 {{- range $s := .array}} 236 "{{$s}}", 237 {{- end}} 238 ) 239 {{ end}} 240 241 {{if $isogenyNeeded}} 242 //TODO: Crude. Do something clever in Jacobian 243 func isOnE{{$CurveIndex}}Prime(p {{$CurveTitle}}Affine) bool { 244 245 var A, B {{$CoordType}} 246 247 {{setString "A" .A}} 248 {{setString "B" .B}} 249 250 var LHS {{$CoordType}} 251 LHS. 252 Square(&p.Y). 253 Sub(&LHS, &B) 254 255 var RHS {{$CoordType}} 256 RHS. 257 Square(&p.X). 258 Add(&RHS, &A). 259 Mul(&RHS, &p.X) 260 261 return LHS.Equal(&RHS) 262 } 263 {{end}} 264 265 //Only works on simple extensions (two-story towers) 266 func {{$CurveName}}CoordSetString(z *{{$CoordType}}, s string) { 267 {{- if eq $TowerDegree 1}} 268 z.SetString(s) 269 {{- else}} 270 ssplit := strings.Split(s, ",") 271 if len(ssplit) != {{$TowerDegree}} { 272 panic("not equal to tower size") 273 } 274 z.SetString( 275 {{- range $i := interval 0 $TowerDegree}} 276 ssplit[{{$i}}], 277 {{- end}} 278 ) 279 {{- end}} 280 } 281 282 func {{$CurveName}}CoordAt(slice []fp.Element, i int) {{$CoordType}} { 283 {{- if eq $TowerDegree 1}} 284 return slice[i] 285 {{- else}} 286 return {{$CoordType}} { 287 {{- range $i := iterate 0 $TowerDegree}} 288 {{$.FieldCoordName}}{{$i}}: slice[i * {{$TowerDegree}} + {{$i}}], 289 {{- end}} 290 } 291 {{- end}} 292 } 293 294 func {{$CurveName}}TestMatchCoord(t *testing.T, coordName string, msg string, expectedStr string, seen {{$CoordType}}) { 295 var expected {{$CoordType}} 296 297 {{$CurveName}}CoordSetString(&expected, expectedStr) 298 299 if !expected.Equal(&seen) { 300 t.Errorf("mismatch on \"%s\", %s:\n\texpected %s\n\tsaw %s", msg, coordName, expected.String(), &seen) 301 } 302 } 303 304 func {{$CurveName}}TestMatchPoint(t *testing.T, pointName string, msg string, expected point, seen *{{$CurveTitle}}Affine) { 305 {{$CurveName}}TestMatchCoord(t, pointName+".x", msg, expected.x, seen.X) 306 {{$CurveName}}TestMatchCoord(t, pointName+".y", msg, expected.y, seen.Y) 307 } 308 309 {{ if eq $CurveName "g1"}} 310 311 type hashTestVector struct { 312 dst []byte 313 cases []hashTestCase 314 } 315 316 type encodeTestVector struct { 317 dst []byte 318 cases []encodeTestCase 319 } 320 321 type point struct { 322 x string 323 y string 324 } 325 326 type encodeTestCase struct { 327 msg string 328 P point //pY a coordinate of P, the final output 329 u string //u hashed onto the field 330 Q point //Q map to curve output 331 } 332 333 type hashTestCase struct { 334 msg string 335 P point //pY a coordinate of P, the final output 336 u0 string //u0 hashed onto the field 337 u1 string //u1 extra hashed onto the field 338 Q0 point //Q0 map to curve output 339 Q1 point //Q1 extra map to curve output 340 } 341 342 {{end}} 343 344 var encodeTo{{$CurveTitle}}Vector encodeTestVector 345 var hashTo{{$CurveTitle}}Vector hashTestVector