330 changes: 330 additions & 0 deletions src/crypto/elliptic/internal/fiat/generate.go
@@ -0,0 +1,330 @@
// 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 32-bit pure Go P-256 in crypto/elliptic is still faster than the
// autogenerated code here, regrettably.
// {
// 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)
}
`
135 changes: 135 additions & 0 deletions src/crypto/elliptic/internal/fiat/p224.go
1,429 changes: 1,429 additions & 0 deletions src/crypto/elliptic/internal/fiat/p224_fiat64.go

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions src/crypto/elliptic/internal/fiat/p224_invert.go
135 changes: 135 additions & 0 deletions src/crypto/elliptic/internal/fiat/p384.go
3,004 changes: 3,004 additions & 0 deletions src/crypto/elliptic/internal/fiat/p384_fiat64.go

Large diffs are not rendered by default.

102 changes: 102 additions & 0 deletions src/crypto/elliptic/internal/fiat/p384_invert.go
170 changes: 48 additions & 122 deletions src/crypto/elliptic/internal/fiat/p521.go
6,141 changes: 4,897 additions & 1,244 deletions src/crypto/elliptic/internal/fiat/p521_fiat64.go

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions src/crypto/elliptic/internal/fiat/p521_invert.go
37 changes: 0 additions & 37 deletions src/crypto/elliptic/internal/fiat/p521_test.go

This file was deleted.

94 changes: 94 additions & 0 deletions src/crypto/elliptic/internal/nistec/nistec_test.go
@@ -0,0 +1,94 @@
// 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.

package nistec_test

import (
"crypto/elliptic/internal/nistec"
"math/rand"
"os"
"strings"
"testing"
)

func TestAllocations(t *testing.T) {
if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-noopt") {
t.Skip("skipping allocations test without relevant optimizations")
}
t.Run("P224", func(t *testing.T) {
if allocs := testing.AllocsPerRun(100, func() {
p := nistec.NewP224Generator()
scalar := make([]byte, 66)
rand.Read(scalar)
p.ScalarMult(p, scalar)
out := p.Bytes()
if _, err := p.SetBytes(out); err != nil {
t.Fatal(err)
}
}); allocs > 0 {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
})
t.Run("P384", func(t *testing.T) {
if allocs := testing.AllocsPerRun(100, func() {
p := nistec.NewP384Generator()
scalar := make([]byte, 66)
rand.Read(scalar)
p.ScalarMult(p, scalar)
out := p.Bytes()
if _, err := p.SetBytes(out); err != nil {
t.Fatal(err)
}
}); allocs > 0 {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
})
t.Run("P521", func(t *testing.T) {
if allocs := testing.AllocsPerRun(100, func() {
p := nistec.NewP521Generator()
scalar := make([]byte, 66)
rand.Read(scalar)
p.ScalarMult(p, scalar)
out := p.Bytes()
if _, err := p.SetBytes(out); err != nil {
t.Fatal(err)
}
}); allocs > 0 {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
})
}

func BenchmarkScalarMult(b *testing.B) {
b.Run("P224", func(b *testing.B) {
scalar := make([]byte, 66)
rand.Read(scalar)
p := nistec.NewP224Generator()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
p.ScalarMult(p, scalar)
}
})
b.Run("P384", func(b *testing.B) {
scalar := make([]byte, 66)
rand.Read(scalar)
p := nistec.NewP384Generator()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
p.ScalarMult(p, scalar)
}
})
b.Run("P521", func(b *testing.B) {
scalar := make([]byte, 66)
rand.Read(scalar)
p := nistec.NewP521Generator()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
p.ScalarMult(p, scalar)
}
})
}
293 changes: 293 additions & 0 deletions src/crypto/elliptic/internal/nistec/p224.go
@@ -0,0 +1,293 @@
// 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.

package nistec

import (
"crypto/elliptic/internal/fiat"
"crypto/subtle"
"errors"
)

var p224B, _ = new(fiat.P224Element).SetBytes([]byte{0xb4, 0x05, 0x0a, 0x85,
0x0c, 0x04, 0xb3, 0xab, 0xf5, 0x41, 0x32, 0x56, 0x50, 0x44, 0xb0, 0xb7,
0xd7, 0xbf, 0xd8, 0xba, 0x27, 0x0b, 0x39, 0x43, 0x23, 0x55, 0xff, 0xb4})

var p224G, _ = NewP224Point().SetBytes([]byte{0x04,
0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f, 0x32, 0x13, 0x90, 0xb9,
0x4a, 0x03, 0xc1, 0xd3, 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6,
0x11, 0x5c, 0x1d, 0x21, 0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb,
0x4c, 0x22, 0xdf, 0xe6, 0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64,
0x44, 0xd5, 0x81, 0x99, 0x85, 0x0, 0x7e, 0x34})

const p224ElementLength = 28

// P224Point is a P-224 point. The zero value is NOT valid.
type P224Point struct {
// The point is represented in projective coordinates (X:Y:Z),
// where x = X/Z and y = Y/Z.
x, y, z *fiat.P224Element
}

// NewP224Point returns a new P224Point representing the point at infinity point.
func NewP224Point() *P224Point {
return &P224Point{
x: new(fiat.P224Element),
y: new(fiat.P224Element).One(),
z: new(fiat.P224Element),
}
}

// NewP224Generator returns a new P224Point set to the canonical generator.
func NewP224Generator() *P224Point {
return (&P224Point{
x: new(fiat.P224Element),
y: new(fiat.P224Element),
z: new(fiat.P224Element),
}).Set(p224G)
}

// Set sets p = q and returns p.
func (p *P224Point) Set(q *P224Point) *P224Point {
p.x.Set(q.x)
p.y.Set(q.y)
p.z.Set(q.z)
return p
}

// SetBytes sets p to the compressed, uncompressed, or infinity value encoded in
// b, as specified in SEC 1, Version 2.0, Section 2.3.4. If the point is not on
// the curve, it returns nil and an error, and the receiver is unchanged.
// Otherwise, it returns p.
func (p *P224Point) SetBytes(b []byte) (*P224Point, error) {
switch {
// Point at infinity.
case len(b) == 1 && b[0] == 0:
return p.Set(NewP224Point()), nil

// Uncompressed form.
case len(b) == 1+2*p224ElementLength && b[0] == 4:
x, err := new(fiat.P224Element).SetBytes(b[1 : 1+p224ElementLength])
if err != nil {
return nil, err
}
y, err := new(fiat.P224Element).SetBytes(b[1+p224ElementLength:])
if err != nil {
return nil, err
}
if err := p224CheckOnCurve(x, y); err != nil {
return nil, err
}
p.x.Set(x)
p.y.Set(y)
p.z.One()
return p, nil

// Compressed form
case len(b) == 1+p224ElementLength && b[0] == 0:
return nil, errors.New("unimplemented") // TODO(filippo)

default:
return nil, errors.New("invalid P224 point encoding")
}
}

func p224CheckOnCurve(x, y *fiat.P224Element) error {
// x³ - 3x + b.
x3 := new(fiat.P224Element).Square(x)
x3.Mul(x3, x)

threeX := new(fiat.P224Element).Add(x, x)
threeX.Add(threeX, x)

x3.Sub(x3, threeX)
x3.Add(x3, p224B)

// y² = x³ - 3x + b
y2 := new(fiat.P224Element).Square(y)

if x3.Equal(y2) != 1 {
return errors.New("P224 point not on curve")
}
return nil
}

// Bytes returns the uncompressed or infinity encoding of p, as specified in
// SEC 1, Version 2.0, Section 2.3.3. Note that the encoding of the point at
// infinity is shorter than all other encodings.
func (p *P224Point) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [133]byte
return p.bytes(&out)
}

func (p *P224Point) bytes(out *[133]byte) []byte {
if p.z.IsZero() == 1 {
return append(out[:0], 0)
}

zinv := new(fiat.P224Element).Invert(p.z)
xx := new(fiat.P224Element).Mul(p.x, zinv)
yy := new(fiat.P224Element).Mul(p.y, zinv)

buf := append(out[:0], 4)
buf = append(buf, xx.Bytes()...)
buf = append(buf, yy.Bytes()...)
return buf
}

// Add sets q = p1 + p2, and returns q. The points may overlap.
func (q *P224Point) Add(p1, p2 *P224Point) *P224Point {
// Complete addition formula for a = -3 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.

t0 := new(fiat.P224Element).Mul(p1.x, p2.x) // t0 := X1 * X2
t1 := new(fiat.P224Element).Mul(p1.y, p2.y) // t1 := Y1 * Y2
t2 := new(fiat.P224Element).Mul(p1.z, p2.z) // t2 := Z1 * Z2
t3 := new(fiat.P224Element).Add(p1.x, p1.y) // t3 := X1 + Y1
t4 := new(fiat.P224Element).Add(p2.x, p2.y) // t4 := X2 + Y2
t3.Mul(t3, t4) // t3 := t3 * t4
t4.Add(t0, t1) // t4 := t0 + t1
t3.Sub(t3, t4) // t3 := t3 - t4
t4.Add(p1.y, p1.z) // t4 := Y1 + Z1
x3 := new(fiat.P224Element).Add(p2.y, p2.z) // X3 := Y2 + Z2
t4.Mul(t4, x3) // t4 := t4 * X3
x3.Add(t1, t2) // X3 := t1 + t2
t4.Sub(t4, x3) // t4 := t4 - X3
x3.Add(p1.x, p1.z) // X3 := X1 + Z1
y3 := new(fiat.P224Element).Add(p2.x, p2.z) // Y3 := X2 + Z2
x3.Mul(x3, y3) // X3 := X3 * Y3
y3.Add(t0, t2) // Y3 := t0 + t2
y3.Sub(x3, y3) // Y3 := X3 - Y3
z3 := new(fiat.P224Element).Mul(p224B, t2) // Z3 := b * t2
x3.Sub(y3, z3) // X3 := Y3 - Z3
z3.Add(x3, x3) // Z3 := X3 + X3
x3.Add(x3, z3) // X3 := X3 + Z3
z3.Sub(t1, x3) // Z3 := t1 - X3
x3.Add(t1, x3) // X3 := t1 + X3
y3.Mul(p224B, y3) // Y3 := b * Y3
t1.Add(t2, t2) // t1 := t2 + t2
t2.Add(t1, t2) // t2 := t1 + t2
y3.Sub(y3, t2) // Y3 := Y3 - t2
y3.Sub(y3, t0) // Y3 := Y3 - t0
t1.Add(y3, y3) // t1 := Y3 + Y3
y3.Add(t1, y3) // Y3 := t1 + Y3
t1.Add(t0, t0) // t1 := t0 + t0
t0.Add(t1, t0) // t0 := t1 + t0
t0.Sub(t0, t2) // t0 := t0 - t2
t1.Mul(t4, y3) // t1 := t4 * Y3
t2.Mul(t0, y3) // t2 := t0 * Y3
y3.Mul(x3, z3) // Y3 := X3 * Z3
y3.Add(y3, t2) // Y3 := Y3 + t2
x3.Mul(t3, x3) // X3 := t3 * X3
x3.Sub(x3, t1) // X3 := X3 - t1
z3.Mul(t4, z3) // Z3 := t4 * Z3
t1.Mul(t3, t0) // t1 := t3 * t0
z3.Add(z3, t1) // Z3 := Z3 + t1

q.x.Set(x3)
q.y.Set(y3)
q.z.Set(z3)
return q
}

// Double sets q = p + p, and returns q. The points may overlap.
func (q *P224Point) Double(p *P224Point) *P224Point {
// Complete addition formula for a = -3 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.

t0 := new(fiat.P224Element).Square(p.x) // t0 := X ^ 2
t1 := new(fiat.P224Element).Square(p.y) // t1 := Y ^ 2
t2 := new(fiat.P224Element).Square(p.z) // t2 := Z ^ 2
t3 := new(fiat.P224Element).Mul(p.x, p.y) // t3 := X * Y
t3.Add(t3, t3) // t3 := t3 + t3
z3 := new(fiat.P224Element).Mul(p.x, p.z) // Z3 := X * Z
z3.Add(z3, z3) // Z3 := Z3 + Z3
y3 := new(fiat.P224Element).Mul(p224B, t2) // Y3 := b * t2
y3.Sub(y3, z3) // Y3 := Y3 - Z3
x3 := new(fiat.P224Element).Add(y3, y3) // X3 := Y3 + Y3
y3.Add(x3, y3) // Y3 := X3 + Y3
x3.Sub(t1, y3) // X3 := t1 - Y3
y3.Add(t1, y3) // Y3 := t1 + Y3
y3.Mul(x3, y3) // Y3 := X3 * Y3
x3.Mul(x3, t3) // X3 := X3 * t3
t3.Add(t2, t2) // t3 := t2 + t2
t2.Add(t2, t3) // t2 := t2 + t3
z3.Mul(p224B, z3) // Z3 := b * Z3
z3.Sub(z3, t2) // Z3 := Z3 - t2
z3.Sub(z3, t0) // Z3 := Z3 - t0
t3.Add(z3, z3) // t3 := Z3 + Z3
z3.Add(z3, t3) // Z3 := Z3 + t3
t3.Add(t0, t0) // t3 := t0 + t0
t0.Add(t3, t0) // t0 := t3 + t0
t0.Sub(t0, t2) // t0 := t0 - t2
t0.Mul(t0, z3) // t0 := t0 * Z3
y3.Add(y3, t0) // Y3 := Y3 + t0
t0.Mul(p.y, p.z) // t0 := Y * Z
t0.Add(t0, t0) // t0 := t0 + t0
z3.Mul(t0, z3) // Z3 := t0 * Z3
x3.Sub(x3, z3) // X3 := X3 - Z3
z3.Mul(t0, t1) // Z3 := t0 * t1
z3.Add(z3, z3) // Z3 := Z3 + Z3
z3.Add(z3, z3) // Z3 := Z3 + Z3

q.x.Set(x3)
q.y.Set(y3)
q.z.Set(z3)
return q
}

// Select sets q to p1 if cond == 1, and to p2 if cond == 0.
func (q *P224Point) Select(p1, p2 *P224Point, cond int) *P224Point {
q.x.Select(p1.x, p2.x, cond)
q.y.Select(p1.y, p2.y, cond)
q.z.Select(p1.z, p2.z, cond)
return q
}

// ScalarMult sets p = scalar * q, and returns p.
func (p *P224Point) ScalarMult(q *P224Point, scalar []byte) *P224Point {
// table holds the first 16 multiples of q. The explicit newP224Point calls
// get inlined, letting the allocations live on the stack.
var table = [16]*P224Point{
NewP224Point(), NewP224Point(), NewP224Point(), NewP224Point(),
NewP224Point(), NewP224Point(), NewP224Point(), NewP224Point(),
NewP224Point(), NewP224Point(), NewP224Point(), NewP224Point(),
NewP224Point(), NewP224Point(), NewP224Point(), NewP224Point(),
}
for i := 1; i < 16; i++ {
table[i].Add(table[i-1], q)
}

// Instead of doing the classic double-and-add chain, we do it with a
// four-bit window: we double four times, and then add [0-15]P.
t := NewP224Point()
p.Set(NewP224Point())
for _, byte := range scalar {
p.Double(p)
p.Double(p)
p.Double(p)
p.Double(p)

for i := uint8(0); i < 16; i++ {
cond := subtle.ConstantTimeByteEq(byte>>4, i)
t.Select(table[i], t, cond)
}
p.Add(p, t)

p.Double(p)
p.Double(p)
p.Double(p)
p.Double(p)

for i := uint8(0); i < 16; i++ {
cond := subtle.ConstantTimeByteEq(byte&0b1111, i)
t.Select(table[i], t, cond)
}
p.Add(p, t)
}

return p
}
298 changes: 298 additions & 0 deletions src/crypto/elliptic/internal/nistec/p384.go
@@ -0,0 +1,298 @@
// 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.

package nistec

import (
"crypto/elliptic/internal/fiat"
"crypto/subtle"
"errors"
)

var p384B, _ = new(fiat.P384Element).SetBytes([]byte{
0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4, 0x98, 0x8e, 0x05, 0x6b,
0xe3, 0xf8, 0x2d, 0x19, 0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81, 0x41, 0x12,
0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a, 0xc6, 0x56, 0x39, 0x8d,
0x8a, 0x2e, 0xd1, 0x9d, 0x2a, 0x85, 0xc8, 0xed, 0xd3, 0xec, 0x2a, 0xef})

var p384G, _ = NewP384Point().SetBytes([]byte{0x4,
0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, 0x8e, 0xb1, 0xc7, 0x1e,
0xf3, 0x20, 0xad, 0x74, 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98,
0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, 0x55, 0x02, 0xf2, 0x5d,
0xbf, 0x55, 0x29, 0x6c, 0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7,
0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f, 0x5d, 0x9e, 0x98, 0xbf,
0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0, 0x0a, 0x60, 0xb1, 0xce,
0x1d, 0x7e, 0x81, 0x9d, 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f})

const p384ElementLength = 48

// P384Point is a P-384 point. The zero value is NOT valid.
type P384Point struct {
// The point is represented in projective coordinates (X:Y:Z),
// where x = X/Z and y = Y/Z.
x, y, z *fiat.P384Element
}

// NewP384Point returns a new P384Point representing the point at infinity point.
func NewP384Point() *P384Point {
return &P384Point{
x: new(fiat.P384Element),
y: new(fiat.P384Element).One(),
z: new(fiat.P384Element),
}
}

// NewP384Generator returns a new P384Point set to the canonical generator.
func NewP384Generator() *P384Point {
return (&P384Point{
x: new(fiat.P384Element),
y: new(fiat.P384Element),
z: new(fiat.P384Element),
}).Set(p384G)
}

// Set sets p = q and returns p.
func (p *P384Point) Set(q *P384Point) *P384Point {
p.x.Set(q.x)
p.y.Set(q.y)
p.z.Set(q.z)
return p
}

// SetBytes sets p to the compressed, uncompressed, or infinity value encoded in
// b, as specified in SEC 1, Version 2.0, Section 2.3.4. If the point is not on
// the curve, it returns nil and an error, and the receiver is unchanged.
// Otherwise, it returns p.
func (p *P384Point) SetBytes(b []byte) (*P384Point, error) {
switch {
// Point at infinity.
case len(b) == 1 && b[0] == 0:
return p.Set(NewP384Point()), nil

// Uncompressed form.
case len(b) == 1+2*p384ElementLength && b[0] == 4:
x, err := new(fiat.P384Element).SetBytes(b[1 : 1+p384ElementLength])
if err != nil {
return nil, err
}
y, err := new(fiat.P384Element).SetBytes(b[1+p384ElementLength:])
if err != nil {
return nil, err
}
if err := p384CheckOnCurve(x, y); err != nil {
return nil, err
}
p.x.Set(x)
p.y.Set(y)
p.z.One()
return p, nil

// Compressed form
case len(b) == 1+p384ElementLength && b[0] == 0:
return nil, errors.New("unimplemented") // TODO(filippo)

default:
return nil, errors.New("invalid P384 point encoding")
}
}

func p384CheckOnCurve(x, y *fiat.P384Element) error {
// x³ - 3x + b.
x3 := new(fiat.P384Element).Square(x)
x3.Mul(x3, x)

threeX := new(fiat.P384Element).Add(x, x)
threeX.Add(threeX, x)

x3.Sub(x3, threeX)
x3.Add(x3, p384B)

// y² = x³ - 3x + b
y2 := new(fiat.P384Element).Square(y)

if x3.Equal(y2) != 1 {
return errors.New("P384 point not on curve")
}
return nil
}

// Bytes returns the uncompressed or infinity encoding of p, as specified in
// SEC 1, Version 2.0, Section 2.3.3. Note that the encoding of the point at
// infinity is shorter than all other encodings.
func (p *P384Point) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [133]byte
return p.bytes(&out)
}

func (p *P384Point) bytes(out *[133]byte) []byte {
if p.z.IsZero() == 1 {
return append(out[:0], 0)
}

zinv := new(fiat.P384Element).Invert(p.z)
xx := new(fiat.P384Element).Mul(p.x, zinv)
yy := new(fiat.P384Element).Mul(p.y, zinv)

buf := append(out[:0], 4)
buf = append(buf, xx.Bytes()...)
buf = append(buf, yy.Bytes()...)
return buf
}

// Add sets q = p1 + p2, and returns q. The points may overlap.
func (q *P384Point) Add(p1, p2 *P384Point) *P384Point {
// Complete addition formula for a = -3 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.

t0 := new(fiat.P384Element).Mul(p1.x, p2.x) // t0 := X1 * X2
t1 := new(fiat.P384Element).Mul(p1.y, p2.y) // t1 := Y1 * Y2
t2 := new(fiat.P384Element).Mul(p1.z, p2.z) // t2 := Z1 * Z2
t3 := new(fiat.P384Element).Add(p1.x, p1.y) // t3 := X1 + Y1
t4 := new(fiat.P384Element).Add(p2.x, p2.y) // t4 := X2 + Y2
t3.Mul(t3, t4) // t3 := t3 * t4
t4.Add(t0, t1) // t4 := t0 + t1
t3.Sub(t3, t4) // t3 := t3 - t4
t4.Add(p1.y, p1.z) // t4 := Y1 + Z1
x3 := new(fiat.P384Element).Add(p2.y, p2.z) // X3 := Y2 + Z2
t4.Mul(t4, x3) // t4 := t4 * X3
x3.Add(t1, t2) // X3 := t1 + t2
t4.Sub(t4, x3) // t4 := t4 - X3
x3.Add(p1.x, p1.z) // X3 := X1 + Z1
y3 := new(fiat.P384Element).Add(p2.x, p2.z) // Y3 := X2 + Z2
x3.Mul(x3, y3) // X3 := X3 * Y3
y3.Add(t0, t2) // Y3 := t0 + t2
y3.Sub(x3, y3) // Y3 := X3 - Y3
z3 := new(fiat.P384Element).Mul(p384B, t2) // Z3 := b * t2
x3.Sub(y3, z3) // X3 := Y3 - Z3
z3.Add(x3, x3) // Z3 := X3 + X3
x3.Add(x3, z3) // X3 := X3 + Z3
z3.Sub(t1, x3) // Z3 := t1 - X3
x3.Add(t1, x3) // X3 := t1 + X3
y3.Mul(p384B, y3) // Y3 := b * Y3
t1.Add(t2, t2) // t1 := t2 + t2
t2.Add(t1, t2) // t2 := t1 + t2
y3.Sub(y3, t2) // Y3 := Y3 - t2
y3.Sub(y3, t0) // Y3 := Y3 - t0
t1.Add(y3, y3) // t1 := Y3 + Y3
y3.Add(t1, y3) // Y3 := t1 + Y3
t1.Add(t0, t0) // t1 := t0 + t0
t0.Add(t1, t0) // t0 := t1 + t0
t0.Sub(t0, t2) // t0 := t0 - t2
t1.Mul(t4, y3) // t1 := t4 * Y3
t2.Mul(t0, y3) // t2 := t0 * Y3
y3.Mul(x3, z3) // Y3 := X3 * Z3
y3.Add(y3, t2) // Y3 := Y3 + t2
x3.Mul(t3, x3) // X3 := t3 * X3
x3.Sub(x3, t1) // X3 := X3 - t1
z3.Mul(t4, z3) // Z3 := t4 * Z3
t1.Mul(t3, t0) // t1 := t3 * t0
z3.Add(z3, t1) // Z3 := Z3 + t1

q.x.Set(x3)
q.y.Set(y3)
q.z.Set(z3)
return q
}

// Double sets q = p + p, and returns q. The points may overlap.
func (q *P384Point) Double(p *P384Point) *P384Point {
// Complete addition formula for a = -3 from "Complete addition formulas for
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.

t0 := new(fiat.P384Element).Square(p.x) // t0 := X ^ 2
t1 := new(fiat.P384Element).Square(p.y) // t1 := Y ^ 2
t2 := new(fiat.P384Element).Square(p.z) // t2 := Z ^ 2
t3 := new(fiat.P384Element).Mul(p.x, p.y) // t3 := X * Y
t3.Add(t3, t3) // t3 := t3 + t3
z3 := new(fiat.P384Element).Mul(p.x, p.z) // Z3 := X * Z
z3.Add(z3, z3) // Z3 := Z3 + Z3
y3 := new(fiat.P384Element).Mul(p384B, t2) // Y3 := b * t2
y3.Sub(y3, z3) // Y3 := Y3 - Z3
x3 := new(fiat.P384Element).Add(y3, y3) // X3 := Y3 + Y3
y3.Add(x3, y3) // Y3 := X3 + Y3
x3.Sub(t1, y3) // X3 := t1 - Y3
y3.Add(t1, y3) // Y3 := t1 + Y3
y3.Mul(x3, y3) // Y3 := X3 * Y3
x3.Mul(x3, t3) // X3 := X3 * t3
t3.Add(t2, t2) // t3 := t2 + t2
t2.Add(t2, t3) // t2 := t2 + t3
z3.Mul(p384B, z3) // Z3 := b * Z3
z3.Sub(z3, t2) // Z3 := Z3 - t2
z3.Sub(z3, t0) // Z3 := Z3 - t0
t3.Add(z3, z3) // t3 := Z3 + Z3
z3.Add(z3, t3) // Z3 := Z3 + t3
t3.Add(t0, t0) // t3 := t0 + t0
t0.Add(t3, t0) // t0 := t3 + t0
t0.Sub(t0, t2) // t0 := t0 - t2
t0.Mul(t0, z3) // t0 := t0 * Z3
y3.Add(y3, t0) // Y3 := Y3 + t0
t0.Mul(p.y, p.z) // t0 := Y * Z
t0.Add(t0, t0) // t0 := t0 + t0
z3.Mul(t0, z3) // Z3 := t0 * Z3
x3.Sub(x3, z3) // X3 := X3 - Z3
z3.Mul(t0, t1) // Z3 := t0 * t1
z3.Add(z3, z3) // Z3 := Z3 + Z3
z3.Add(z3, z3) // Z3 := Z3 + Z3

q.x.Set(x3)
q.y.Set(y3)
q.z.Set(z3)
return q
}

// Select sets q to p1 if cond == 1, and to p2 if cond == 0.
func (q *P384Point) Select(p1, p2 *P384Point, cond int) *P384Point {
q.x.Select(p1.x, p2.x, cond)
q.y.Select(p1.y, p2.y, cond)
q.z.Select(p1.z, p2.z, cond)
return q
}

// ScalarMult sets p = scalar * q, and returns p.
func (p *P384Point) ScalarMult(q *P384Point, scalar []byte) *P384Point {
// table holds the first 16 multiples of q. The explicit newP384Point calls
// get inlined, letting the allocations live on the stack.
var table = [16]*P384Point{
NewP384Point(), NewP384Point(), NewP384Point(), NewP384Point(),
NewP384Point(), NewP384Point(), NewP384Point(), NewP384Point(),
NewP384Point(), NewP384Point(), NewP384Point(), NewP384Point(),
NewP384Point(), NewP384Point(), NewP384Point(), NewP384Point(),
}
for i := 1; i < 16; i++ {
table[i].Add(table[i-1], q)
}

// Instead of doing the classic double-and-add chain, we do it with a
// four-bit window: we double four times, and then add [0-15]P.
t := NewP384Point()
p.Set(NewP384Point())
for _, byte := range scalar {
p.Double(p)
p.Double(p)
p.Double(p)
p.Double(p)

for i := uint8(0); i < 16; i++ {
cond := subtle.ConstantTimeByteEq(byte>>4, i)
t.Select(table[i], t, cond)
}
p.Add(p, t)

p.Double(p)
p.Double(p)
p.Double(p)
p.Double(p)

for i := uint8(0); i < 16; i++ {
cond := subtle.ConstantTimeByteEq(byte&0b1111, i)
t.Select(table[i], t, cond)
}
p.Add(p, t)
}

return p
}
6 changes: 5 additions & 1 deletion src/crypto/elliptic/internal/nistec/p521.go
Expand Up @@ -58,7 +58,11 @@ func NewP521Point() *P521Point {

// NewP521Generator returns a new P521Point set to the canonical generator.
func NewP521Generator() *P521Point {
return NewP521Point().Set(p521G)
return (&P521Point{
x: new(fiat.P521Element),
y: new(fiat.P521Element),
z: new(fiat.P521Element),
}).Set(p521G)
}

// Set sets p = q and returns p.
Expand Down
44 changes: 0 additions & 44 deletions src/crypto/elliptic/internal/nistec/p521_test.go

This file was deleted.

785 changes: 91 additions & 694 deletions src/crypto/elliptic/p224.go

Large diffs are not rendered by default.

306 changes: 1 addition & 305 deletions src/crypto/elliptic/p224_test.go
Expand Up @@ -8,313 +8,9 @@ import (
"encoding/hex"
"fmt"
"math/big"
"math/bits"
"math/rand"
"reflect"
"testing"
"testing/quick"
)

var toFromBigTests = []string{
"0",
"1",
"23",
"b70e0cb46bb4bf7f321390b94a03c1d356c01122343280d6105c1d21",
"706a46d476dcb76798e6046d89474788d164c18032d268fd10704fa6",
}

func p224AlternativeToBig(in *p224FieldElement) *big.Int {
ret := new(big.Int)
tmp := new(big.Int)

for i := len(in) - 1; i >= 0; i-- {
ret.Lsh(ret, 28)
tmp.SetInt64(int64(in[i]))
ret.Add(ret, tmp)
}
ret.Mod(ret, P224().Params().P)
return ret
}

func TestP224ToFromBig(t *testing.T) {
for i, test := range toFromBigTests {
n, _ := new(big.Int).SetString(test, 16)
var x p224FieldElement
p224FromBig(&x, n)
m := p224ToBig(&x)
if n.Cmp(m) != 0 {
t.Errorf("#%d: %x != %x", i, n, m)
}
q := p224AlternativeToBig(&x)
if n.Cmp(q) != 0 {
t.Errorf("#%d: %x != %x (alternative)", i, n, q)
}
}
}

// quickCheckConfig32 will make each quickcheck test run (32 * -quickchecks)
// times. The default value of -quickchecks is 100.
var quickCheckConfig32 = &quick.Config{MaxCountScale: 32}

// weirdLimbs can be combined to generate a range of edge-case field elements.
var weirdLimbs = [...]uint32{
0, 1, (1 << 29) - 1,
(1 << 12), (1 << 12) - 1,
(1 << 28), (1 << 28) - 1,
}

func generateLimb(rand *rand.Rand) uint32 {
const bottom29Bits = 0x1fffffff
n := rand.Intn(len(weirdLimbs) + 3)
switch n {
case len(weirdLimbs):
// Random value.
return uint32(rand.Int31n(1 << 29))
case len(weirdLimbs) + 1:
// Sum of two values.
k := generateLimb(rand) + generateLimb(rand)
return k & bottom29Bits
case len(weirdLimbs) + 2:
// Difference of two values.
k := generateLimb(rand) - generateLimb(rand)
return k & bottom29Bits
default:
return weirdLimbs[n]
}
}

func (p224FieldElement) Generate(rand *rand.Rand, size int) reflect.Value {
return reflect.ValueOf(p224FieldElement{
generateLimb(rand),
generateLimb(rand),
generateLimb(rand),
generateLimb(rand),
generateLimb(rand),
generateLimb(rand),
generateLimb(rand),
generateLimb(rand),
})
}

func isInBounds(x *p224FieldElement) bool {
return bits.Len32(x[0]) <= 29 &&
bits.Len32(x[1]) <= 29 &&
bits.Len32(x[2]) <= 29 &&
bits.Len32(x[3]) <= 29 &&
bits.Len32(x[4]) <= 29 &&
bits.Len32(x[5]) <= 29 &&
bits.Len32(x[6]) <= 29 &&
bits.Len32(x[7]) <= 29
}

func TestP224Mul(t *testing.T) {
mulMatchesBigInt := func(a, b, out p224FieldElement) bool {
var tmp p224LargeFieldElement
p224Mul(&out, &a, &b, &tmp)

exp := new(big.Int).Mul(p224AlternativeToBig(&a), p224AlternativeToBig(&b))
exp.Mod(exp, P224().Params().P)
got := p224AlternativeToBig(&out)
if exp.Cmp(got) != 0 || !isInBounds(&out) {
t.Logf("a = %x", a)
t.Logf("b = %x", b)
t.Logf("p224Mul(a, b) = %x = %v", out, got)
t.Logf("a * b = %v", exp)
return false
}

return true
}

a := p224FieldElement{0xfffffff, 0xfffffff, 0xf00ffff, 0x20f, 0x0, 0x0, 0x0, 0x0}
b := p224FieldElement{1, 0, 0, 0, 0, 0, 0, 0}
if !mulMatchesBigInt(a, b, p224FieldElement{}) {
t.Fail()
}

if err := quick.Check(mulMatchesBigInt, quickCheckConfig32); err != nil {
t.Error(err)
}
}

func TestP224Square(t *testing.T) {
squareMatchesBigInt := func(a, out p224FieldElement) bool {
var tmp p224LargeFieldElement
p224Square(&out, &a, &tmp)

exp := p224AlternativeToBig(&a)
exp.Mul(exp, exp)
exp.Mod(exp, P224().Params().P)
got := p224AlternativeToBig(&out)
if exp.Cmp(got) != 0 || !isInBounds(&out) {
t.Logf("a = %x", a)
t.Logf("p224Square(a, b) = %x = %v", out, got)
t.Logf("a * a = %v", exp)
return false
}

return true
}

if err := quick.Check(squareMatchesBigInt, quickCheckConfig32); err != nil {
t.Error(err)
}
}

func TestP224Add(t *testing.T) {
addMatchesBigInt := func(a, b, out p224FieldElement) bool {
p224Add(&out, &a, &b)

exp := new(big.Int).Add(p224AlternativeToBig(&a), p224AlternativeToBig(&b))
exp.Mod(exp, P224().Params().P)
got := p224AlternativeToBig(&out)
if exp.Cmp(got) != 0 {
t.Logf("a = %x", a)
t.Logf("b = %x", b)
t.Logf("p224Add(a, b) = %x = %v", out, got)
t.Logf("a + b = %v", exp)
return false
}

return true
}

if err := quick.Check(addMatchesBigInt, quickCheckConfig32); err != nil {
t.Error(err)
}
}

func TestP224Reduce(t *testing.T) {
reduceMatchesBigInt := func(a p224FieldElement) bool {
out := a
// TODO: generate higher values for functions like p224Reduce that are
// expected to work with higher input bounds.
p224Reduce(&out)

exp := p224AlternativeToBig(&a)
got := p224AlternativeToBig(&out)
if exp.Cmp(got) != 0 || !isInBounds(&out) {
t.Logf("a = %x = %v", a, exp)
t.Logf("p224Reduce(a) = %x = %v", out, got)
return false
}

return true
}

if err := quick.Check(reduceMatchesBigInt, quickCheckConfig32); err != nil {
t.Error(err)
}
}

func TestP224Contract(t *testing.T) {
contractMatchesBigInt := func(a, out p224FieldElement) bool {
p224Contract(&out, &a)

exp := p224AlternativeToBig(&a)
got := p224AlternativeToBig(&out)
if exp.Cmp(got) != 0 {
t.Logf("a = %x = %v", a, exp)
t.Logf("p224Contract(a) = %x = %v", out, got)
return false
}

// Check that out < P.
for i := range p224P {
k := 8 - i - 1
if out[k] > p224P[k] {
t.Logf("p224Contract(a) = %x", out)
return false
}
if out[k] < p224P[k] {
return true
}
}
t.Logf("p224Contract(a) = %x", out)
return false
}

if !contractMatchesBigInt(p224P, p224FieldElement{}) {
t.Error("p224Contract(p) is broken")
}
pMinus1 := p224FieldElement{0, 0, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
if !contractMatchesBigInt(pMinus1, p224FieldElement{}) {
t.Error("p224Contract(p - 1) is broken")
}
// Check that we can handle input above p, but lowest limb zero.
a := p224FieldElement{0, 1, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
if !contractMatchesBigInt(a, p224FieldElement{}) {
t.Error("p224Contract(p + 2²⁸) is broken")
}
// Check that we can handle input above p, but lowest three limbs zero.
b := p224FieldElement{0, 0, 0, 0xffff001, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
if !contractMatchesBigInt(b, p224FieldElement{}) {
t.Error("p224Contract(p + 2⁸⁴) is broken")
}

if err := quick.Check(contractMatchesBigInt, quickCheckConfig32); err != nil {
t.Error(err)
}
}

func TestP224IsZero(t *testing.T) {
if got := p224IsZero(&p224FieldElement{}); got != 1 {
t.Errorf("p224IsZero(0) = %d, expected 1", got)
}
if got := p224IsZero(&p224P); got != 1 {
t.Errorf("p224IsZero(p) = %d, expected 1", got)
}
if got := p224IsZero(&p224FieldElement{1}); got != 0 {
t.Errorf("p224IsZero(1) = %d, expected 0", got)
}

isZeroMatchesBigInt := func(a p224FieldElement) bool {
isZero := p224IsZero(&a)

big := p224AlternativeToBig(&a)
if big.Sign() == 0 && isZero != 1 {
return false
}
if big.Sign() != 0 && isZero != 0 {
return false
}
return true
}

if err := quick.Check(isZeroMatchesBigInt, quickCheckConfig32); err != nil {
t.Error(err)
}
}

func TestP224Invert(t *testing.T) {
var out p224FieldElement

p224Invert(&out, &p224FieldElement{})
if got := p224IsZero(&out); got != 1 {
t.Errorf("p224Invert(0) = %x, expected 0", out)
}

p224Invert(&out, &p224P)
if got := p224IsZero(&out); got != 1 {
t.Errorf("p224Invert(p) = %x, expected 0", out)
}

p224Invert(&out, &p224FieldElement{1})
p224Contract(&out, &out)
if out != (p224FieldElement{1}) {
t.Errorf("p224Invert(1) = %x, expected 1", out)
}

var tmp p224LargeFieldElement
a := p224FieldElement{1, 2, 3, 4, 5, 6, 7, 8}
p224Invert(&out, &a)
p224Mul(&out, &out, &a, &tmp)
p224Contract(&out, &out)
if out != (p224FieldElement{1}) {
t.Errorf("p224Invert(a) * a = %x, expected 1", out)
}
}

type baseMultTest struct {
k string
x, y string
Expand Down Expand Up @@ -602,7 +298,7 @@ func TestP224BaseMult(t *testing.T) {

func TestP224GenericBaseMult(t *testing.T) {
// We use the P224 CurveParams directly in order to test the generic implementation.
p224 := P224().Params()
p224 := genericParamsForCurve(P224())
for i, e := range p224BaseMultTests {
k, ok := new(big.Int).SetString(e.k, 10)
if !ok {
Expand Down
3 changes: 3 additions & 0 deletions src/crypto/elliptic/p256.go
Expand Up @@ -209,6 +209,8 @@ var p256Precomputed = [p256Limbs * 2 * 15 * 2]uint32{

// Field element operations:

const bottom28Bits = 0xfffffff

// nonZeroToAllOnes returns:
// 0xffffffff for 0 < x <= 2**31
// 0 for x == 0 or x > 2**31.
Expand Down Expand Up @@ -269,6 +271,7 @@ const (
two30m2 = 1<<30 - 1<<2
two30p13m2 = 1<<30 + 1<<13 - 1<<2
two31m2 = 1<<31 - 1<<2
two31m3 = 1<<31 - 1<<3
two31p24m2 = 1<<31 + 1<<24 - 1<<2
two30m27m2 = 1<<30 - 1<<27 - 1<<2
)
Expand Down
19 changes: 1 addition & 18 deletions src/crypto/elliptic/p256_test.go
Expand Up @@ -34,7 +34,7 @@ var p256MultTests = []scalarMultTest{

func TestP256BaseMult(t *testing.T) {
p256 := P256()
p256Generic := p256.Params()
p256Generic := genericParamsForCurve(p256)

scalars := make([]*big.Int, 0, len(p224BaseMultTests)+1)
for _, e := range p224BaseMultTests {
Expand All @@ -60,23 +60,6 @@ func TestP256BaseMult(t *testing.T) {

func TestP256Mult(t *testing.T) {
p256 := P256()
p256Generic := p256.Params()

for i, e := range p224BaseMultTests {
x, _ := new(big.Int).SetString(e.x, 16)
y, _ := new(big.Int).SetString(e.y, 16)
k, _ := new(big.Int).SetString(e.k, 10)

xx, yy := p256.ScalarMult(x, y, k.Bytes())
xx2, yy2 := p256Generic.ScalarMult(x, y, k.Bytes())
if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 {
t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, xx, yy, xx2, yy2)
}
if testing.Short() && i > 5 {
break
}
}

for i, e := range p256MultTests {
x, _ := new(big.Int).SetString(e.xIn, 16)
y, _ := new(big.Int).SetString(e.yIn, 16)
Expand Down
141 changes: 141 additions & 0 deletions src/crypto/elliptic/p384.go
@@ -0,0 +1,141 @@
// Copyright 2013 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.

package elliptic

import (
"crypto/elliptic/internal/nistec"
"crypto/rand"
"math/big"
)

// p384Curve is a Curve implementation based on nistec.P384Point.
//
// It's a wrapper that exposes the big.Int-based Curve interface and encodes the
// legacy idiosyncrasies it requires, such as invalid and infinity point
// handling.
//
// To interact with the nistec package, points are encoded into and decoded from
// properly formatted byte slices. All big.Int use is limited to this package.
// Encoding and decoding is 1/1000th of the runtime of a scalar multiplication,
// so the overhead is acceptable.
type p384Curve struct {
params *CurveParams
}

var p384 p384Curve
var _ Curve = p384

func initP384() {
p384.params = &CurveParams{
Name: "P-384",
BitSize: 384,
// FIPS 186-4, section D.1.2.4
P: bigFromDecimal("394020061963944792122790401001436138050797392704654" +
"46667948293404245721771496870329047266088258938001861606973112319"),
N: bigFromDecimal("394020061963944792122790401001436138050797392704654" +
"46667946905279627659399113263569398956308152294913554433653942643"),
B: bigFromHex("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088" +
"f5013875ac656398d8a2ed19d2a85c8edd3ec2aef"),
Gx: bigFromHex("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741" +
"e082542a385502f25dbf55296c3a545e3872760ab7"),
Gy: bigFromHex("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da31" +
"13b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"),
}
}

func (curve p384Curve) Params() *CurveParams {
return curve.params
}

func (curve p384Curve) IsOnCurve(x, y *big.Int) bool {
// IsOnCurve is documented to reject (0, 0), the conventional point at
// infinity, which however is accepted by p384PointFromAffine.
if x.Sign() == 0 && y.Sign() == 0 {
return false
}
_, ok := p384PointFromAffine(x, y)
return ok
}

func p384PointFromAffine(x, y *big.Int) (p *nistec.P384Point, ok bool) {
// (0, 0) is by convention the point at infinity, which can't be represented
// in affine coordinates. Marshal incorrectly encodes it as an uncompressed
// point, which SetBytes would correctly reject. See Issue 37294.
if x.Sign() == 0 && y.Sign() == 0 {
return nistec.NewP384Point(), true
}
if x.BitLen() > 384 || y.BitLen() > 384 {
return nil, false
}
p, err := nistec.NewP384Point().SetBytes(Marshal(P384(), x, y))
if err != nil {
return nil, false
}
return p, true
}

func p384PointToAffine(p *nistec.P384Point) (x, y *big.Int) {
out := p.Bytes()
if len(out) == 1 && out[0] == 0 {
// This is the correct encoding of the point at infinity, which
// Unmarshal does not support. See Issue 37294.
return new(big.Int), new(big.Int)
}
x, y = Unmarshal(P384(), out)
if x == nil {
panic("crypto/elliptic: internal error: Unmarshal rejected a valid point encoding")
}
return x, y
}

// p384RandomPoint returns a random point on the curve. It's used when Add,
// Double, or ScalarMult are fed a point not on the curve, which is undefined
// behavior. Originally, we used to do the math on it anyway (which allows
// invalid curve attacks) and relied on the caller and Unmarshal to avoid this
// happening in the first place. Now, we just can't construct a nistec.P384Point
// for an invalid pair of coordinates, because that API is safer. If we panic,
// we risk introducing a DoS. If we return nil, we risk a panic. If we return
// the input, ecdsa.Verify might fail open. The safest course seems to be to
// return a valid, random point, which hopefully won't help the attacker.
func p384RandomPoint() (x, y *big.Int) {
_, x, y, err := GenerateKey(P384(), rand.Reader)
if err != nil {
panic("crypto/elliptic: failed to generate random point")
}
return x, y
}

func (p384Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
p1, ok := p384PointFromAffine(x1, y1)
if !ok {
return p384RandomPoint()
}
p2, ok := p384PointFromAffine(x2, y2)
if !ok {
return p384RandomPoint()
}
return p384PointToAffine(p1.Add(p1, p2))
}

func (p384Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
p, ok := p384PointFromAffine(x1, y1)
if !ok {
return p384RandomPoint()
}
return p384PointToAffine(p.Double(p))
}

func (p384Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
p, ok := p384PointFromAffine(Bx, By)
if !ok {
return p384RandomPoint()
}
return p384PointToAffine(p.ScalarMult(p, scalar))
}

func (p384Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
p := nistec.NewP384Generator()
return p384PointToAffine(p.ScalarMult(p, scalar))
}
8 changes: 4 additions & 4 deletions src/crypto/elliptic/p521.go
Expand Up @@ -112,7 +112,7 @@ func p521RandomPoint() (x, y *big.Int) {
return x, y
}

func (curve p521Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
func (p521Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
p1, ok := p521PointFromAffine(x1, y1)
if !ok {
return p521RandomPoint()
Expand All @@ -124,23 +124,23 @@ func (curve p521Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
return p521PointToAffine(p1.Add(p1, p2))
}

func (curve p521Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
func (p521Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
p, ok := p521PointFromAffine(x1, y1)
if !ok {
return p521RandomPoint()
}
return p521PointToAffine(p.Double(p))
}

func (curve p521Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
func (p521Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
p, ok := p521PointFromAffine(Bx, By)
if !ok {
return p521RandomPoint()
}
return p521PointToAffine(p.ScalarMult(p, scalar))
}

func (curve p521Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
func (p521Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
p := nistec.NewP521Generator()
return p521PointToAffine(p.ScalarMult(p, scalar))
}
Expand Down