gmsm/internal/sm2ec/fiat/generate.go

299 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import (
"bytes"
"go/format"
"io"
"log"
"os"
"os/exec"
"text/template"
)
var curves = []struct {
Element string
Prime string
Prefix string
FiatType string
BytesLen int
}{
{
Element: "SM2P256Element",
Prime: "2^256 - 2^224 - 2^96 + 2^64 - 1",
Prefix: "sm2p256",
FiatType: "[4]uint64",
BytesLen: 32,
},
{
Element: "SM2P256OrderElement",
Prime: "2^256 - 2^224 - 188730267045675049073202170516080344797",
Prefix: "sm2p256scalar",
FiatType: "[4]uint64",
BytesLen: 32,
},
}
func main() {
t := template.Must(template.New("montgomery").Parse(tmplWrapper))
tmplAddchainFile, err := os.CreateTemp("", "addchain-template")
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmplAddchainFile.Name())
if _, err := io.WriteString(tmplAddchainFile, tmplAddchain); err != nil {
log.Fatal(err)
}
if err := tmplAddchainFile.Close(); err != nil {
log.Fatal(err)
}
for _, c := range curves {
log.Printf("Generating %s.go...", c.Prefix)
f, err := os.Create(c.Prefix + ".go")
if err != nil {
log.Fatal(err)
}
if err := t.Execute(f, c); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
log.Printf("Generating %s_fiat64.go...", c.Prefix)
cmd := exec.Command("docker", "run", "--rm", "--entrypoint", "word_by_word_montgomery",
"fiat-crypto:v0.0.9", "--lang", "Go", "--no-wide-int", "--cmovznz-by-mul",
"--relax-primitive-carry-to-bitwidth", "32,64", "--internal-static",
"--public-function-case", "camelCase", "--public-type-case", "camelCase",
"--private-function-case", "camelCase", "--private-type-case", "camelCase",
"--doc-text-before-function-name", "", "--doc-newline-before-package-declaration",
"--doc-prepend-header", "Code generated by Fiat Cryptography. DO NOT EDIT.",
"--package-name", "fiat", "--no-prefix-fiat", c.Prefix, "64", c.Prime,
"mul", "square", "add", "sub", "one", "from_montgomery", "to_montgomery",
"selectznz", "to_bytes", "from_bytes", "nonzero", "opp", "msat", "divstep", "divstep_precomp")
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
out, err = format.Source(out)
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile(c.Prefix+"_fiat64.go", out, 0644); err != nil {
log.Fatal(err)
}
log.Printf("Generating %s_invert.go...", c.Prefix)
f, err = os.CreateTemp("", "addchain-"+c.Prefix)
if err != nil {
log.Fatal(err)
}
defer os.Remove(f.Name())
cmd = exec.Command("addchain", "search", c.Prime+" - 2")
cmd.Stderr = os.Stderr
cmd.Stdout = f
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
cmd = exec.Command("addchain", "gen", "-tmpl", tmplAddchainFile.Name(), f.Name())
cmd.Stderr = os.Stderr
out, err = cmd.Output()
if err != nil {
log.Fatal(err)
}
out = bytes.Replace(out, []byte("Element"), []byte(c.Element), -1)
out, err = format.Source(out)
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile(c.Prefix+"_invert.go", out, 0644); err != nil {
log.Fatal(err)
}
}
}
const tmplWrapper = `// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by generate.go. DO NOT EDIT.
package fiat
import (
"crypto/subtle"
"errors"
_subtle "github.com/emmansun/gmsm/internal/subtle"
)
// {{ .Element }} is an integer modulo {{ .Prime }}.
//
// The zero value is a valid zero element.
type {{ .Element }} struct {
// Values are represented internally always in the Montgomery domain, and
// converted in Bytes and SetBytes.
x {{ .Prefix }}MontgomeryDomainFieldElement
}
const {{ .Prefix }}ElementLen = {{ .BytesLen }}
type {{ .Prefix }}UntypedFieldElement = {{ .FiatType }}
// One sets e = 1, and returns e.
func (e *{{ .Element }}) One() *{{ .Element }} {
{{ .Prefix }}SetOne(&e.x)
return e
}
// Equal returns 1 if e == t, and zero otherwise.
func (e *{{ .Element }}) Equal(t *{{ .Element }}) int {
eBytes := e.Bytes()
tBytes := t.Bytes()
return subtle.ConstantTimeCompare(eBytes, tBytes)
}
// IsZero returns 1 if e == 0, and zero otherwise.
func (e *{{ .Element }}) IsZero() int {
zero := make([]byte, {{ .Prefix }}ElementLen)
eBytes := e.Bytes()
return subtle.ConstantTimeCompare(eBytes, zero)
}
// Set sets e = t, and returns e.
func (e *{{ .Element }}) Set(t *{{ .Element }}) *{{ .Element }} {
e.x = t.x
return e
}
// Bytes returns the {{ .BytesLen }}-byte big-endian encoding of e.
func (e *{{ .Element }}) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [{{ .Prefix }}ElementLen]byte
return e.bytes(&out)
}
func (e *{{ .Element }}) bytes(out *[{{ .Prefix }}ElementLen]byte) []byte {
var tmp {{ .Prefix }}NonMontgomeryDomainFieldElement
{{ .Prefix }}FromMontgomery(&tmp, &e.x)
{{ .Prefix }}ToBytes(out, (*{{ .Prefix }}UntypedFieldElement)(&tmp))
{{ .Prefix }}InvertEndianness(out[:])
return out[:]
}
// SetBytes sets e = v, where v is a big-endian {{ .BytesLen }}-byte encoding, and returns e.
// If v is not {{ .BytesLen }} bytes or it encodes a value higher than {{ .Prime }},
// SetBytes returns nil and an error, and e is unchanged.
func (e *{{ .Element }}) SetBytes(v []byte) (*{{ .Element }}, error) {
if len(v) != {{ .Prefix }}ElementLen {
return nil, errors.New("invalid {{ .Element }} encoding")
}
// Check for non-canonical encodings (p + k, 2p + k, etc.) by comparing to
// the encoding of -1 mod p, so p - 1, the highest canonical encoding.
var minusOneEncoding = new({{ .Element }}).Sub(
new({{ .Element }}), new({{ .Element }}).One()).Bytes()
if _subtle.ConstantTimeLessOrEqBytes(v, minusOneEncoding) == 0 {
return nil, errors.New("invalid {{ .Element }} encoding")
}
var in [{{ .Prefix }}ElementLen]byte
copy(in[:], v)
{{ .Prefix }}InvertEndianness(in[:])
var tmp {{ .Prefix }}NonMontgomeryDomainFieldElement
{{ .Prefix }}FromBytes((*{{ .Prefix }}UntypedFieldElement)(&tmp), &in)
{{ .Prefix }}ToMontgomery(&e.x, &tmp)
return e, nil
}
// Add sets e = t1 + t2, and returns e.
func (e *{{ .Element }}) Add(t1, t2 *{{ .Element }}) *{{ .Element }} {
{{ .Prefix }}Add(&e.x, &t1.x, &t2.x)
return e
}
// Sub sets e = t1 - t2, and returns e.
func (e *{{ .Element }}) Sub(t1, t2 *{{ .Element }}) *{{ .Element }} {
{{ .Prefix }}Sub(&e.x, &t1.x, &t2.x)
return e
}
// Mul sets e = t1 * t2, and returns e.
func (e *{{ .Element }}) Mul(t1, t2 *{{ .Element }}) *{{ .Element }} {
{{ .Prefix }}Mul(&e.x, &t1.x, &t2.x)
return e
}
// Square sets e = t * t, and returns e.
func (e *{{ .Element }}) Square(t *{{ .Element }}) *{{ .Element }} {
{{ .Prefix }}Square(&e.x, &t.x)
return e
}
// Select sets v to a if cond == 1, and to b if cond == 0.
func (v *{{ .Element }}) Select(a, b *{{ .Element }}, cond int) *{{ .Element }} {
{{ .Prefix }}Selectznz((*{{ .Prefix }}UntypedFieldElement)(&v.x), {{ .Prefix }}Uint1(cond),
(*{{ .Prefix }}UntypedFieldElement)(&b.x), (*{{ .Prefix }}UntypedFieldElement)(&a.x))
return v
}
func {{ .Prefix }}InvertEndianness(v []byte) {
for i := 0; i < len(v)/2; i++ {
v[i], v[len(v)-1-i] = v[len(v)-1-i], v[i]
}
}
`
const tmplAddchain = `// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by {{ .Meta.Name }}. DO NOT EDIT.
package fiat
// Invert sets e = 1/x, and returns e.
//
// If x == 0, Invert returns e = 0.
func (e *Element) Invert(x *Element) *Element {
// Inversion is implemented as exponentiation with exponent p 2.
// The sequence of {{ .Ops.Adds }} multiplications and {{ .Ops.Doubles }} squarings is derived from the
// following addition chain generated with {{ .Meta.Module }} {{ .Meta.ReleaseTag }}.
//
{{- range lines (format .Script) }}
// {{ . }}
{{- end }}
//
var z = new(Element).Set(e)
{{- range .Program.Temporaries }}
var {{ . }} = new(Element)
{{- end }}
{{ range $i := .Program.Instructions -}}
{{- with add $i.Op }}
{{ $i.Output }}.Mul({{ .X }}, {{ .Y }})
{{- end -}}
{{- with double $i.Op }}
{{ $i.Output }}.Square({{ .X }})
{{- end -}}
{{- with shift $i.Op -}}
{{- $first := 0 -}}
{{- if ne $i.Output.Identifier .X.Identifier }}
{{ $i.Output }}.Square({{ .X }})
{{- $first = 1 -}}
{{- end }}
for s := {{ $first }}; s < {{ .S }}; s++ {
{{ $i.Output }}.Square({{ $i.Output }})
}
{{- end -}}
{{- end }}
return e.Set(z)
}
`