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  }