crypto/elliptic: port P-224 and P-384 to fiat-crypto

Also, adopt addchain code generation for field inversion, and switch
P-521 to Montgomery multiplication, which is significantly slower but
allows us to reuse the P-224/P-256/P-384 wrapper code. No one uses P-521
anyway, and it's still faster than it was in Go 1.16.

Removed a portion of tests that ran the P-224 vectors against P-256,
for some reason.

Sadly, fiat-crypto is not fast enough to replace the generic 32-bit
P-256 implementation (just yet?).

A change in visible behavior is that we literally can't internally
operate on invalid curve points anymore (yay!) but the crypto/elliptic
API locked us into accepting any pair of integers for
Add/Double/ScalarMult and return no error (sigh), although of course
that's undefined behavior. Panics are always regretted. Returning nil
leads to panics. A fixed point might be exploited. The most reasonable
solution felt to return a made up random point, which is not that
different from an off-curve point but leaks less.

name                                  old time/op    new time/op    delta
pkg:crypto/elliptic goos:darwin goarch:arm64
ScalarBaseMult/P224-8                    573µs ± 0%     146µs ± 0%   -74.56%  (p=0.000 n=7+9)
ScalarMult/P224-8                        574µs ± 0%     152µs ± 5%   -73.58%  (p=0.000 n=7+10)
MarshalUnmarshal/P224/Uncompressed-8     664ns ± 0%     481ns ± 1%   -27.64%  (p=0.000 n=8+10)
MarshalUnmarshal/P224/Compressed-8       666ns ± 1%     480ns ± 0%   -27.92%  (p=0.000 n=10+10)
pkg:crypto/ecdsa goos:darwin goarch:arm64
Sign/P224-8                              597µs ± 0%     169µs ± 2%   -71.71%  (p=0.000 n=10+9)
Verify/P224-8                           1.18ms ± 1%    0.32ms ± 5%   -72.81%  (p=0.000 n=10+10)
GenerateKey/P224-8                       577µs ± 0%     147µs ± 0%   -74.51%  (p=0.000 n=8+8)

name                                  old time/op    new time/op    delta
pkg:crypto/elliptic goos:darwin goarch:arm64
ScalarBaseMult/P384-8                   2.01ms ± 2%    0.50ms ± 0%  -75.00%  (p=0.000 n=10+8)
ScalarMult/P384-8                       2.02ms ± 3%    0.51ms ± 3%  -74.64%  (p=0.000 n=10+10)
MarshalUnmarshal/P384/Uncompressed-8    1.09µs ± 1%    0.76µs ± 0%  -30.27%  (p=0.000 n=10+9)
MarshalUnmarshal/P384/Compressed-8      1.08µs ± 0%    0.76µs ± 1%  -29.86%  (p=0.000 n=8+10)
pkg:crypto/ecdsa goos:darwin goarch:arm64
Sign/P384-8                             2.06ms ± 1%    0.56ms ± 2%  -72.76%  (p=0.000 n=10+10)
Verify/P384-8                           4.06ms ± 2%    1.08ms ± 0%  -73.49%  (p=0.000 n=10+8)
GenerateKey/P384-8                      2.01ms ± 1%    0.51ms ± 3%  -74.65%  (p=0.000 n=10+10)

name                                  old time/op    new time/op    delta
pkg:crypto/elliptic goos:darwin goarch:arm64
ScalarBaseMult/P521-8                    715µs ± 6%    1525µs ± 4%  +113.39%  (p=0.000 n=10+10)
ScalarMult/P521-8                        698µs ± 1%    1543µs ± 1%  +120.99%  (p=0.000 n=9+9)
MarshalUnmarshal/P521/Uncompressed-8     797ns ± 0%    1296ns ± 0%   +62.65%  (p=0.000 n=10+9)
MarshalUnmarshal/P521/Compressed-8       798ns ± 0%    1299ns ± 1%   +62.82%  (p=0.000 n=8+10)
pkg:crypto/ecdsa goos:darwin goarch:arm64
Sign/P521-8                              810µs ± 3%    1645µs ± 0%  +103.03%  (p=0.000 n=10+10)
Verify/P521-8                           1.42ms ± 1%    3.19ms ± 1%  +125.28%  (p=0.000 n=10+8)
GenerateKey/P521-8                       698µs ± 1%    1549µs ± 0%  +121.87%  (p=0.000 n=10+7)

Updates #40171

Change-Id: I34edf5002b5e9fad0ebb6c1e2119fb123ea6d18f
Reviewed-on: https://go-review.googlesource.com/c/go/+/360014
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Trust: Filippo Valsorda <filippo@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Filippo Valsorda 2021-10-30 00:27:51 -04:00
parent 53bab198d9
commit 93bab8a2f9
28 changed files with 11331 additions and 2552 deletions

View File

