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