// 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) } // 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() for i := range v { if v[i] < minusOneEncoding[i] { break } if v[i] > 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) } `