summaryrefslogtreecommitdiffstats
path: root/src/crypto/internal/nistec/fiat/generate.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/internal/nistec/fiat/generate.go')
-rw-r--r--src/crypto/internal/nistec/fiat/generate.go331
1 files changed, 331 insertions, 0 deletions
diff --git a/src/crypto/internal/nistec/fiat/generate.go b/src/crypto/internal/nistec/fiat/generate.go
new file mode 100644
index 0000000..3b97307
--- /dev/null
+++ b/src/crypto/internal/nistec/fiat/generate.go
@@ -0,0 +1,331 @@
+// 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: "P224Element",
+ Prime: "2^224 - 2^96 + 1",
+ Prefix: "p224",
+ FiatType: "[4]uint64",
+ BytesLen: 28,
+ },
+ // The P-256 fiat implementation is used only on 32-bit architectures, but
+ // the uint32 fiat code is for some reason slower than the uint64 one. That
+ // suggests there is a wide margin for improvement.
+ {
+ Element: "P256Element",
+ Prime: "2^256 - 2^224 + 2^192 + 2^96 - 1",
+ Prefix: "p256",
+ FiatType: "[4]uint64",
+ BytesLen: 32,
+ },
+ {
+ Element: "P384Element",
+ Prime: "2^384 - 2^128 - 2^96 + 2^32 - 1",
+ Prefix: "p384",
+ FiatType: "[6]uint64",
+ BytesLen: 48,
+ },
+ // Note that unsaturated_solinas would be about 2x faster than
+ // word_by_word_montgomery for P-521, but this curve is used rarely enough
+ // that it's not worth carrying unsaturated_solinas support for it.
+ {
+ Element: "P521Element",
+ Prime: "2^521 - 1",
+ Prefix: "p521",
+ FiatType: "[9]uint64",
+ BytesLen: 66,
+ },
+}
+
+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")
+ 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"
+)
+
+// {{ .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)
+}
+
+var {{ .Prefix }}ZeroEncoding = new({{ .Element }}).Bytes()
+
+// IsZero returns 1 if e == 0, and zero otherwise.
+func (e *{{ .Element }}) IsZero() int {
+ eBytes := e.Bytes()
+ return subtle.ConstantTimeCompare(eBytes, {{ .Prefix }}ZeroEncoding)
+}
+
+// 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[:]
+}
+
+// {{ .Prefix }}MinusOneEncoding is the encoding of -1 mod p, so p - 1, the
+// highest canonical encoding. It is used by SetBytes to check for non-canonical
+// encodings such as p + k, 2p + k, etc.
+var {{ .Prefix }}MinusOneEncoding = new({{ .Element }}).Sub(
+ new({{ .Element }}), new({{ .Element }}).One()).Bytes()
+
+// 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")
+ }
+ for i := range v {
+ if v[i] < {{ .Prefix }}MinusOneEncoding[i] {
+ break
+ }
+ if v[i] > {{ .Prefix }}MinusOneEncoding[i] {
+ 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)
+}
+`