@ -89,6 +89,9 @@ func TestStmtLines(t *testing.T) {
if pkgname == "runtime" {
continue
}
if pkgname == "crypto/elliptic/internal/fiat" {
continue // golang.org/issue/49372
}
if e.Val(dwarf.AttrStmtList) == nil {
continue
}

View File

@ -85,7 +85,7 @@ func (curve *CurveParams) polynomial(x *big.Int) *big.Int {
func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p384, p521); ok {
return specific.IsOnCurve(x, y)
}
@ -128,7 +128,7 @@ func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.
func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p384, p521); ok {
return specific.Add(x1, y1, x2, y2)
}
@ -218,7 +218,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p384, p521); ok {
return specific.Double(x1, y1)
}
@ -290,7 +290,7 @@ func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int,
func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p256, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p256, p384, p521); ok {
return specific.ScalarMult(Bx, By, k)
}
@ -313,7 +313,7 @@ func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.
func (curve *CurveParams) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p256, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p256, p384, p521); ok {
return specific.ScalarBaseMult(k)
}
@ -431,7 +431,6 @@ func UnmarshalCompressed(curve Curve, data []byte) (x, y *big.Int) {
}
var initonce sync.Once
var p384 *CurveParams
func initAll() {
initP224()
@ -440,15 +439,16 @@ func initAll() {
initP521()
}
func initP384() {
// See FIPS 186-3, section D.2.4
p384 = &CurveParams{Name: "P-384"}
p384.P, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", 10)
p384.N, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643", 10)
p384.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16)
p384.Gx, _ = new(big.Int).SetString("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16)
p384.Gy, _ = new(big.Int).SetString("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16)
p384.BitSize = 384
// P224 returns a Curve which implements NIST P-224 (FIPS 186-3, section D.2.2),
// also known as secp224r1. The CurveParams.Name of this Curve is "P-224".
//
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
//
// The cryptographic operations are implemented using constant-time algorithms.
func P224() Curve {
initonce.Do(initAll)
return p224
}
// P256 returns a Curve which implements NIST P-256 (FIPS 186-3, section D.2.3),
@ -470,7 +470,7 @@ func P256() Curve {
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
//
// The cryptographic operations do not use constant-time algorithms.
// The cryptographic operations are implemented using constant-time algorithms.
func P384() Curve {
initonce.Do(initAll)
return p384

View File

@ -14,9 +14,8 @@ import (
// genericParamsForCurve returns the dereferenced CurveParams for
// the specified curve. This is used to avoid the logic for
// upgrading a curve to it's specific implementation, forcing
// usage of the generic implementation. This is only relevant
// for the P224, P256, and P521 curves.
// upgrading a curve to its specific implementation, forcing
// usage of the generic implementation.
func genericParamsForCurve(c Curve) *CurveParams {
d := *(c.Params())
return &d

View File

@ -4,9 +4,9 @@
FROM coqorg/coq:8.13.2
RUN git clone https://github.com/mit-plv/fiat-crypto
RUN cd fiat-crypto && git checkout c076f3550bea2bb7f4cb5766a32594b9e67694f2
RUN cd fiat-crypto && git submodule update --init --recursive
RUN git clone https://github.com/mit-plv/fiat-crypto && cd fiat-crypto && \
git checkout 23d2dbc4ab897d14bde4404f70cd6991635f9c01 && \
git submodule update --init --recursive
RUN cd fiat-crypto && eval $(opam env) && make -j4 standalone-ocaml SKIP_BEDROCK2=1
ENTRYPOINT ["fiat-crypto/src/ExtractionOCaml/unsaturated_solinas"]
ENV PATH /home/coq/fiat-crypto/src/ExtractionOCaml:$PATH

View File

@ -1,17 +1,12 @@
The code in this package was autogenerated by the fiat-crypto project
at commit c076f3550 from a formally verified model.
at version v0.0.9 from a formally verified model, and by the addchain
project at a recent tip version.
docker build -t fiat-crypto:c076f3550 .
docker run fiat-crypto:c076f3550 --lang Go --no-wide-int --cmovznz-by-mul \
--internal-static --public-function-case camelCase --public-type-case camelCase \
--private-function-case camelCase --private-type-case camelCase \
--no-prefix-fiat --package-name fiat --doc-text-before-function-name '' \
--doc-prepend-header 'Code generated by Fiat Cryptography. DO NOT EDIT.' \
--doc-newline-before-package-declaration p521 64 9 '2^521 - 1' \
carry_mul carry_square carry add sub to_bytes from_bytes selectznz \
> p521_fiat64.go
docker build -t fiat-crypto:v0.0.9 .
go install github.com/mmcloughlin/addchain/cmd/addchain@v0.3.1-0.20211027081849-6a7d3decbe08
../../../../../bin/go run generate.go
It comes under the following license.
fiat-crypto code comes under the following license.
Copyright (c) 2015-2020 The fiat-crypto Authors. All rights reserved.

View File

@ -0,0 +1,64 @@
// 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 fiat_test
import (
"crypto/elliptic/internal/fiat"
"testing"
)
func BenchmarkMul(b *testing.B) {
b.Run("P224", func(b *testing.B) {
v := new(fiat.P224Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Mul(v, v)
}
})
b.Run("P384", func(b *testing.B) {
v := new(fiat.P384Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Mul(v, v)
}
})
b.Run("P521", func(b *testing.B) {
v := new(fiat.P521Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Mul(v, v)
}
})
}
func BenchmarkSquare(b *testing.B) {
b.Run("P224", func(b *testing.B) {
v := new(fiat.P224Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Square(v)
}
})
b.Run("P384", func(b *testing.B) {
v := new(fiat.P384Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Square(v)
}
})
b.Run("P521", func(b *testing.B) {
v := new(fiat.P521Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Square(v)
}
})
}

View File

@ -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)
}
`

View File

@ -0,0 +1,135 @@
// 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"
)
// P224Element is an integer modulo 2^224 - 2^96 + 1.
//
// The zero value is a valid zero element.
type P224Element struct {
// Values are represented internally always in the Montgomery domain, and
// converted in Bytes and SetBytes.
x p224MontgomeryDomainFieldElement
}
const p224ElementLen = 28
type p224UntypedFieldElement = [4]uint64
// One sets e = 1, and returns e.
func (e *P224Element) One() *P224Element {
p224SetOne(&e.x)
return e
}
// Equal returns 1 if e == t, and zero otherwise.
func (e *P224Element) Equal(t *P224Element) int {
eBytes := e.Bytes()
tBytes := t.Bytes()
return subtle.ConstantTimeCompare(eBytes, tBytes)
}
var p224ZeroEncoding = new(P224Element).Bytes()
// IsZero returns 1 if e == 0, and zero otherwise.
func (e *P224Element) IsZero() int {
eBytes := e.Bytes()
return subtle.ConstantTimeCompare(eBytes, p224ZeroEncoding)
}
// Set sets e = t, and returns e.
func (e *P224Element) Set(t *P224Element) *P224Element {
e.x = t.x
return e
}
// Bytes returns the 28-byte big-endian encoding of e.
func (e *P224Element) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [p224ElementLen]byte
return e.bytes(&out)
}
func (e *P224Element) bytes(out *[p224ElementLen]byte) []byte {
var tmp p224NonMontgomeryDomainFieldElement
p224FromMontgomery(&tmp, &e.x)
p224ToBytes(out, (*p224UntypedFieldElement)(&tmp))
p224InvertEndianness(out[:])
return out[:]
}
// p224MinusOneEncoding 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 p224MinusOneEncoding = new(P224Element).Sub(
new(P224Element), new(P224Element).One()).Bytes()
// SetBytes sets e = v, where v is a big-endian 28-byte encoding, and returns e.
// If v is not 28 bytes or it encodes a value higher than 2^224 - 2^96 + 1,
// SetBytes returns nil and an error, and e is unchanged.
func (e *P224Element) SetBytes(v []byte) (*P224Element, error) {
if len(v) != p224ElementLen {
return nil, errors.New("invalid P224Element encoding")
}
for i := range v {
if v[i] < p224MinusOneEncoding[i] {
break
}
if v[i] > p224MinusOneEncoding[i] {
return nil, errors.New("invalid P224Element encoding")
}
}
var in [p224ElementLen]byte
copy(in[:], v)
p224InvertEndianness(in[:])
var tmp p224NonMontgomeryDomainFieldElement
p224FromBytes((*p224UntypedFieldElement)(&tmp), &in)
p224ToMontgomery(&e.x, &tmp)
return e, nil
}
// Add sets e = t1 + t2, and returns e.
func (e *P224Element) Add(t1, t2 *P224Element) *P224Element {
p224Add(&e.x, &t1.x, &t2.x)
return e
}
// Sub sets e = t1 - t2, and returns e.
func (e *P224Element) Sub(t1, t2 *P224Element) *P224Element {
p224Sub(&e.x, &t1.x, &t2.x)
return e
}
// Mul sets e = t1 * t2, and returns e.
func (e *P224Element) Mul(t1, t2 *P224Element) *P224Element {
p224Mul(&e.x, &t1.x, &t2.x)
return e
}
// Square sets e = t * t, and returns e.
func (e *P224Element) Square(t *P224Element) *P224Element {
p224Square(&e.x, &t.x)
return e
}
// Select sets v to a if cond == 1, and to b if cond == 0.
func (v *P224Element) Select(a, b *P224Element, cond int) *P224Element {
p224Selectznz((*p224UntypedFieldElement)(&v.x), p224Uint1(cond),
(*p224UntypedFieldElement)(&b.x), (*p224UntypedFieldElement)(&a.x))
return v
}
func p224InvertEndianness(v []byte) {
for i := 0; i < len(v)/2; i++ {
v[i], v[len(v)-1-i] = v[len(v)-1-i], v[i]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
// 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 addchain. DO NOT EDIT.
package fiat
// Invert sets e = 1/x, and returns e.
//
// If x == 0, Invert returns e = 0.
func (e *P224Element) Invert(x *P224Element) *P224Element {
// Inversion is implemented as exponentiation with exponent p 2.
// The sequence of 11 multiplications and 223 squarings is derived from the
// following addition chain generated with github.com/mmcloughlin/addchain v0.3.0.
//
// _10 = 2*1
// _11 = 1 + _10
// _110 = 2*_11
// _111 = 1 + _110
// _111000 = _111 << 3
// _111111 = _111 + _111000
// x12 = _111111 << 6 + _111111
// x14 = x12 << 2 + _11
// x17 = x14 << 3 + _111
// x31 = x17 << 14 + x14
// x48 = x31 << 17 + x17
// x96 = x48 << 48 + x48
// x127 = x96 << 31 + x31
// return x127 << 97 + x96
//
var z = new(P224Element).Set(e)
var t0 = new(P224Element)
var t1 = new(P224Element)
var t2 = new(P224Element)
z.Square(x)
t0.Mul(x, z)
z.Square(t0)
z.Mul(x, z)
t1.Square(z)
for s := 1; s < 3; s++ {
t1.Square(t1)
}
t1.Mul(z, t1)
t2.Square(t1)
for s := 1; s < 6; s++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
for s := 0; s < 2; s++ {
t1.Square(t1)
}
t0.Mul(t0, t1)
t1.Square(t0)
for s := 1; s < 3; s++ {
t1.Square(t1)
}
z.Mul(z, t1)
t1.Square(z)
for s := 1; s < 14; s++ {
t1.Square(t1)
}
t0.Mul(t0, t1)
t1.Square(t0)
for s := 1; s < 17; s++ {
t1.Square(t1)
}
z.Mul(z, t1)
t1.Square(z)
for s := 1; s < 48; s++ {
t1.Square(t1)
}
z.Mul(z, t1)
t1.Square(z)
for s := 1; s < 31; s++ {
t1.Square(t1)
}
t0.Mul(t0, t1)
for s := 0; s < 97; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
return e.Set(z)
}

View File

@ -0,0 +1,135 @@
// 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"
)
// P384Element is an integer modulo 2^384 - 2^128 - 2^96 + 2^32 - 1.
//
// The zero value is a valid zero element.
type P384Element struct {
// Values are represented internally always in the Montgomery domain, and
// converted in Bytes and SetBytes.
x p384MontgomeryDomainFieldElement
}
const p384ElementLen = 48
type p384UntypedFieldElement = [6]uint64
// One sets e = 1, and returns e.
func (e *P384Element) One() *P384Element {
p384SetOne(&e.x)
return e
}
// Equal returns 1 if e == t, and zero otherwise.
func (e *P384Element) Equal(t *P384Element) int {
eBytes := e.Bytes()
tBytes := t.Bytes()
return subtle.ConstantTimeCompare(eBytes, tBytes)
}
var p384ZeroEncoding = new(P384Element).Bytes()
// IsZero returns 1 if e == 0, and zero otherwise.
func (e *P384Element) IsZero() int {
eBytes := e.Bytes()
return subtle.ConstantTimeCompare(eBytes, p384ZeroEncoding)
}
// Set sets e = t, and returns e.
func (e *P384Element) Set(t *P384Element) *P384Element {
e.x = t.x
return e
}
// Bytes returns the 48-byte big-endian encoding of e.
func (e *P384Element) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [p384ElementLen]byte
return e.bytes(&out)
}
func (e *P384Element) bytes(out *[p384ElementLen]byte) []byte {
var tmp p384NonMontgomeryDomainFieldElement
p384FromMontgomery(&tmp, &e.x)
p384ToBytes(out, (*p384UntypedFieldElement)(&tmp))
p384InvertEndianness(out[:])
return out[:]
}
// p384MinusOneEncoding 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 p384MinusOneEncoding = new(P384Element).Sub(
new(P384Element), new(P384Element).One()).Bytes()
// SetBytes sets e = v, where v is a big-endian 48-byte encoding, and returns e.
// If v is not 48 bytes or it encodes a value higher than 2^384 - 2^128 - 2^96 + 2^32 - 1,
// SetBytes returns nil and an error, and e is unchanged.
func (e *P384Element) SetBytes(v []byte) (*P384Element, error) {
if len(v) != p384ElementLen {
return nil, errors.New("invalid P384Element encoding")
}
for i := range v {
if v[i] < p384MinusOneEncoding[i] {
break
}
if v[i] > p384MinusOneEncoding[i] {
return nil, errors.New("invalid P384Element encoding")
}
}
var in [p384ElementLen]byte
copy(in[:], v)
p384InvertEndianness(in[:])
var tmp p384NonMontgomeryDomainFieldElement
p384FromBytes((*p384UntypedFieldElement)(&tmp), &in)
p384ToMontgomery(&e.x, &tmp)
return e, nil
}
// Add sets e = t1 + t2, and returns e.
func (e *P384Element) Add(t1, t2 *P384Element) *P384Element {
p384Add(&e.x, &t1.x, &t2.x)
return e
}
// Sub sets e = t1 - t2, and returns e.
func (e *P384Element) Sub(t1, t2 *P384Element) *P384Element {
p384Sub(&e.x, &t1.x, &t2.x)
return e
}
// Mul sets e = t1 * t2, and returns e.
func (e *P384Element) Mul(t1, t2 *P384Element) *P384Element {
p384Mul(&e.x, &t1.x, &t2.x)
return e
}
// Square sets e = t * t, and returns e.
func (e *P384Element) Square(t *P384Element) *P384Element {
p384Square(&e.x, &t.x)
return e
}
// Select sets v to a if cond == 1, and to b if cond == 0.
func (v *P384Element) Select(a, b *P384Element, cond int) *P384Element {
p384Selectznz((*p384UntypedFieldElement)(&v.x), p384Uint1(cond),
(*p384UntypedFieldElement)(&b.x), (*p384UntypedFieldElement)(&a.x))
return v
}
func p384InvertEndianness(v []byte) {
for i := 0; i < len(v)/2; i++ {
v[i], v[len(v)-1-i] = v[len(v)-1-i], v[i]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
// 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 addchain. DO NOT EDIT.
package fiat
// Invert sets e = 1/x, and returns e.
//
// If x == 0, Invert returns e = 0.
func (e *P384Element) Invert(x *P384Element) *P384Element {
// Inversion is implemented as exponentiation with exponent p 2.
// The sequence of 15 multiplications and 383 squarings is derived from the
// following addition chain generated with github.com/mmcloughlin/addchain v0.3.0.
//
// _10 = 2*1
// _11 = 1 + _10
// _110 = 2*_11
// _111 = 1 + _110
// _111000 = _111 << 3
// _111111 = _111 + _111000
// x12 = _111111 << 6 + _111111
// x24 = x12 << 12 + x12
// x30 = x24 << 6 + _111111
// x31 = 2*x30 + 1
// x32 = 2*x31 + 1
// x63 = x32 << 31 + x31
// x126 = x63 << 63 + x63
// x252 = x126 << 126 + x126
// x255 = x252 << 3 + _111
// i397 = ((x255 << 33 + x32) << 94 + x30) << 2
// return 1 + i397
//
var z = new(P384Element).Set(e)
var t0 = new(P384Element)
var t1 = new(P384Element)
var t2 = new(P384Element)
var t3 = new(P384Element)
z.Square(x)
z.Mul(x, z)
z.Square(z)
t1.Mul(x, z)
z.Square(t1)
for s := 1; s < 3; s++ {
z.Square(z)
}
z.Mul(t1, z)
t0.Square(z)
for s := 1; s < 6; s++ {
t0.Square(t0)
}
t0.Mul(z, t0)
t2.Square(t0)
for s := 1; s < 12; s++ {
t2.Square(t2)
}
t0.Mul(t0, t2)
for s := 0; s < 6; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
t2.Mul(x, t0)
t0.Square(t2)
t0.Mul(x, t0)
t3.Square(t0)
for s := 1; s < 31; s++ {
t3.Square(t3)
}
t2.Mul(t2, t3)
t3.Square(t2)
for s := 1; s < 63; s++ {
t3.Square(t3)
}
t2.Mul(t2, t3)
t3.Square(t2)
for s := 1; s < 126; s++ {
t3.Square(t3)
}
t2.Mul(t2, t3)
for s := 0; s < 3; s++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
for s := 0; s < 33; s++ {
t1.Square(t1)
}
t0.Mul(t0, t1)
for s := 0; s < 94; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
for s := 0; s < 2; s++ {
z.Square(z)
}
z.Mul(x, z)
return e.Set(z)
}

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fiat implements prime order fields using formally verified algorithms
// from the Fiat Cryptography project.
// Code generated by generate.go. DO NOT EDIT.
package fiat
import (
@ -15,20 +15,18 @@ import (
//
// The zero value is a valid zero element.
type P521Element struct {
// This element has the following bounds, which are tighter than
// the output bounds of some operations. Those operations must be
// followed by a carry.
//
// [0x0 ~> 0x400000000000000], [0x0 ~> 0x400000000000000], [0x0 ~> 0x400000000000000],
// [0x0 ~> 0x400000000000000], [0x0 ~> 0x400000000000000], [0x0 ~> 0x400000000000000],
// [0x0 ~> 0x400000000000000], [0x0 ~> 0x400000000000000], [0x0 ~> 0x200000000000000]
x [9]uint64
// Values are represented internally always in the Montgomery domain, and
// converted in Bytes and SetBytes.
x p521MontgomeryDomainFieldElement
}
const p521ElementLen = 66
type p521UntypedFieldElement = [9]uint64
// One sets e = 1, and returns e.
func (e *P521Element) One() *P521Element {
*e = P521Element{}
e.x[0] = 1
p521SetOne(&e.x)
return e
}
@ -57,153 +55,81 @@ func (e *P521Element) Set(t *P521Element) *P521Element {
func (e *P521Element) Bytes() []byte {
// This function is outlined to make the allocations inline in the caller
// rather than happen on the heap.
var out [66]byte
var out [p521ElementLen]byte
return e.bytes(&out)
}
func (e *P521Element) bytes(out *[66]byte) []byte {
p521ToBytes(out, &e.x)
invertEndianness(out[:])
func (e *P521Element) bytes(out *[p521ElementLen]byte) []byte {
var tmp p521NonMontgomeryDomainFieldElement
p521FromMontgomery(&tmp, &e.x)
p521ToBytes(out, (*p521UntypedFieldElement)(&tmp))
p521InvertEndianness(out[:])
return out[:]
}
// SetBytes sets e = v, where v is a big-endian 66-byte encoding, and returns
// e. If v is not 66 bytes or it encodes a value higher than 2^521 - 1, SetBytes
// returns nil and an error, and e is unchanged.
func (e *P521Element) SetBytes(v []byte) (*P521Element, error) {
if len(v) != 66 || v[0] > 1 {
return nil, errors.New("invalid P-521 field encoding")
}
var in [66]byte
copy(in[:], v)
invertEndianness(in[:])
p521FromBytes(&e.x, &in)
return e, nil
}
// p521MinusOneEncoding 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 p521MinusOneEncoding = new(P521Element).Sub(
new(P521Element), new(P521Element).One()).Bytes()
func 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]
// SetBytes sets e = v, where v is a big-endian 66-byte encoding, and returns e.
// If v is not 66 bytes or it encodes a value higher than 2^521 - 1,
// SetBytes returns nil and an error, and e is unchanged.
func (e *P521Element) SetBytes(v []byte) (*P521Element, error) {
if len(v) != p521ElementLen {
return nil, errors.New("invalid P521Element encoding")
}
for i := range v {
if v[i] < p521MinusOneEncoding[i] {
break
}
if v[i] > p521MinusOneEncoding[i] {
return nil, errors.New("invalid P521Element encoding")
}
}
var in [p521ElementLen]byte
copy(in[:], v)
p521InvertEndianness(in[:])
var tmp p521NonMontgomeryDomainFieldElement
p521FromBytes((*p521UntypedFieldElement)(&tmp), &in)
p521ToMontgomery(&e.x, &tmp)
return e, nil
}
// Add sets e = t1 + t2, and returns e.
func (e *P521Element) Add(t1, t2 *P521Element) *P521Element {
p521Add(&e.x, &t1.x, &t2.x)
p521Carry(&e.x, &e.x)
return e
}
// Sub sets e = t1 - t2, and returns e.
func (e *P521Element) Sub(t1, t2 *P521Element) *P521Element {
p521Sub(&e.x, &t1.x, &t2.x)
p521Carry(&e.x, &e.x)
return e
}
// Mul sets e = t1 * t2, and returns e.
func (e *P521Element) Mul(t1, t2 *P521Element) *P521Element {
p521CarryMul(&e.x, &t1.x, &t2.x)
p521Mul(&e.x, &t1.x, &t2.x)
return e
}
// Square sets e = t * t, and returns e.
func (e *P521Element) Square(t *P521Element) *P521Element {
p521CarrySquare(&e.x, &t.x)
p521Square(&e.x, &t.x)
return e
}
// Select sets e to a if cond == 1, and to b if cond == 0.
// Select sets v to a if cond == 1, and to b if cond == 0.
func (v *P521Element) Select(a, b *P521Element, cond int) *P521Element {
p521Selectznz(&v.x, p521Uint1(cond), &b.x, &a.x)
p521Selectznz((*p521UntypedFieldElement)(&v.x), p521Uint1(cond),
(*p521UntypedFieldElement)(&b.x), (*p521UntypedFieldElement)(&a.x))
return v
}
// Invert sets e = 1/t, and returns e.
//
// If t == 0, Invert returns e = 0.
func (e *P521Element) Invert(t *P521Element) *P521Element {
// Inversion is implemented as exponentiation with exponent p 2.
// The sequence of multiplications and squarings was generated with
// github.com/mmcloughlin/addchain v0.2.0.
var t1, t2 = new(P521Element), new(P521Element)
// _10 = 2 * 1
t1.Square(t)
// _11 = 1 + _10
t1.Mul(t, t1)
// _1100 = _11 << 2
t2.Square(t1)
t2.Square(t2)
// _1111 = _11 + _1100
t1.Mul(t1, t2)
// _11110000 = _1111 << 4
t2.Square(t1)
for i := 0; i < 3; i++ {
t2.Square(t2)
func p521InvertEndianness(v []byte) {
for i := 0; i < len(v)/2; i++ {
v[i], v[len(v)-1-i] = v[len(v)-1-i], v[i]
}
// _11111111 = _1111 + _11110000
t1.Mul(t1, t2)
// x16 = _11111111<<8 + _11111111
t2.Square(t1)
for i := 0; i < 7; i++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
// x32 = x16<<16 + x16
t2.Square(t1)
for i := 0; i < 15; i++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
// x64 = x32<<32 + x32
t2.Square(t1)
for i := 0; i < 31; i++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
// x65 = 2*x64 + 1
t2.Square(t1)
t2.Mul(t2, t)
// x129 = x65<<64 + x64
for i := 0; i < 64; i++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
// x130 = 2*x129 + 1
t2.Square(t1)
t2.Mul(t2, t)
// x259 = x130<<129 + x129
for i := 0; i < 129; i++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
// x260 = 2*x259 + 1
t2.Square(t1)
t2.Mul(t2, t)
// x519 = x260<<259 + x259
for i := 0; i < 259; i++ {
t2.Square(t2)
}
t1.Mul(t1, t2)
// return x519<<2 + 1
t1.Square(t1)
t1.Square(t1)
return e.Mul(t1, t)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
// 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 addchain. DO NOT EDIT.
package fiat
// Invert sets e = 1/x, and returns e.
//
// If x == 0, Invert returns e = 0.
func (e *P521Element) Invert(x *P521Element) *P521Element {
// Inversion is implemented as exponentiation with exponent p 2.
// The sequence of 13 multiplications and 520 squarings is derived from the
// following addition chain generated with github.com/mmcloughlin/addchain v0.3.0.
//
// _10 = 2*1
// _11 = 1 + _10
// _1100 = _11 << 2
// _1111 = _11 + _1100
// _11110000 = _1111 << 4
// _11111111 = _1111 + _11110000
// x16 = _11111111 << 8 + _11111111
// x32 = x16 << 16 + x16
// x64 = x32 << 32 + x32
// x65 = 2*x64 + 1
// x129 = x65 << 64 + x64
// x130 = 2*x129 + 1
// x259 = x130 << 129 + x129
// x260 = 2*x259 + 1
// x519 = x260 << 259 + x259
// return x519 << 2 + 1
//
var z = new(P521Element).Set(e)
var t0 = new(P521Element)
z.Square(x)
z.Mul(x, z)
t0.Square(z)
for s := 1; s < 2; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
for s := 1; s < 4; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
for s := 1; s < 8; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
for s := 1; s < 16; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
for s := 1; s < 32; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
t0.Mul(x, t0)
for s := 0; s < 64; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
t0.Mul(x, t0)
for s := 0; s < 129; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
t0.Square(z)
t0.Mul(x, t0)
for s := 0; s < 259; s++ {
t0.Square(t0)
}
z.Mul(z, t0)
for s := 0; s < 2; s++ {
z.Square(z)
}
z.Mul(x, z)
return e.Set(z)
}

View File

@ -1,37 +0,0 @@
// 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 fiat_test
import (
"crypto/elliptic/internal/fiat"
"crypto/rand"
"testing"
)
func p521Random(t *testing.T) *fiat.P521Element {
buf := make([]byte, 66)
if _, err := rand.Read(buf); err != nil {
t.Fatal(err)
}
buf[0] &= 1
e, err := new(fiat.P521Element).SetBytes(buf)
if err != nil {
t.Fatal(err)
}
return e
}
func TestP521Invert(t *testing.T) {
a := p521Random(t)
inv := new(fiat.P521Element).Invert(a)
one := new(fiat.P521Element).Mul(a, inv)
if new(fiat.P521Element).One().Equal(one) != 1 {
t.Errorf("a * 1/a != 1; got %x for %x", one.Bytes(), a.Bytes())
}
inv.Invert(new(fiat.P521Element))
if new(fiat.P521Element).Equal(inv) != 1 {
t.Errorf("1/0 != 0; got %x", inv.Bytes())
}
}

View File

@ -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)
}
})
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -1,44 +0,0 @@
// 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 TestP521Allocations(t *testing.T) {
if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-noopt") {
t.Skip("skipping allocations test without relevant optimizations")
}
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("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)
}
})
}

View File

@ -1,739 +1,136 @@
// Copyright 2012 The Go Authors. All rights reserved.
// 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
// This is a constant-time, 32-bit implementation of P224. See FIPS 186-3,
// section D.2.2.
//
// See https://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
import (
"encoding/binary"
"crypto/elliptic/internal/nistec"
"crypto/rand"
"math/big"
"math/bits"
)
var p224 p224Curve
// p224Curve is a Curve implementation based on nistec.P224Point.
//
// 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 p224Curve struct {
*CurveParams
gx, gy, b p224FieldElement
params *CurveParams
}
var p224 p224Curve
var _ Curve = p224
func initP224() {
// See FIPS 186-3, section D.2.2
p224.CurveParams = &CurveParams{Name: "P-224"}
p224.P, _ = new(big.Int).SetString("26959946667150639794667015087019630673557916260026308143510066298881", 10)
p224.N, _ = new(big.Int).SetString("26959946667150639794667015087019625940457807714424391721682722368061", 10)
p224.B, _ = new(big.Int).SetString("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", 16)
p224.Gx, _ = new(big.Int).SetString("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", 16)
p224.Gy, _ = new(big.Int).SetString("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34", 16)
p224.BitSize = 224
p224FromBig(&p224.gx, p224.Gx)
p224FromBig(&p224.gy, p224.Gy)
p224FromBig(&p224.b, p224.B)
}
// P224 returns a Curve which implements P-224 (see FIPS 186-3, section D.2.2).
//
// The cryptographic operations are implemented using constant-time algorithms.
func P224() Curve {
initonce.Do(initAll)
return p224
p224.params = &CurveParams{
Name: "P-224",
BitSize: 224,
// FIPS 186-4, section D.1.2.2
P: bigFromDecimal("26959946667150639794667015087019630673557916260026308143510066298881"),
N: bigFromDecimal("26959946667150639794667015087019625940457807714424391721682722368061"),
B: bigFromHex("b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4"),
Gx: bigFromHex("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"),
Gy: bigFromHex("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"),
}
}
func (curve p224Curve) Params() *CurveParams {
return curve.CurveParams
return curve.params
}
func (curve p224Curve) IsOnCurve(bigX, bigY *big.Int) bool {
if bigX.BitLen() > 224 || bigY.BitLen() > 224 {
func (curve p224Curve) IsOnCurve(x, y *big.Int) bool {
// IsOnCurve is documented to reject (0, 0), the conventional point at
// infinity, which however is accepted by p224PointFromAffine.
if x.Sign() == 0 && y.Sign() == 0 {
return false
}
var x, y p224FieldElement
p224FromBig(&x, bigX)
p224FromBig(&y, bigY)
// y² = x³ - 3x + b
var tmp p224LargeFieldElement
var x3 p224FieldElement
p224Square(&x3, &x, &tmp)
p224Mul(&x3, &x3, &x, &tmp)
for i := 0; i < 8; i++ {
x[i] *= 3
}
p224Sub(&x3, &x3, &x)
p224Reduce(&x3)
p224Add(&x3, &x3, &curve.b)
p224Contract(&x3, &x3)
p224Square(&y, &y, &tmp)
p224Contract(&y, &y)
for i := 0; i < 8; i++ {
if y[i] != x3[i] {
return false
}
}
return true
_, ok := p224PointFromAffine(x, y)
return ok
}
func (p224Curve) Add(bigX1, bigY1, bigX2, bigY2 *big.Int) (x, y *big.Int) {
var x1, y1, z1, x2, y2, z2, x3, y3, z3 p224FieldElement
p224FromBig(&x1, bigX1)
p224FromBig(&y1, bigY1)
if bigX1.Sign() != 0 || bigY1.Sign() != 0 {
z1[0] = 1
func p224PointFromAffine(x, y *big.Int) (p *nistec.P224Point, 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.NewP224Point(), true
}
p224FromBig(&x2, bigX2)
p224FromBig(&y2, bigY2)
if bigX2.Sign() != 0 || bigY2.Sign() != 0 {
z2[0] = 1
if x.BitLen() > 224 || y.BitLen() > 224 {
return nil, false
}
p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2)
return p224ToAffine(&x3, &y3, &z3)
p, err := nistec.NewP224Point().SetBytes(Marshal(P224(), x, y))
if err != nil {
return nil, false
}
return p, true
}
func (p224Curve) Double(bigX1, bigY1 *big.Int) (x, y *big.Int) {
var x1, y1, z1, x2, y2, z2 p224FieldElement
p224FromBig(&x1, bigX1)
p224FromBig(&y1, bigY1)
z1[0] = 1
p224DoubleJacobian(&x2, &y2, &z2, &x1, &y1, &z1)
return p224ToAffine(&x2, &y2, &z2)
}
func (p224Curve) ScalarMult(bigX1, bigY1 *big.Int, scalar []byte) (x, y *big.Int) {
var x1, y1, z1, x2, y2, z2 p224FieldElement
p224FromBig(&x1, bigX1)
p224FromBig(&y1, bigY1)
z1[0] = 1
p224ScalarMult(&x2, &y2, &z2, &x1, &y1, &z1, scalar)
return p224ToAffine(&x2, &y2, &z2)
}
func (curve p224Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
var z1, x2, y2, z2 p224FieldElement
z1[0] = 1
p224ScalarMult(&x2, &y2, &z2, &curve.gx, &curve.gy, &z1, scalar)
return p224ToAffine(&x2, &y2, &z2)
}
// Field element functions.
//
// The field that we're dealing with is /p where p = 2**224 - 2**96 + 1.
//
// Field elements are represented by a FieldElement, which is a typedef to an
// array of 8 uint32's. The value of a FieldElement, a, is:
// a[0] + 2**28·a[1] + 2**56·a[1] + ... + 2**196·a[7]
//
// Using 28-bit limbs means that there's only 4 bits of headroom, which is less
// than we would really like. But it has the useful feature that we hit 2**224
// exactly, making the reflections during a reduce much nicer.
type p224FieldElement [8]uint32
// p224P is the order of the field, represented as a p224FieldElement.
var p224P = p224FieldElement{1, 0, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
// p224IsZero returns 1 if a == 0 mod p and 0 otherwise.
//
// a[i] < 2**29
func p224IsZero(a *p224FieldElement) uint32 {
var minimal p224FieldElement
p224Contract(&minimal, a)
var acc uint32
for _, v := range minimal {
acc |= v
}
mask := ^maskIsNotZero(acc)
return 1 & mask
}
// p224Add computes *out = a+b
//
// a[i] + b[i] < 2**32
func p224Add(out, a, b *p224FieldElement) {
for i := 0; i < 8; i++ {
out[i] = a[i] + b[i]
}
}
const two31p3 = 1<<31 + 1<<3
const two31m3 = 1<<31 - 1<<3
const two31m15m3 = 1<<31 - 1<<15 - 1<<3
// p224ZeroModP31 is 0 mod p where bit 31 is set in all limbs so that we can
// subtract smaller amounts without underflow. See the section "Subtraction" in
// [1] for reasoning.
//
// To calculate this value, start by adding 2³¹ to the lowest limb and
// subtracting 2³ from the next one to compensate. Repeat for each next limb,
// ending up with 2³¹ - 2³ in each of them, and a carry of -2³. Apply the
// reduction identity, and we need to subtract 2³ * 2⁹⁶ - 2³ = 2¹⁵ * 2⁸⁴ - 2³ so
// we subtract 2¹⁵ from the 4th limb and add 2³ to the first limb.
var p224ZeroModP31 = []uint32{two31p3, two31m3, two31m3, two31m15m3, two31m3, two31m3, two31m3, two31m3}
// p224Sub computes *out = a-b
//
// a[i], b[i] < 2**30
// out[i] < 2**32
func p224Sub(out, a, b *p224FieldElement) {
for i := 0; i < 8; i++ {
out[i] = a[i] + p224ZeroModP31[i] - b[i]
}
}
// LargeFieldElement also represents an element of the field. The limbs are
// still spaced 28-bits apart and in little-endian order. So the limbs are at
// 0, 28, 56, ..., 392 bits, each 64-bits wide.
type p224LargeFieldElement [15]uint64
const two63p35 = 1<<63 + 1<<35
const two63m35 = 1<<63 - 1<<35
const two63m35m19 = 1<<63 - 1<<35 - 1<<19
// p224ZeroModP63 is 0 mod p where bit 63 is set in all limbs. See the section
// "Subtraction" in [1] for why.
var p224ZeroModP63 = [8]uint64{two63p35, two63m35, two63m35, two63m35, two63m35m19, two63m35, two63m35, two63m35}
const bottom12Bits = 0xfff
const bottom28Bits = 0xfffffff
// p224Mul computes *out = a*b
//
// a[i] < 2**29, b[i] < 2**30 (or vice versa)
// out[i] < 2**29
func p224Mul(out, a, b *p224FieldElement, tmp *p224LargeFieldElement) {
for i := range tmp {
tmp[i] = 0
}
for i := 0; i < 8; i++ {
for j := 0; j < 8; j++ {
tmp[i+j] += uint64(a[i]) * uint64(b[j])
}
}
p224ReduceLarge(out, tmp)
}
// Square computes *out = a*a
//
// a[i] < 2**29
// out[i] < 2**29
func p224Square(out, a *p224FieldElement, tmp *p224LargeFieldElement) {
for i := range tmp {
tmp[i] = 0
}
for i := 0; i < 8; i++ {
for j := 0; j <= i; j++ {
r := uint64(a[i]) * uint64(a[j])
if i == j {
tmp[i+j] += r
} else {
tmp[i+j] += r * 2
}
}
}
p224ReduceLarge(out, tmp)
}
// ReduceLarge converts a p224LargeFieldElement to a p224FieldElement.
//
// in[i] < 2**62
// out[i] < 2**29
func p224ReduceLarge(out *p224FieldElement, in *p224LargeFieldElement) {
for i := 0; i < 8; i++ {
in[i] += p224ZeroModP63[i]
}
// Eliminate the coefficients at 2**224 and greater by applying the
// reduction identity.
//
// a + top * 2²²⁴ = a + top * 2⁹⁶ - top
//
// Since top here is in[8..14], both the subtraction at offset 0 and the
// addition at offset 96 (3 * 28 + 16) span multiple limbs. The subtraction
// can't underflow because of the p224ZeroModP63 addition above, while the
// addition can't overflow because of the 62 bit input bounds.
for i := 14; i >= 8; i-- {
in[i-8] -= in[i]
in[i-5] += (in[i] & 0xffff) << 12
in[i-4] += in[i] >> 16
}
in[8] = 0
// in[0..7] < 2**64
// in[9..14] discarded
// Run a carry chain and light reduction. Keep [0] large so we can do the
// subtraction safely. As the values become small enough, we start to store
// them in out and use 32-bit operations.
for i := 1; i < 8; i++ {
in[i+1] += in[i] >> 28
out[i] = uint32(in[i] & bottom28Bits)
}
in[0] -= in[8]
out[3] += uint32(in[8]&0xffff) << 12
out[4] += uint32(in[8] >> 16)
// in[0] < 2**64
// out[3] < 2**29
// out[4] < 2**29
// out[1,2,5..7] < 2**28
// Carry the overflow of [0] into the short 28 bit limbs.
out[0] = uint32(in[0] & bottom28Bits)
out[1] += uint32((in[0] >> 28) & bottom28Bits)
out[2] += uint32(in[0] >> 56)
// out[0] < 2**28
// out[1..4] < 2**29
// out[5..7] < 2**28
}
// Reduce reduces the coefficients of a to smaller bounds.
//
// On entry: a[i] < 2**31 + 2**30
// On exit: a[i] < 2**29
func p224Reduce(a *p224FieldElement) {
for i := 0; i < 7; i++ {
a[i+1] += a[i] >> 28
a[i] &= bottom28Bits
}
top := a[7] >> 28
a[7] &= bottom28Bits
a[0] -= top
a[3] += top << 12
// We may have just made a[0] negative but if we did top must have been not
// zero, so a[3] is not zero, so we can carry down to a[0]. (Note that we
// don't actually check if a[0] went negative, like in p224Contract, nor we
// try to stop the carry at a[1] or a[2], because here we can afford to go
// above 28 bits, so instead we carry all the way down from a[3].)
mask := maskIsNotZero(top)
a[3] -= 1 & mask
a[2] += mask & (1<<28 - 1)
a[1] += mask & (1<<28 - 1)
a[0] += mask & (1 << 28)
}
// p224Invert calculates *out = in**-1 by using Fermat's little theorem and
// computing in**(p-2) = in**(2**224 - 2**96 - 1).
func p224Invert(out, in *p224FieldElement) {
var f1, f2, f3, f4 p224FieldElement
var c p224LargeFieldElement
p224Square(&f1, in, &c) // 2
p224Mul(&f1, &f1, in, &c) // 2**2 - 1
p224Square(&f1, &f1, &c) // 2**3 - 2
p224Mul(&f1, &f1, in, &c) // 2**3 - 1
p224Square(&f2, &f1, &c) // 2**4 - 2
p224Square(&f2, &f2, &c) // 2**5 - 4
p224Square(&f2, &f2, &c) // 2**6 - 8
p224Mul(&f1, &f1, &f2, &c) // 2**6 - 1
p224Square(&f2, &f1, &c) // 2**7 - 2
for i := 0; i < 5; i++ { // 2**12 - 2**6
p224Square(&f2, &f2, &c)
}
p224Mul(&f2, &f2, &f1, &c) // 2**12 - 1
p224Square(&f3, &f2, &c) // 2**13 - 2
for i := 0; i < 11; i++ { // 2**24 - 2**12
p224Square(&f3, &f3, &c)
}
p224Mul(&f2, &f3, &f2, &c) // 2**24 - 1
p224Square(&f3, &f2, &c) // 2**25 - 2
for i := 0; i < 23; i++ { // 2**48 - 2**24
p224Square(&f3, &f3, &c)
}
p224Mul(&f3, &f3, &f2, &c) // 2**48 - 1
p224Square(&f4, &f3, &c) // 2**49 - 2
for i := 0; i < 47; i++ { // 2**96 - 2**48
p224Square(&f4, &f4, &c)
}
p224Mul(&f3, &f3, &f4, &c) // 2**96 - 1
p224Square(&f4, &f3, &c) // 2**97 - 2
for i := 0; i < 23; i++ { // 2**120 - 2**24
p224Square(&f4, &f4, &c)
}
p224Mul(&f2, &f4, &f2, &c) // 2**120 - 1
for i := 0; i < 6; i++ { // 2**126 - 2**6
p224Square(&f2, &f2, &c)
}
p224Mul(&f1, &f1, &f2, &c) // 2**126 - 1
p224Square(&f1, &f1, &c) // 2**127 - 2
p224Mul(&f1, &f1, in, &c) // 2**127 - 1
for i := 0; i < 97; i++ { // 2**224 - 2**97
p224Square(&f1, &f1, &c)
}
p224Mul(out, &f1, &f3, &c) // 2**224 - 2**96 - 1
}
// p224Contract converts a FieldElement to its unique, minimal form.
//
// On entry, in[i] < 2**29
// On exit, out[i] < 2**28 and out < p
func p224Contract(out, in *p224FieldElement) {
copy(out[:], in[:])
// First, carry the bits above 28 to the higher limb.
for i := 0; i < 7; i++ {
out[i+1] += out[i] >> 28
out[i] &= bottom28Bits
}
top := out[7] >> 28
out[7] &= bottom28Bits
// Use the reduction identity to carry the overflow.
//
// a + top * 2²²⁴ = a + top * 2⁹⁶ - top
out[0] -= top
out[3] += top << 12
// We may just have made out[0] negative. So we carry down. If we made
// out[0] negative then we know that out[3] is sufficiently positive
// because we just added to it.
for i := 0; i < 3; i++ {
mask := maskIsNegative(out[i])
out[i] += (1 << 28) & mask
out[i+1] -= 1 & mask
}
// We might have pushed out[3] over 2**28 so we perform another, partial,
// carry chain; carry the overflow according to the reduction identity; and
// carry down in case we made out[0] negative.
for i := 3; i < 7; i++ {
out[i+1] += out[i] >> 28
out[i] &= bottom28Bits
}
top = out[7] >> 28
out[7] &= bottom28Bits
out[0] -= top
out[3] += top << 12
for i := 0; i < 3; i++ {
mask := maskIsNegative(out[i])
out[i] += (1 << 28) & mask
out[i+1] -= 1 & mask
}
// There are two cases to consider for out[3]:
// 1) The first time that we eliminated top, we didn't push out[3] over
// 2**28. In this case, the partial carry chain didn't change any values
// and top is now zero.
// 2) We did push out[3] over 2**28 the first time that we eliminated top.
// The first value of top was in [0..2], therefore, after overflowing
// and being reduced by the second carry chain, out[3] <= 2<<12 - 1.
// In both cases, out[3] cannot have overflowed when we eliminated top for
// the second time.
// Now we need to subtract p if the value is >= p. To check, we subtract p
// with a borrow chain and look at the final borrow bit.
var b uint32
for i := 0; i < len(out); i++ {
_, b = bits.Sub32(out[i], p224P[i], b)
}
mask := ^maskIsNotZero(b)
out[0] -= 1 & mask
out[3] -= 0xffff000 & mask
out[4] -= 0xfffffff & mask
out[5] -= 0xfffffff & mask
out[6] -= 0xfffffff & mask
out[7] -= 0xfffffff & mask
// Do one final carry down, in case we made out[0] negative. One of
// out[0..3] needs to be positive and able to absorb the -1 or the value
// would have been < p, and the subtraction wouldn't have happened.
for i := 0; i < 3; i++ {
mask := maskIsNegative(out[i])
out[i] += (1 << 28) & mask
out[i+1] -= 1 & mask
}
}
// maskIsNegative returns 0xffffffff if the most significant bit of v is set,
// and 0 otherwise.
func maskIsNegative(v uint32) uint32 { return uint32(int32(v) >> 31) }
// maskIfNegative returns 0xffffffff if v is not zero, and 0 otherwise.
func maskIsNotZero(v uint32) uint32 {
v |= v >> 16
v |= v >> 8
v |= v >> 4
v |= v >> 2
v |= v >> 1
return uint32(int32(v<<31) >> 31)
}
// Group element functions.
//
// These functions deal with group elements. The group is an elliptic curve
// group with a = -3 defined in FIPS 186-3, section D.2.2.
// p224AddJacobian computes *out = a+b where a != b.
func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
// See https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-p224Add-2007-bl
var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement
var c p224LargeFieldElement
z1IsZero := p224IsZero(z1)
z2IsZero := p224IsZero(z2)
// Z1Z1 = Z1²
p224Square(&z1z1, z1, &c)
// Z2Z2 = Z2²
p224Square(&z2z2, z2, &c)
// U1 = X1*Z2Z2
p224Mul(&u1, x1, &z2z2, &c)
// U2 = X2*Z1Z1
p224Mul(&u2, x2, &z1z1, &c)
// S1 = Y1*Z2*Z2Z2
p224Mul(&s1, z2, &z2z2, &c)
p224Mul(&s1, y1, &s1, &c)
// S2 = Y2*Z1*Z1Z1
p224Mul(&s2, z1, &z1z1, &c)
p224Mul(&s2, y2, &s2, &c)
// H = U2-U1
p224Sub(&h, &u2, &u1)
p224Reduce(&h)
xEqual := p224IsZero(&h)
// I = (2*H)²
for j := 0; j < 8; j++ {
i[j] = h[j] << 1
}
p224Reduce(&i)
p224Square(&i, &i, &c)
// J = H*I
p224Mul(&j, &h, &i, &c)
// r = 2*(S2-S1)
p224Sub(&r, &s2, &s1)
p224Reduce(&r)
yEqual := p224IsZero(&r)
if xEqual == 1 && yEqual == 1 && z1IsZero == 0 && z2IsZero == 0 {
p224DoubleJacobian(x3, y3, z3, x1, y1, z1)
return
}
for i := 0; i < 8; i++ {
r[i] <<= 1
}
p224Reduce(&r)
// V = U1*I
p224Mul(&v, &u1, &i, &c)
// Z3 = ((Z1+Z2)²-Z1Z1-Z2Z2)*H
p224Add(&z1z1, &z1z1, &z2z2)
p224Add(&z2z2, z1, z2)
p224Reduce(&z2z2)
p224Square(&z2z2, &z2z2, &c)
p224Sub(z3, &z2z2, &z1z1)
p224Reduce(z3)
p224Mul(z3, z3, &h, &c)
// X3 = r²-J-2*V
for i := 0; i < 8; i++ {
z1z1[i] = v[i] << 1
}
p224Add(&z1z1, &j, &z1z1)
p224Reduce(&z1z1)
p224Square(x3, &r, &c)
p224Sub(x3, x3, &z1z1)
p224Reduce(x3)
// Y3 = r*(V-X3)-2*S1*J
for i := 0; i < 8; i++ {
s1[i] <<= 1
}
p224Mul(&s1, &s1, &j, &c)
p224Sub(&z1z1, &v, x3)
p224Reduce(&z1z1)
p224Mul(&z1z1, &z1z1, &r, &c)
p224Sub(y3, &z1z1, &s1)
p224Reduce(y3)
p224CopyConditional(x3, x2, z1IsZero)
p224CopyConditional(x3, x1, z2IsZero)
p224CopyConditional(y3, y2, z1IsZero)
p224CopyConditional(y3, y1, z2IsZero)
p224CopyConditional(z3, z2, z1IsZero)
p224CopyConditional(z3, z1, z2IsZero)
}
// p224DoubleJacobian computes *out = a+a.
func p224DoubleJacobian(x3, y3, z3, x1, y1, z1 *p224FieldElement) {
var delta, gamma, beta, alpha, t p224FieldElement
var c p224LargeFieldElement
p224Square(&delta, z1, &c)
p224Square(&gamma, y1, &c)
p224Mul(&beta, x1, &gamma, &c)
// alpha = 3*(X1-delta)*(X1+delta)
p224Add(&t, x1, &delta)
for i := 0; i < 8; i++ {
t[i] += t[i] << 1
}
p224Reduce(&t)
p224Sub(&alpha, x1, &delta)
p224Reduce(&alpha)
p224Mul(&alpha, &alpha, &t, &c)
// Z3 = (Y1+Z1)²-gamma-delta
p224Add(z3, y1, z1)
p224Reduce(z3)
p224Square(z3, z3, &c)
p224Sub(z3, z3, &gamma)
p224Reduce(z3)
p224Sub(z3, z3, &delta)
p224Reduce(z3)
// X3 = alpha²-8*beta
for i := 0; i < 8; i++ {
delta[i] = beta[i] << 3
}
p224Reduce(&delta)
p224Square(x3, &alpha, &c)
p224Sub(x3, x3, &delta)
p224Reduce(x3)
// Y3 = alpha*(4*beta-X3)-8*gamma²
for i := 0; i < 8; i++ {
beta[i] <<= 2
}
p224Sub(&beta, &beta, x3)
p224Reduce(&beta)
p224Square(&gamma, &gamma, &c)
for i := 0; i < 8; i++ {
gamma[i] <<= 3
}
p224Reduce(&gamma)
p224Mul(y3, &alpha, &beta, &c)
p224Sub(y3, y3, &gamma)
p224Reduce(y3)
}
// p224CopyConditional sets *out = *in in constant time if control is not zero.
func p224CopyConditional(out, in *p224FieldElement, control uint32) {
mask := maskIsNotZero(control)
for i := 0; i < 8; i++ {
out[i] ^= (out[i] ^ in[i]) & mask
}
}
func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) {
var xx, yy, zz p224FieldElement
for i := 0; i < 8; i++ {
outX[i] = 0
outY[i] = 0
outZ[i] = 0
}
for _, byte := range scalar {
for bitNum := uint(0); bitNum < 8; bitNum++ {
p224DoubleJacobian(outX, outY, outZ, outX, outY, outZ)
bit := uint32((byte >> (7 - bitNum)) & 1)
p224AddJacobian(&xx, &yy, &zz, inX, inY, inZ, outX, outY, outZ)
p224CopyConditional(outX, &xx, bit)
p224CopyConditional(outY, &yy, bit)
p224CopyConditional(outZ, &zz, bit)
}
}
}
// p224ToAffine converts from Jacobian to affine form.
func p224ToAffine(x, y, z *p224FieldElement) (*big.Int, *big.Int) {
var zinv, zinvsq, outx, outy p224FieldElement
var tmp p224LargeFieldElement
if isPointAtInfinity := p224IsZero(z); isPointAtInfinity == 1 {
func p224PointToAffine(p *nistec.P224Point) (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)
}
p224Invert(&zinv, z)
p224Square(&zinvsq, &zinv, &tmp)
p224Mul(x, x, &zinvsq, &tmp)
p224Mul(&zinvsq, &zinvsq, &zinv, &tmp)
p224Mul(y, y, &zinvsq, &tmp)
p224Contract(&outx, x)
p224Contract(&outy, y)
return p224ToBig(&outx), p224ToBig(&outy)
}
// get28BitsFromEnd returns the least-significant 28 bits from buf>>shift,
// where buf is interpreted as a big-endian number. shift must be at most
// 4 bits higher than a multiple of 8.
func get28BitsFromEnd(buf []byte, shift int) uint32 {
buf = buf[:len(buf)-shift/8]
shift = shift % 8
if shift > 4 {
panic("misuse of get28BitsFromEnd")
x, y = Unmarshal(P224(), out)
if x == nil {
panic("crypto/elliptic: internal error: Unmarshal rejected a valid point encoding")
}
ret := binary.BigEndian.Uint32(buf[len(buf)-4:])
ret >>= shift
ret &= bottom28Bits
return ret
return x, y
}
// p224FromBig sets *out = *in.
func p224FromBig(out *p224FieldElement, in *big.Int) {
bytes := in.FillBytes(make([]byte, 224/8))
for i := range out {
out[i] = get28BitsFromEnd(bytes, 28*i)
// p224RandomPoint 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.P224Point
// 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 p224RandomPoint() (x, y *big.Int) {
_, x, y, err := GenerateKey(P224(), rand.Reader)
if err != nil {
panic("crypto/elliptic: failed to generate random point")
}
return x, y
}
// p224ToBig returns in as a big.Int.
func p224ToBig(in *p224FieldElement) *big.Int {
var buf [28]byte
buf[27] = byte(in[0])
buf[26] = byte(in[0] >> 8)
buf[25] = byte(in[0] >> 16)
buf[24] = byte(((in[0] >> 24) & 0x0f) | (in[1]<<4)&0xf0)
buf[23] = byte(in[1] >> 4)
buf[22] = byte(in[1] >> 12)
buf[21] = byte(in[1] >> 20)
buf[20] = byte(in[2])
buf[19] = byte(in[2] >> 8)
buf[18] = byte(in[2] >> 16)
buf[17] = byte(((in[2] >> 24) & 0x0f) | (in[3]<<4)&0xf0)
buf[16] = byte(in[3] >> 4)
buf[15] = byte(in[3] >> 12)
buf[14] = byte(in[3] >> 20)
buf[13] = byte(in[4])
buf[12] = byte(in[4] >> 8)
buf[11] = byte(in[4] >> 16)
buf[10] = byte(((in[4] >> 24) & 0x0f) | (in[5]<<4)&0xf0)
buf[9] = byte(in[5] >> 4)
buf[8] = byte(in[5] >> 12)
buf[7] = byte(in[5] >> 20)
buf[6] = byte(in[6])
buf[5] = byte(in[6] >> 8)
buf[4] = byte(in[6] >> 16)
buf[3] = byte(((in[6] >> 24) & 0x0f) | (in[7]<<4)&0xf0)
buf[2] = byte(in[7] >> 4)
buf[1] = byte(in[7] >> 12)
buf[0] = byte(in[7] >> 20)
return new(big.Int).SetBytes(buf[:])
func (p224Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
p1, ok := p224PointFromAffine(x1, y1)
if !ok {
return p224RandomPoint()
}
p2, ok := p224PointFromAffine(x2, y2)
if !ok {
return p224RandomPoint()
}
return p224PointToAffine(p1.Add(p1, p2))
}
func (p224Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
p, ok := p224PointFromAffine(x1, y1)
if !ok {
return p224RandomPoint()
}
return p224PointToAffine(p.Double(p))
}
func (p224Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
p, ok := p224PointFromAffine(Bx, By)
if !ok {
return p224RandomPoint()
}
return p224PointToAffine(p.ScalarMult(p, scalar))
}
func (p224Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
p := nistec.NewP224Generator()
return p224PointToAffine(p.ScalarMult(p, scalar))
}

View File

@ -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
@ -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 {

View File

@ -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.
@ -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
)

View File

@ -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 {
@ -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)

141
src/crypto/elliptic/p384.go Normal file
View File

@ -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))
}

View File

@ -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()
@ -124,7 +124,7 @@ 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()
@ -132,7 +132,7 @@ func (curve p521Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
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()
@ -140,7 +140,7 @@ func (curve p521Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *bi
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))
}