first commit
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
name: Go tests
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
name: Go ${{ matrix.go }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go: [ '1.24', '1.x' ]
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with: { go-version: "${{ matrix.go }}" }
|
||||
- uses: actions/checkout@v2
|
||||
- run: go test -short ./...
|
||||
- run: go test -short -tags purego ./...
|
||||
- run: GOARCH=arm64 go test -c
|
||||
- run: GOARCH=arm go test -c
|
||||
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,16 @@
|
||||
# filippo.io/edwards25519
|
||||
|
||||
```
|
||||
import "filippo.io/edwards25519"
|
||||
```
|
||||
|
||||
This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives.
|
||||
Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519).
|
||||
|
||||
The package tracks the upstream standard library package `crypto/internal/fips140/edwards25519` and extends it with additional functionality.
|
||||
|
||||
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17.
|
||||
|
||||
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `crypto/ecdh` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of the internal `edwards25519` package or of `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
|
||||
|
||||
Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements.
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 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 edwards25519 implements group logic for the twisted Edwards curve
|
||||
//
|
||||
// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
|
||||
//
|
||||
// This is better known as the Edwards curve equivalent to Curve25519, and is
|
||||
// the curve used by the Ed25519 signature scheme.
|
||||
//
|
||||
// Most users don't need this package, and should instead use crypto/ed25519 for
|
||||
// signatures, crypto/ecdh for Diffie-Hellman, or github.com/gtank/ristretto255
|
||||
// for prime order group logic.
|
||||
//
|
||||
// However, developers who do need to interact with low-level edwards25519
|
||||
// operations can use this package, which is an extended version of
|
||||
// crypto/internal/fips140/edwards25519 from the standard library repackaged as
|
||||
// an importable module.
|
||||
package edwards25519
|
||||
@@ -0,0 +1,427 @@
|
||||
// Copyright (c) 2017 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 edwards25519
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"filippo.io/edwards25519/field"
|
||||
)
|
||||
|
||||
// Point types.
|
||||
|
||||
type projP1xP1 struct {
|
||||
X, Y, Z, T field.Element
|
||||
}
|
||||
|
||||
type projP2 struct {
|
||||
X, Y, Z field.Element
|
||||
}
|
||||
|
||||
// Point represents a point on the edwards25519 curve.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||
// are allowed to alias.
|
||||
//
|
||||
// The zero value is NOT valid, and it may be used only as a receiver.
|
||||
type Point struct {
|
||||
// Make the type not comparable (i.e. used with == or as a map key), as
|
||||
// equivalent points can be represented by different Go values.
|
||||
_ incomparable
|
||||
|
||||
// The point is internally represented in extended coordinates (X, Y, Z, T)
|
||||
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
|
||||
x, y, z, t field.Element
|
||||
}
|
||||
|
||||
type incomparable [0]func()
|
||||
|
||||
func checkInitialized(points ...*Point) {
|
||||
for _, p := range points {
|
||||
if p.x == (field.Element{}) && p.y == (field.Element{}) {
|
||||
panic("edwards25519: use of uninitialized Point")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type projCached struct {
|
||||
YplusX, YminusX, Z, T2d field.Element
|
||||
}
|
||||
|
||||
type affineCached struct {
|
||||
YplusX, YminusX, T2d field.Element
|
||||
}
|
||||
|
||||
// Constructors.
|
||||
|
||||
func (v *projP2) Zero() *projP2 {
|
||||
v.X.Zero()
|
||||
v.Y.One()
|
||||
v.Z.One()
|
||||
return v
|
||||
}
|
||||
|
||||
// identity is the point at infinity.
|
||||
var identity, _ = new(Point).SetBytes([]byte{
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
|
||||
// NewIdentityPoint returns a new Point set to the identity.
|
||||
func NewIdentityPoint() *Point {
|
||||
return new(Point).Set(identity)
|
||||
}
|
||||
|
||||
// generator is the canonical curve basepoint. See TestGenerator for the
|
||||
// correspondence of this encoding with the values in RFC 8032.
|
||||
var generator, _ = new(Point).SetBytes([]byte{
|
||||
0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})
|
||||
|
||||
// NewGeneratorPoint returns a new Point set to the canonical generator.
|
||||
func NewGeneratorPoint() *Point {
|
||||
return new(Point).Set(generator)
|
||||
}
|
||||
|
||||
func (v *projCached) Zero() *projCached {
|
||||
v.YplusX.One()
|
||||
v.YminusX.One()
|
||||
v.Z.One()
|
||||
v.T2d.Zero()
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *affineCached) Zero() *affineCached {
|
||||
v.YplusX.One()
|
||||
v.YminusX.One()
|
||||
v.T2d.Zero()
|
||||
return v
|
||||
}
|
||||
|
||||
// Assignments.
|
||||
|
||||
// Set sets v = u, and returns v.
|
||||
func (v *Point) Set(u *Point) *Point {
|
||||
*v = *u
|
||||
return v
|
||||
}
|
||||
|
||||
// Encoding.
|
||||
|
||||
// Bytes returns the canonical 32-byte encoding of v, according to RFC 8032,
|
||||
// Section 5.1.2.
|
||||
func (v *Point) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var buf [32]byte
|
||||
return v.bytes(&buf)
|
||||
}
|
||||
|
||||
func (v *Point) bytes(buf *[32]byte) []byte {
|
||||
checkInitialized(v)
|
||||
|
||||
var zInv, x, y field.Element
|
||||
zInv.Invert(&v.z) // zInv = 1 / Z
|
||||
x.Multiply(&v.x, &zInv) // x = X / Z
|
||||
y.Multiply(&v.y, &zInv) // y = Y / Z
|
||||
|
||||
out := copyFieldElement(buf, &y)
|
||||
out[31] |= byte(x.IsNegative() << 7)
|
||||
return out
|
||||
}
|
||||
|
||||
var feOne = new(field.Element).One()
|
||||
|
||||
// SetBytes sets v = x, where x is a 32-byte encoding of v. If x does not
|
||||
// represent a valid point on the curve, SetBytes returns nil and an error and
|
||||
// the receiver is unchanged. Otherwise, SetBytes returns v.
|
||||
//
|
||||
// Note that SetBytes accepts all non-canonical encodings of valid points.
|
||||
// That is, it follows decoding rules that match most implementations in
|
||||
// the ecosystem rather than RFC 8032.
|
||||
func (v *Point) SetBytes(x []byte) (*Point, error) {
|
||||
// Specifically, the non-canonical encodings that are accepted are
|
||||
// 1) the ones where the field element is not reduced (see the
|
||||
// (*field.Element).SetBytes docs) and
|
||||
// 2) the ones where the x-coordinate is zero and the sign bit is set.
|
||||
//
|
||||
// Read more at https://hdevalence.ca/blog/2020-10-04-its-25519am,
|
||||
// specifically the "Canonical A, R" section.
|
||||
|
||||
y, err := new(field.Element).SetBytes(x)
|
||||
if err != nil {
|
||||
return nil, errors.New("edwards25519: invalid point encoding length")
|
||||
}
|
||||
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// x² + dx²y² = x²(dy² + 1) = y² - 1
|
||||
// x² = (y² - 1) / (dy² + 1)
|
||||
|
||||
// u = y² - 1
|
||||
y2 := new(field.Element).Square(y)
|
||||
u := new(field.Element).Subtract(y2, feOne)
|
||||
|
||||
// v = dy² + 1
|
||||
vv := new(field.Element).Multiply(y2, d)
|
||||
vv = vv.Add(vv, feOne)
|
||||
|
||||
// x = +√(u/v)
|
||||
xx, wasSquare := new(field.Element).SqrtRatio(u, vv)
|
||||
if wasSquare == 0 {
|
||||
return nil, errors.New("edwards25519: invalid point encoding")
|
||||
}
|
||||
|
||||
// Select the negative square root if the sign bit is set.
|
||||
xxNeg := new(field.Element).Negate(xx)
|
||||
xx = xx.Select(xxNeg, xx, int(x[31]>>7))
|
||||
|
||||
v.x.Set(xx)
|
||||
v.y.Set(y)
|
||||
v.z.One()
|
||||
v.t.Multiply(xx, y) // xy = T / Z
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func copyFieldElement(buf *[32]byte, v *field.Element) []byte {
|
||||
copy(buf[:], v.Bytes())
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
// Conversions.
|
||||
|
||||
func (v *projP2) FromP1xP1(p *projP1xP1) *projP2 {
|
||||
v.X.Multiply(&p.X, &p.T)
|
||||
v.Y.Multiply(&p.Y, &p.Z)
|
||||
v.Z.Multiply(&p.Z, &p.T)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP2) FromP3(p *Point) *projP2 {
|
||||
v.X.Set(&p.x)
|
||||
v.Y.Set(&p.y)
|
||||
v.Z.Set(&p.z)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Point) fromP1xP1(p *projP1xP1) *Point {
|
||||
v.x.Multiply(&p.X, &p.T)
|
||||
v.y.Multiply(&p.Y, &p.Z)
|
||||
v.z.Multiply(&p.Z, &p.T)
|
||||
v.t.Multiply(&p.X, &p.Y)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Point) fromP2(p *projP2) *Point {
|
||||
v.x.Multiply(&p.X, &p.Z)
|
||||
v.y.Multiply(&p.Y, &p.Z)
|
||||
v.z.Square(&p.Z)
|
||||
v.t.Multiply(&p.X, &p.Y)
|
||||
return v
|
||||
}
|
||||
|
||||
// d is a constant in the curve equation.
|
||||
var d, _ = new(field.Element).SetBytes([]byte{
|
||||
0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75,
|
||||
0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00,
|
||||
0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c,
|
||||
0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52})
|
||||
var d2 = new(field.Element).Add(d, d)
|
||||
|
||||
func (v *projCached) FromP3(p *Point) *projCached {
|
||||
v.YplusX.Add(&p.y, &p.x)
|
||||
v.YminusX.Subtract(&p.y, &p.x)
|
||||
v.Z.Set(&p.z)
|
||||
v.T2d.Multiply(&p.t, d2)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *affineCached) FromP3(p *Point) *affineCached {
|
||||
v.YplusX.Add(&p.y, &p.x)
|
||||
v.YminusX.Subtract(&p.y, &p.x)
|
||||
v.T2d.Multiply(&p.t, d2)
|
||||
|
||||
var invZ field.Element
|
||||
invZ.Invert(&p.z)
|
||||
v.YplusX.Multiply(&v.YplusX, &invZ)
|
||||
v.YminusX.Multiply(&v.YminusX, &invZ)
|
||||
v.T2d.Multiply(&v.T2d, &invZ)
|
||||
return v
|
||||
}
|
||||
|
||||
// (Re)addition and subtraction.
|
||||
|
||||
// Add sets v = p + q, and returns v.
|
||||
func (v *Point) Add(p, q *Point) *Point {
|
||||
checkInitialized(p, q)
|
||||
qCached := new(projCached).FromP3(q)
|
||||
result := new(projP1xP1).Add(p, qCached)
|
||||
return v.fromP1xP1(result)
|
||||
}
|
||||
|
||||
// Subtract sets v = p - q, and returns v.
|
||||
func (v *Point) Subtract(p, q *Point) *Point {
|
||||
checkInitialized(p, q)
|
||||
qCached := new(projCached).FromP3(q)
|
||||
result := new(projP1xP1).Sub(p, qCached)
|
||||
return v.fromP1xP1(result)
|
||||
}
|
||||
|
||||
func (v *projP1xP1) Add(p *Point, q *projCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YplusX)
|
||||
MM.Multiply(&YminusX, &q.YminusX)
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
ZZ2.Multiply(&p.z, &q.Z)
|
||||
|
||||
ZZ2.Add(&ZZ2, &ZZ2)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Add(&ZZ2, &TT2d)
|
||||
v.T.Subtract(&ZZ2, &TT2d)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP1xP1) Sub(p *Point, q *projCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
||||
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
ZZ2.Multiply(&p.z, &q.Z)
|
||||
|
||||
ZZ2.Add(&ZZ2, &ZZ2)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Subtract(&ZZ2, &TT2d) // flipped sign
|
||||
v.T.Add(&ZZ2, &TT2d) // flipped sign
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP1xP1) AddAffine(p *Point, q *affineCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YplusX)
|
||||
MM.Multiply(&YminusX, &q.YminusX)
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
|
||||
Z2.Add(&p.z, &p.z)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Add(&Z2, &TT2d)
|
||||
v.T.Subtract(&Z2, &TT2d)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP1xP1) SubAffine(p *Point, q *affineCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
||||
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
|
||||
Z2.Add(&p.z, &p.z)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Subtract(&Z2, &TT2d) // flipped sign
|
||||
v.T.Add(&Z2, &TT2d) // flipped sign
|
||||
return v
|
||||
}
|
||||
|
||||
// Doubling.
|
||||
|
||||
func (v *projP1xP1) Double(p *projP2) *projP1xP1 {
|
||||
var XX, YY, ZZ2, XplusYsq field.Element
|
||||
|
||||
XX.Square(&p.X)
|
||||
YY.Square(&p.Y)
|
||||
ZZ2.Square(&p.Z)
|
||||
ZZ2.Add(&ZZ2, &ZZ2)
|
||||
XplusYsq.Add(&p.X, &p.Y)
|
||||
XplusYsq.Square(&XplusYsq)
|
||||
|
||||
v.Y.Add(&YY, &XX)
|
||||
v.Z.Subtract(&YY, &XX)
|
||||
|
||||
v.X.Subtract(&XplusYsq, &v.Y)
|
||||
v.T.Subtract(&ZZ2, &v.Z)
|
||||
return v
|
||||
}
|
||||
|
||||
// Negation.
|
||||
|
||||
// Negate sets v = -p, and returns v.
|
||||
func (v *Point) Negate(p *Point) *Point {
|
||||
checkInitialized(p)
|
||||
v.x.Negate(&p.x)
|
||||
v.y.Set(&p.y)
|
||||
v.z.Set(&p.z)
|
||||
v.t.Negate(&p.t)
|
||||
return v
|
||||
}
|
||||
|
||||
// Equal returns 1 if v is equivalent to u, and 0 otherwise.
|
||||
func (v *Point) Equal(u *Point) int {
|
||||
checkInitialized(v, u)
|
||||
|
||||
var t1, t2, t3, t4 field.Element
|
||||
t1.Multiply(&v.x, &u.z)
|
||||
t2.Multiply(&u.x, &v.z)
|
||||
t3.Multiply(&v.y, &u.z)
|
||||
t4.Multiply(&u.y, &v.z)
|
||||
|
||||
return t1.Equal(&t2) & t3.Equal(&t4)
|
||||
}
|
||||
|
||||
// Constant-time operations
|
||||
|
||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||
func (v *projCached) Select(a, b *projCached, cond int) *projCached {
|
||||
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||
v.Z.Select(&a.Z, &b.Z, cond)
|
||||
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||
func (v *affineCached) Select(a, b *affineCached, cond int) *affineCached {
|
||||
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||
func (v *projCached) CondNeg(cond int) *projCached {
|
||||
v.YplusX.Swap(&v.YminusX, cond)
|
||||
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||
func (v *affineCached) CondNeg(cond int) *affineCached {
|
||||
v.YplusX.Swap(&v.YminusX, cond)
|
||||
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
// Copyright (c) 2019 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 edwards25519
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"filippo.io/edwards25519/field"
|
||||
)
|
||||
|
||||
var B = NewGeneratorPoint()
|
||||
var I = NewIdentityPoint()
|
||||
|
||||
func checkOnCurve(t *testing.T, points ...*Point) {
|
||||
t.Helper()
|
||||
for i, p := range points {
|
||||
if p.z.Equal(new(field.Element)) == 1 {
|
||||
t.Errorf("point %d has Z == 0 (degenerate projective point)", i)
|
||||
}
|
||||
var XX, YY, ZZ, ZZZZ field.Element
|
||||
XX.Square(&p.x)
|
||||
YY.Square(&p.y)
|
||||
ZZ.Square(&p.z)
|
||||
ZZZZ.Square(&ZZ)
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// -(X/Z)² + (Y/Z)² = 1 + d(X/Z)²(Y/Z)²
|
||||
// (-X² + Y²)/Z² = 1 + (dX²Y²)/Z⁴
|
||||
// (-X² + Y²)*Z² = Z⁴ + dX²Y²
|
||||
var lhs, rhs field.Element
|
||||
lhs.Subtract(&YY, &XX).Multiply(&lhs, &ZZ)
|
||||
rhs.Multiply(d, &XX).Multiply(&rhs, &YY).Add(&rhs, &ZZZZ)
|
||||
if lhs.Equal(&rhs) != 1 {
|
||||
t.Errorf("X, Y, and Z do not specify a point on the curve\nX = %v\nY = %v\nZ = %v", p.x, p.y, p.z)
|
||||
}
|
||||
// xy = T/Z
|
||||
lhs.Multiply(&p.x, &p.y)
|
||||
rhs.Multiply(&p.z, &p.t)
|
||||
if lhs.Equal(&rhs) != 1 {
|
||||
t.Errorf("point %d is not valid\nX = %v\nY = %v\nZ = %v", i, p.x, p.y, p.z)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerator(t *testing.T) {
|
||||
// These are the coordinates of B from RFC 8032, Section 5.1, converted to
|
||||
// little endian hex.
|
||||
x := "1ad5258f602d56c9b2a7259560c72c695cdcd6fd31e2a4c0fe536ecdd3366921"
|
||||
y := "5866666666666666666666666666666666666666666666666666666666666666"
|
||||
if got := hex.EncodeToString(B.x.Bytes()); got != x {
|
||||
t.Errorf("wrong B.x: got %s, expected %s", got, x)
|
||||
}
|
||||
if got := hex.EncodeToString(B.y.Bytes()); got != y {
|
||||
t.Errorf("wrong B.y: got %s, expected %s", got, y)
|
||||
}
|
||||
if B.z.Equal(feOne) != 1 {
|
||||
t.Errorf("wrong B.z: got %v, expected 1", B.z)
|
||||
}
|
||||
// Check that t is correct.
|
||||
checkOnCurve(t, B)
|
||||
}
|
||||
|
||||
func TestAddSubNegOnBasePoint(t *testing.T) {
|
||||
checkLhs, checkRhs := &Point{}, &Point{}
|
||||
|
||||
checkLhs.Add(B, B)
|
||||
tmpP2 := new(projP2).FromP3(B)
|
||||
tmpP1xP1 := new(projP1xP1).Double(tmpP2)
|
||||
checkRhs.fromP1xP1(tmpP1xP1)
|
||||
if checkLhs.Equal(checkRhs) != 1 {
|
||||
t.Error("B + B != [2]B")
|
||||
}
|
||||
checkOnCurve(t, checkLhs, checkRhs)
|
||||
|
||||
checkLhs.Subtract(B, B)
|
||||
Bneg := new(Point).Negate(B)
|
||||
checkRhs.Add(B, Bneg)
|
||||
if checkLhs.Equal(checkRhs) != 1 {
|
||||
t.Error("B - B != B + (-B)")
|
||||
}
|
||||
if I.Equal(checkLhs) != 1 {
|
||||
t.Error("B - B != 0")
|
||||
}
|
||||
if I.Equal(checkRhs) != 1 {
|
||||
t.Error("B + (-B) != 0")
|
||||
}
|
||||
checkOnCurve(t, checkLhs, checkRhs, Bneg)
|
||||
}
|
||||
|
||||
func TestComparable(t *testing.T) {
|
||||
if reflect.TypeOf(Point{}).Comparable() {
|
||||
t.Error("Point is unexpectedly comparable")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidEncodings(t *testing.T) {
|
||||
// An invalid point, that also happens to have y > p.
|
||||
invalid := "efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"
|
||||
p := NewGeneratorPoint()
|
||||
if out, err := p.SetBytes(decodeHex(invalid)); err == nil {
|
||||
t.Error("expected error for invalid point")
|
||||
} else if out != nil {
|
||||
t.Error("SetBytes did not return nil on an invalid encoding")
|
||||
} else if p.Equal(B) != 1 {
|
||||
t.Error("the Point was modified while decoding an invalid encoding")
|
||||
}
|
||||
checkOnCurve(t, p)
|
||||
}
|
||||
|
||||
func TestNonCanonicalPoints(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
encoding, canonical string
|
||||
}
|
||||
tests := []test{
|
||||
// Points with x = 0 and the sign bit set. With x = 0 the curve equation
|
||||
// gives y² = 1, so y = ±1. 1 has two valid encodings.
|
||||
{
|
||||
"y=1,sign-",
|
||||
"0100000000000000000000000000000000000000000000000000000000000080",
|
||||
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+1,sign-",
|
||||
"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p-1,sign-",
|
||||
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
},
|
||||
|
||||
// Non-canonical y encodings with values 2²⁵⁵-19 (p) to 2²⁵⁵-1 (p+18).
|
||||
{
|
||||
"y=p,sign+",
|
||||
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p,sign-",
|
||||
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0000000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
{
|
||||
"y=p+1,sign+",
|
||||
"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
// "y=p+1,sign-" is already tested above.
|
||||
// p+2 is not a valid y-coordinate.
|
||||
{
|
||||
"y=p+3,sign+",
|
||||
"f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0300000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+3,sign-",
|
||||
"f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0300000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
{
|
||||
"y=p+4,sign+",
|
||||
"f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0400000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+4,sign-",
|
||||
"f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0400000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
{
|
||||
"y=p+5,sign+",
|
||||
"f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0500000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+5,sign-",
|
||||
"f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0500000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
{
|
||||
"y=p+6,sign+",
|
||||
"f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0600000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+6,sign-",
|
||||
"f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0600000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
// p+7 is not a valid y-coordinate.
|
||||
// p+8 is not a valid y-coordinate.
|
||||
{
|
||||
"y=p+9,sign+",
|
||||
"f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0900000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+9,sign-",
|
||||
"f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0900000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
{
|
||||
"y=p+10,sign+",
|
||||
"f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0a00000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+10,sign-",
|
||||
"f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0a00000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
// p+11 is not a valid y-coordinate.
|
||||
// p+12 is not a valid y-coordinate.
|
||||
// p+13 is not a valid y-coordinate.
|
||||
{
|
||||
"y=p+14,sign+",
|
||||
"fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0e00000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+14,sign-",
|
||||
"fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0e00000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
{
|
||||
"y=p+15,sign+",
|
||||
"fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"0f00000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+15,sign-",
|
||||
"fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"0f00000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
{
|
||||
"y=p+16,sign+",
|
||||
"fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"1000000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+16,sign-",
|
||||
"fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"1000000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
// p+17 is not a valid y-coordinate.
|
||||
{
|
||||
"y=p+18,sign+",
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||
"1200000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
{
|
||||
"y=p+18,sign-",
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"1200000000000000000000000000000000000000000000000000000000000080",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p1, err := new(Point).SetBytes(decodeHex(tt.encoding))
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding non-canonical point: %v", err)
|
||||
}
|
||||
p2, err := new(Point).SetBytes(decodeHex(tt.canonical))
|
||||
if err != nil {
|
||||
t.Fatalf("error decoding canonical point: %v", err)
|
||||
}
|
||||
if p1.Equal(p2) != 1 {
|
||||
t.Errorf("equivalent points are not equal: %v, %v", p1, p2)
|
||||
}
|
||||
if encoding := hex.EncodeToString(p1.Bytes()); encoding != tt.canonical {
|
||||
t.Errorf("re-encoding does not match canonical; got %q, expected %q", encoding, tt.canonical)
|
||||
}
|
||||
checkOnCurve(t, p1, p2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var testAllocationsSink byte
|
||||
|
||||
func TestAllocations(t *testing.T) {
|
||||
if allocs := testing.AllocsPerRun(100, func() {
|
||||
p := NewIdentityPoint()
|
||||
p.Add(p, NewGeneratorPoint())
|
||||
s := NewScalar()
|
||||
testAllocationsSink ^= s.Bytes()[0]
|
||||
testAllocationsSink ^= p.Bytes()[0]
|
||||
}); allocs > 0 {
|
||||
t.Errorf("expected zero allocations, got %0.1v", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeHex(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func BenchmarkEncodingDecoding(b *testing.B) {
|
||||
p := new(Point).Set(dalekScalarBasepoint)
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := p.Bytes()
|
||||
_, err := p.SetBytes(buf)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
// Copyright (c) 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 edwards25519
|
||||
|
||||
// This file contains additional functionality that is not included in the
|
||||
// upstream crypto/internal/edwards25519 package.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
"filippo.io/edwards25519/field"
|
||||
)
|
||||
|
||||
// ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where
|
||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||
func (v *Point) ExtendedCoordinates() (X, Y, Z, T *field.Element) {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap. Don't change the style without making
|
||||
// sure it doesn't increase the inliner cost.
|
||||
var e [4]field.Element
|
||||
X, Y, Z, T = v.extendedCoordinates(&e)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Point) extendedCoordinates(e *[4]field.Element) (X, Y, Z, T *field.Element) {
|
||||
checkInitialized(v)
|
||||
X = e[0].Set(&v.x)
|
||||
Y = e[1].Set(&v.y)
|
||||
Z = e[2].Set(&v.z)
|
||||
T = e[3].Set(&v.t)
|
||||
return
|
||||
}
|
||||
|
||||
// SetExtendedCoordinates sets v = (X:Y:Z:T) in extended coordinates where
|
||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||
//
|
||||
// If the coordinates are invalid or don't represent a valid point on the curve,
|
||||
// SetExtendedCoordinates returns nil and an error and the receiver is
|
||||
// unchanged. Otherwise, SetExtendedCoordinates returns v.
|
||||
func (v *Point) SetExtendedCoordinates(X, Y, Z, T *field.Element) (*Point, error) {
|
||||
if !isOnCurve(X, Y, Z, T) {
|
||||
return nil, errors.New("edwards25519: invalid point coordinates")
|
||||
}
|
||||
v.x.Set(X)
|
||||
v.y.Set(Y)
|
||||
v.z.Set(Z)
|
||||
v.t.Set(T)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func isOnCurve(X, Y, Z, T *field.Element) bool {
|
||||
var lhs, rhs field.Element
|
||||
XX := new(field.Element).Square(X)
|
||||
YY := new(field.Element).Square(Y)
|
||||
ZZ := new(field.Element).Square(Z)
|
||||
TT := new(field.Element).Square(T)
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// -(X/Z)² + (Y/Z)² = 1 + d(T/Z)²
|
||||
// -X² + Y² = Z² + dT²
|
||||
lhs.Subtract(YY, XX)
|
||||
rhs.Multiply(d, TT).Add(&rhs, ZZ)
|
||||
if lhs.Equal(&rhs) != 1 {
|
||||
return false
|
||||
}
|
||||
// xy = T/Z
|
||||
// XY/Z² = T/Z
|
||||
// XY = TZ
|
||||
lhs.Multiply(X, Y)
|
||||
rhs.Multiply(T, Z)
|
||||
return lhs.Equal(&rhs) == 1
|
||||
}
|
||||
|
||||
// BytesMontgomery converts v to a point on the birationally-equivalent
|
||||
// Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding
|
||||
// according to RFC 7748.
|
||||
//
|
||||
// Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode
|
||||
// to the same value. If v is the identity point, BytesMontgomery returns 32
|
||||
// zero bytes, analogously to the X25519 function.
|
||||
//
|
||||
// The lack of an inverse operation (such as SetMontgomeryBytes) is deliberate:
|
||||
// while every valid edwards25519 point has a unique u-coordinate Montgomery
|
||||
// encoding, X25519 accepts inputs on the quadratic twist, which don't correspond
|
||||
// to any edwards25519 point, and every other X25519 input corresponds to two
|
||||
// edwards25519 points.
|
||||
func (v *Point) BytesMontgomery() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var buf [32]byte
|
||||
return v.bytesMontgomery(&buf)
|
||||
}
|
||||
|
||||
func (v *Point) bytesMontgomery(buf *[32]byte) []byte {
|
||||
checkInitialized(v)
|
||||
|
||||
// RFC 7748, Section 4.1 provides the bilinear map to calculate the
|
||||
// Montgomery u-coordinate
|
||||
//
|
||||
// u = (1 + y) / (1 - y)
|
||||
//
|
||||
// where y = Y / Z and therefore
|
||||
//
|
||||
// u = (Z + Y) / (Z - Y)
|
||||
|
||||
var n, r, u field.Element
|
||||
|
||||
n.Add(&v.z, &v.y) // n = Z + Y
|
||||
r.Invert(r.Subtract(&v.z, &v.y)) // r = 1 / (Z - Y)
|
||||
u.Multiply(&n, &r) // u = n * r
|
||||
|
||||
return copyFieldElement(buf, &u)
|
||||
}
|
||||
|
||||
// MultByCofactor sets v = 8 * p, and returns v.
|
||||
func (v *Point) MultByCofactor(p *Point) *Point {
|
||||
checkInitialized(p)
|
||||
result := projP1xP1{}
|
||||
pp := (&projP2{}).FromP3(p)
|
||||
result.Double(pp)
|
||||
pp.FromP1xP1(&result)
|
||||
result.Double(pp)
|
||||
pp.FromP1xP1(&result)
|
||||
result.Double(pp)
|
||||
return v.fromP1xP1(&result)
|
||||
}
|
||||
|
||||
// Given k > 0, set s = s**(2*k).
|
||||
func (s *Scalar) pow2k(k int) {
|
||||
for i := 0; i < k; i++ {
|
||||
s.Multiply(s, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Invert sets s to the inverse of a nonzero scalar v, and returns s.
|
||||
//
|
||||
// If t is zero, Invert returns zero.
|
||||
func (s *Scalar) Invert(t *Scalar) *Scalar {
|
||||
// Uses a hardcoded sliding window of width 4.
|
||||
var table [8]Scalar
|
||||
var tt Scalar
|
||||
tt.Multiply(t, t)
|
||||
table[0] = *t
|
||||
for i := 0; i < 7; i++ {
|
||||
table[i+1].Multiply(&table[i], &tt)
|
||||
}
|
||||
// Now table = [t**1, t**3, t**5, t**7, t**9, t**11, t**13, t**15]
|
||||
// so t**k = t[k/2] for odd k
|
||||
|
||||
// To compute the sliding window digits, use the following Sage script:
|
||||
|
||||
// sage: import itertools
|
||||
// sage: def sliding_window(w,k):
|
||||
// ....: digits = []
|
||||
// ....: while k > 0:
|
||||
// ....: if k % 2 == 1:
|
||||
// ....: kmod = k % (2**w)
|
||||
// ....: digits.append(kmod)
|
||||
// ....: k = k - kmod
|
||||
// ....: else:
|
||||
// ....: digits.append(0)
|
||||
// ....: k = k // 2
|
||||
// ....: return digits
|
||||
|
||||
// Now we can compute s roughly as follows:
|
||||
|
||||
// sage: s = 1
|
||||
// sage: for coeff in reversed(sliding_window(4,l-2)):
|
||||
// ....: s = s*s
|
||||
// ....: if coeff > 0 :
|
||||
// ....: s = s*t**coeff
|
||||
|
||||
// This works on one bit at a time, with many runs of zeros.
|
||||
// The digits can be collapsed into [(count, coeff)] as follows:
|
||||
|
||||
// sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))]
|
||||
|
||||
// Entries of the form (k, 0) turn into pow2k(k)
|
||||
// Entries of the form (1, coeff) turn into a squaring and then a table lookup.
|
||||
// We can fold the squaring into the previous pow2k(k) as pow2k(k+1).
|
||||
|
||||
*s = table[1/2]
|
||||
s.pow2k(127 + 1)
|
||||
s.Multiply(s, &table[1/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[13/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[5/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[1/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
s.pow2k(5 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
s.pow2k(9 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[13/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// MultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||
//
|
||||
// Execution time depends only on the lengths of the two slices, which must match.
|
||||
func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
||||
if len(scalars) != len(points) {
|
||||
panic("edwards25519: called MultiScalarMult with different size inputs")
|
||||
}
|
||||
checkInitialized(points...)
|
||||
|
||||
// Proceed as in the single-base case, but share doublings
|
||||
// between each point in the multiscalar equation.
|
||||
|
||||
// Build lookup tables for each point
|
||||
tables := make([]projLookupTable, 0, 2) // avoid allocation for small sizes
|
||||
tables = slices.Grow(tables, len(points))[:len(points)]
|
||||
for i := range tables {
|
||||
tables[i].FromP3(points[i])
|
||||
}
|
||||
// Compute signed radix-16 digits for each scalar
|
||||
digits := make([][64]int8, 0, 2) // avoid allocation for small sizes
|
||||
digits = slices.Grow(digits, len(scalars))[:len(scalars)]
|
||||
for i := range digits {
|
||||
digits[i] = scalars[i].signedRadix16()
|
||||
}
|
||||
|
||||
// Unwrap first loop iteration to save computing 16*identity
|
||||
multiple := &projCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
// Lookup-and-add the appropriate multiple of each input point
|
||||
v.Set(NewIdentityPoint())
|
||||
for j := range tables {
|
||||
tables[j].SelectInto(multiple, digits[j][63])
|
||||
tmp1.Add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // update v
|
||||
}
|
||||
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||
for i := 62; i >= 0; i-- {
|
||||
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
||||
// Lookup-and-add the appropriate multiple of each input point
|
||||
for j := range tables {
|
||||
tables[j].SelectInto(multiple, digits[j][i])
|
||||
tmp1.Add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // update v
|
||||
}
|
||||
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// VarTimeMultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||
//
|
||||
// Execution time depends on the inputs.
|
||||
func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
||||
if len(scalars) != len(points) {
|
||||
panic("edwards25519: called VarTimeMultiScalarMult with different size inputs")
|
||||
}
|
||||
checkInitialized(points...)
|
||||
|
||||
// Generalize double-base NAF computation to arbitrary sizes.
|
||||
// Here all the points are dynamic, so we only use the smaller
|
||||
// tables.
|
||||
|
||||
// Build lookup tables for each point
|
||||
tables := make([]nafLookupTable5, len(points))
|
||||
for i := range tables {
|
||||
tables[i].FromP3(points[i])
|
||||
}
|
||||
// Compute a NAF for each scalar
|
||||
nafs := make([][256]int8, len(scalars))
|
||||
for i := range nafs {
|
||||
nafs[i] = scalars[i].nonAdjacentForm(5)
|
||||
}
|
||||
|
||||
multiple := &projCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
tmp2.Zero()
|
||||
|
||||
// Move from high to low bits, doubling the accumulator
|
||||
// at each iteration and checking whether there is a nonzero
|
||||
// coefficient to look up a multiple of.
|
||||
//
|
||||
// Skip trying to find the first nonzero coefficent, because
|
||||
// searching might be more work than a few extra doublings.
|
||||
for i := 255; i >= 0; i-- {
|
||||
tmp1.Double(tmp2)
|
||||
|
||||
for j := range nafs {
|
||||
if nafs[j][i] > 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
tables[j].SelectInto(multiple, nafs[j][i])
|
||||
tmp1.Add(v, multiple)
|
||||
} else if nafs[j][i] < 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
tables[j].SelectInto(multiple, -nafs[j][i])
|
||||
tmp1.Sub(v, multiple)
|
||||
}
|
||||
}
|
||||
|
||||
tmp2.FromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
v.fromP2(tmp2)
|
||||
return v
|
||||
}
|
||||
|
||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||
func (v *Point) Select(a, b *Point, cond int) *Point {
|
||||
checkInitialized(a, b)
|
||||
v.x.Select(&a.x, &b.x, cond)
|
||||
v.y.Select(&a.y, &b.y, cond)
|
||||
v.z.Select(&a.z, &b.z, cond)
|
||||
v.t.Select(&a.t, &b.t, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// Double sets v = p + p, and returns v.
|
||||
func (v *Point) Double(p *Point) *Point {
|
||||
checkInitialized(p)
|
||||
|
||||
pp := new(projP2).FromP3(p)
|
||||
p1 := new(projP1xP1).Double(pp)
|
||||
return v.fromP1xP1(p1)
|
||||
}
|
||||
|
||||
func (v *Point) addCached(p *Point, qCached *projCached) *Point {
|
||||
result := new(projP1xP1).Add(p, qCached)
|
||||
return v.fromP1xP1(result)
|
||||
}
|
||||
|
||||
// ScalarMultSlow sets v = x * q, and returns v. It doesn't precompute a large
|
||||
// table, so it is considerably slower, but requires less memory.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
func (v *Point) ScalarMultSlow(x *Scalar, q *Point) *Point {
|
||||
checkInitialized(q)
|
||||
|
||||
s := x.Bytes()
|
||||
qCached := new(projCached).FromP3(q)
|
||||
v.Set(NewIdentityPoint())
|
||||
t := new(Point)
|
||||
|
||||
for i := 255; i >= 0; i-- {
|
||||
v.Double(v)
|
||||
t.addCached(v, qCached)
|
||||
cond := (s[i/8] >> (i % 8)) & 1
|
||||
v.Select(t, v, int(cond))
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
// Copyright (c) 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 edwards25519
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
// TestBytesMontgomery tests the SetBytesWithClamping+BytesMontgomery path
|
||||
// equivalence to curve25519.X25519 for basepoint scalar multiplications.
|
||||
//
|
||||
// Note that you can't actually implement X25519 with this package because
|
||||
// there is no SetBytesMontgomery, and it would not be possible to implement
|
||||
// it properly: points on the twist would get rejected, and the Scalar returned
|
||||
// by SetBytesWithClamping does not preserve its cofactor-clearing properties.
|
||||
//
|
||||
// Disabled to avoid the golang.org/x/crypto module dependency.
|
||||
/* func TestBytesMontgomery(t *testing.T) {
|
||||
f := func(scalar [32]byte) bool {
|
||||
s := NewScalar().SetBytesWithClamping(scalar[:])
|
||||
p := (&Point{}).ScalarBaseMult(s)
|
||||
got := p.BytesMontgomery()
|
||||
want, _ := curve25519.X25519(scalar[:], curve25519.Basepoint)
|
||||
return bytes.Equal(got, want)
|
||||
}
|
||||
if err := quick.Check(f, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
} */
|
||||
|
||||
func TestBytesMontgomerySodium(t *testing.T) {
|
||||
// Generated with libsodium.js 1.0.18
|
||||
// crypto_sign_keypair().publicKey
|
||||
publicKey := "3bf918ffc2c955dc895bf145f566fb96623c1cadbe040091175764b5fde322c0"
|
||||
p, err := (&Point{}).SetBytes(decodeHex(publicKey))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// crypto_sign_ed25519_pk_to_curve25519(publicKey)
|
||||
want := "efc6c9d0738e9ea18d738ad4a2653631558931b0f1fde4dd58c436d19686dc28"
|
||||
if got := hex.EncodeToString(p.BytesMontgomery()); got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesMontgomeryInfinity(t *testing.T) {
|
||||
p := NewIdentityPoint()
|
||||
want := "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
if got := hex.EncodeToString(p.BytesMontgomery()); got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultByCofactor(t *testing.T) {
|
||||
lowOrderBytes := "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85"
|
||||
lowOrder, err := (&Point{}).SetBytes(decodeHex(lowOrderBytes))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if p := (&Point{}).MultByCofactor(lowOrder); p.Equal(NewIdentityPoint()) != 1 {
|
||||
t.Errorf("expected low order point * cofactor to be the identity")
|
||||
}
|
||||
|
||||
f := func(scalar [64]byte) bool {
|
||||
s, _ := NewScalar().SetUniformBytes(scalar[:])
|
||||
p := (&Point{}).ScalarBaseMult(s)
|
||||
p8 := (&Point{}).MultByCofactor(p)
|
||||
checkOnCurve(t, p8)
|
||||
|
||||
// 8 * p == (8 * s) * B
|
||||
reprEight := [32]byte{8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
scEight, _ := (&Scalar{}).SetCanonicalBytes(reprEight[:])
|
||||
s.Multiply(s, scEight)
|
||||
pp := (&Point{}).ScalarBaseMult(s)
|
||||
if p8.Equal(pp) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 8 * p == 8 * (lowOrder + p)
|
||||
pp.Add(p, lowOrder)
|
||||
pp.MultByCofactor(pp)
|
||||
if p8.Equal(pp) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 8 * p == p + p + p + p + p + p + p + p
|
||||
pp.Set(NewIdentityPoint())
|
||||
for i := 0; i < 8; i++ {
|
||||
pp.Add(pp, p)
|
||||
}
|
||||
return p8.Equal(pp) == 1
|
||||
}
|
||||
if err := quick.Check(f, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarInvert(t *testing.T) {
|
||||
invertWorks := func(xInv Scalar, x notZeroScalar) bool {
|
||||
xInv.Invert((*Scalar)(&x))
|
||||
var check Scalar
|
||||
check.Multiply((*Scalar)(&x), &xInv)
|
||||
|
||||
return check.Equal(scOne) == 1 && isReduced(xInv.Bytes())
|
||||
}
|
||||
|
||||
if err := quick.Check(invertWorks, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
randomScalar := *dalekScalar
|
||||
randomInverse := NewScalar().Invert(&randomScalar)
|
||||
var check Scalar
|
||||
check.Multiply(&randomScalar, randomInverse)
|
||||
|
||||
if check.Equal(scOne) == 0 || !isReduced(randomInverse.Bytes()) {
|
||||
t.Error("inversion did not work")
|
||||
}
|
||||
|
||||
zero := NewScalar()
|
||||
if xx := NewScalar().Invert(zero); xx.Equal(zero) != 1 {
|
||||
t.Errorf("inverting zero did not return zero")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiScalarMultMatchesBaseMult(t *testing.T) {
|
||||
multiScalarMultMatchesBaseMult := func(x, y, z Scalar) bool {
|
||||
var p, q1, q2, q3, check Point
|
||||
|
||||
p.MultiScalarMult([]*Scalar{&x, &y, &z}, []*Point{B, B, B})
|
||||
|
||||
q1.ScalarBaseMult(&x)
|
||||
q2.ScalarBaseMult(&y)
|
||||
q3.ScalarBaseMult(&z)
|
||||
check.Add(&q1, &q2).Add(&check, &q3)
|
||||
|
||||
checkOnCurve(t, &p, &check, &q1, &q2, &q3)
|
||||
return p.Equal(&check) == 1
|
||||
}
|
||||
|
||||
if err := quick.Check(multiScalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiScalarMultZeroReceiver(t *testing.T) {
|
||||
// A zero-value (uninitialized) receiver should be handled correctly,
|
||||
// producing a valid point on the curve.
|
||||
var p Point
|
||||
p.MultiScalarMult([]*Scalar{dalekScalar}, []*Point{B})
|
||||
|
||||
var check Point
|
||||
check.ScalarBaseMult(dalekScalar)
|
||||
|
||||
checkOnCurve(t, &p, &check)
|
||||
if p.Equal(&check) != 1 {
|
||||
t.Error("MultiScalarMult with zero-value receiver did not match ScalarBaseMult")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiScalarMultReceiverAliasing(t *testing.T) {
|
||||
// The receiver v aliasing one of the input points should produce
|
||||
// the correct result.
|
||||
p := NewGeneratorPoint()
|
||||
p.MultiScalarMult([]*Scalar{dalekScalar}, []*Point{p})
|
||||
|
||||
var check Point
|
||||
check.ScalarBaseMult(dalekScalar)
|
||||
|
||||
checkOnCurve(t, p, &check)
|
||||
if p.Equal(&check) != 1 {
|
||||
t.Error("MultiScalarMult with aliased receiver did not match ScalarBaseMult")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVarTimeMultiScalarMultMatchesBaseMult(t *testing.T) {
|
||||
varTimeMultiScalarMultMatchesBaseMult := func(x, y, z Scalar) bool {
|
||||
var p, q1, q2, q3, check Point
|
||||
|
||||
p.VarTimeMultiScalarMult([]*Scalar{&x, &y, &z}, []*Point{B, B, B})
|
||||
|
||||
q1.ScalarBaseMult(&x)
|
||||
q2.ScalarBaseMult(&y)
|
||||
q3.ScalarBaseMult(&z)
|
||||
check.Add(&q1, &q2).Add(&check, &q3)
|
||||
|
||||
checkOnCurve(t, &p, &check, &q1, &q2, &q3)
|
||||
return p.Equal(&check) == 1
|
||||
}
|
||||
|
||||
if err := quick.Check(varTimeMultiScalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiScalarMult2NoAllocs(t *testing.T) {
|
||||
p := NewIdentityPoint()
|
||||
if allocs := testing.AllocsPerRun(100, func() {
|
||||
p.MultiScalarMult([]*Scalar{dalekScalar, dalekScalar}, []*Point{B, B})
|
||||
}); allocs != 0 {
|
||||
t.Errorf("MultiScalarMult allocated %v times, expected 0", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultSlowMatchesMult(t *testing.T) {
|
||||
scalarMultSlowMatchesMult := func(x, y Scalar) bool {
|
||||
p := NewGeneratorPoint()
|
||||
p.ScalarMultSlow(&x, p)
|
||||
p.ScalarMultSlow(&y, p)
|
||||
|
||||
q := NewGeneratorPoint()
|
||||
q.ScalarMult(&x, B)
|
||||
q.ScalarMult(&y, q)
|
||||
|
||||
checkOnCurve(t, p, q)
|
||||
return p.Equal(q) == 1
|
||||
}
|
||||
|
||||
if err := quick.Check(scalarMultSlowMatchesMult, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarMultSlow(b *testing.B) {
|
||||
var p Point
|
||||
x := dalekScalar
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.ScalarMultSlow(x, B)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMultiScalarMultSize8(t *testing.B) {
|
||||
var p Point
|
||||
x := dalekScalar
|
||||
|
||||
for i := 0; i < t.N; i++ {
|
||||
p.MultiScalarMult([]*Scalar{x, x, x, x, x, x, x, x},
|
||||
[]*Point{B, B, B, B, B, B, B, B})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarAddition(b *testing.B) {
|
||||
var rnd [128]byte
|
||||
rand.Read(rnd[:])
|
||||
s1, _ := (&Scalar{}).SetUniformBytes(rnd[0:64])
|
||||
s2, _ := (&Scalar{}).SetUniformBytes(rnd[64:128])
|
||||
t := &Scalar{}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
t.Add(s1, s2)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarMultiplication(b *testing.B) {
|
||||
var rnd [128]byte
|
||||
rand.Read(rnd[:])
|
||||
s1, _ := (&Scalar{}).SetUniformBytes(rnd[0:64])
|
||||
s2, _ := (&Scalar{}).SetUniformBytes(rnd[64:128])
|
||||
t := &Scalar{}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
t.Multiply(s1, s2)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarInversion(b *testing.B) {
|
||||
var rnd [64]byte
|
||||
rand.Read(rnd[:])
|
||||
s1, _ := (&Scalar{}).SetUniformBytes(rnd[0:64])
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
s1.Invert(s1)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBytesMontgomery(b *testing.B) {
|
||||
publicKey := "3bf918ffc2c955dc895bf145f566fb96623c1cadbe040091175764b5fde322c0"
|
||||
p, err := (&Point{}).SetBytes(decodeHex(publicKey))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = p.BytesMontgomery()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
// Copyright (c) 2017 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 field implements fast arithmetic modulo 2^255-19.
|
||||
package field
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Element represents an element of the field GF(2^255-19). Note that this
|
||||
// is not a cryptographically secure group, and should only be used to interact
|
||||
// with edwards25519.Point coordinates.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||
// are allowed to alias.
|
||||
//
|
||||
// The zero value is a valid zero element.
|
||||
type Element struct {
|
||||
// An element t represents the integer
|
||||
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
|
||||
//
|
||||
// Between operations, all limbs are expected to be lower than 2^52.
|
||||
l0 uint64
|
||||
l1 uint64
|
||||
l2 uint64
|
||||
l3 uint64
|
||||
l4 uint64
|
||||
}
|
||||
|
||||
const maskLow51Bits uint64 = (1 << 51) - 1
|
||||
|
||||
var feZero = &Element{0, 0, 0, 0, 0}
|
||||
|
||||
// Zero sets v = 0, and returns v.
|
||||
func (v *Element) Zero() *Element {
|
||||
*v = *feZero
|
||||
return v
|
||||
}
|
||||
|
||||
var feOne = &Element{1, 0, 0, 0, 0}
|
||||
|
||||
// One sets v = 1, and returns v.
|
||||
func (v *Element) One() *Element {
|
||||
*v = *feOne
|
||||
return v
|
||||
}
|
||||
|
||||
// reduce reduces v modulo 2^255 - 19 and returns it.
|
||||
func (v *Element) reduce() *Element {
|
||||
v.carryPropagate()
|
||||
|
||||
// After the light reduction we now have a field element representation
|
||||
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
|
||||
|
||||
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
|
||||
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
|
||||
c := (v.l0 + 19) >> 51
|
||||
c = (v.l1 + c) >> 51
|
||||
c = (v.l2 + c) >> 51
|
||||
c = (v.l3 + c) >> 51
|
||||
c = (v.l4 + c) >> 51
|
||||
|
||||
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
|
||||
// effectively applying the reduction identity to the carry.
|
||||
v.l0 += 19 * c
|
||||
|
||||
v.l1 += v.l0 >> 51
|
||||
v.l0 = v.l0 & maskLow51Bits
|
||||
v.l2 += v.l1 >> 51
|
||||
v.l1 = v.l1 & maskLow51Bits
|
||||
v.l3 += v.l2 >> 51
|
||||
v.l2 = v.l2 & maskLow51Bits
|
||||
v.l4 += v.l3 >> 51
|
||||
v.l3 = v.l3 & maskLow51Bits
|
||||
// no additional carry
|
||||
v.l4 = v.l4 & maskLow51Bits
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Add sets v = a + b, and returns v.
|
||||
func (v *Element) Add(a, b *Element) *Element {
|
||||
v.l0 = a.l0 + b.l0
|
||||
v.l1 = a.l1 + b.l1
|
||||
v.l2 = a.l2 + b.l2
|
||||
v.l3 = a.l3 + b.l3
|
||||
v.l4 = a.l4 + b.l4
|
||||
return v.carryPropagate()
|
||||
}
|
||||
|
||||
// Subtract sets v = a - b, and returns v.
|
||||
func (v *Element) Subtract(a, b *Element) *Element {
|
||||
// We first add 2 * p, to guarantee the subtraction won't underflow, and
|
||||
// then subtract b (which can be up to 2^255 + 2^13 * 19).
|
||||
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
|
||||
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
|
||||
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
|
||||
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
|
||||
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
|
||||
return v.carryPropagate()
|
||||
}
|
||||
|
||||
// Negate sets v = -a, and returns v.
|
||||
func (v *Element) Negate(a *Element) *Element {
|
||||
return v.Subtract(feZero, a)
|
||||
}
|
||||
|
||||
// Invert sets v = 1/z mod p, and returns v.
|
||||
//
|
||||
// If z == 0, Invert returns v = 0.
|
||||
func (v *Element) Invert(z *Element) *Element {
|
||||
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
|
||||
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
|
||||
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element
|
||||
|
||||
z2.Square(z) // 2
|
||||
t.Square(&z2) // 4
|
||||
t.Square(&t) // 8
|
||||
z9.Multiply(&t, z) // 9
|
||||
z11.Multiply(&z9, &z2) // 11
|
||||
t.Square(&z11) // 22
|
||||
z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0
|
||||
|
||||
t.Square(&z2_5_0) // 2^6 - 2^1
|
||||
for i := 0; i < 4; i++ {
|
||||
t.Square(&t) // 2^10 - 2^5
|
||||
}
|
||||
z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0
|
||||
|
||||
t.Square(&z2_10_0) // 2^11 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.Square(&t) // 2^20 - 2^10
|
||||
}
|
||||
z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0
|
||||
|
||||
t.Square(&z2_20_0) // 2^21 - 2^1
|
||||
for i := 0; i < 19; i++ {
|
||||
t.Square(&t) // 2^40 - 2^20
|
||||
}
|
||||
t.Multiply(&t, &z2_20_0) // 2^40 - 2^0
|
||||
|
||||
t.Square(&t) // 2^41 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.Square(&t) // 2^50 - 2^10
|
||||
}
|
||||
z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0
|
||||
|
||||
t.Square(&z2_50_0) // 2^51 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.Square(&t) // 2^100 - 2^50
|
||||
}
|
||||
z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0
|
||||
|
||||
t.Square(&z2_100_0) // 2^101 - 2^1
|
||||
for i := 0; i < 99; i++ {
|
||||
t.Square(&t) // 2^200 - 2^100
|
||||
}
|
||||
t.Multiply(&t, &z2_100_0) // 2^200 - 2^0
|
||||
|
||||
t.Square(&t) // 2^201 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.Square(&t) // 2^250 - 2^50
|
||||
}
|
||||
t.Multiply(&t, &z2_50_0) // 2^250 - 2^0
|
||||
|
||||
t.Square(&t) // 2^251 - 2^1
|
||||
t.Square(&t) // 2^252 - 2^2
|
||||
t.Square(&t) // 2^253 - 2^3
|
||||
t.Square(&t) // 2^254 - 2^4
|
||||
t.Square(&t) // 2^255 - 2^5
|
||||
|
||||
return v.Multiply(&t, &z11) // 2^255 - 21
|
||||
}
|
||||
|
||||
// Set sets v = a, and returns v.
|
||||
func (v *Element) Set(a *Element) *Element {
|
||||
*v = *a
|
||||
return v
|
||||
}
|
||||
|
||||
// SetBytes sets v to x, where x is a 32-byte little-endian encoding. If x is
|
||||
// not of the right length, SetBytes returns nil and an error, and the
|
||||
// receiver is unchanged.
|
||||
//
|
||||
// Consistent with RFC 7748, the most significant bit (the high bit of the
|
||||
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
|
||||
// are accepted. Note that this is laxer than specified by RFC 8032, but
|
||||
// consistent with most Ed25519 implementations.
|
||||
func (v *Element) SetBytes(x []byte) (*Element, error) {
|
||||
if len(x) != 32 {
|
||||
return nil, errors.New("edwards25519: invalid field element input size")
|
||||
}
|
||||
|
||||
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
|
||||
v.l0 = binary.LittleEndian.Uint64(x[0:8])
|
||||
v.l0 &= maskLow51Bits
|
||||
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
|
||||
v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3
|
||||
v.l1 &= maskLow51Bits
|
||||
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
|
||||
v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6
|
||||
v.l2 &= maskLow51Bits
|
||||
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
|
||||
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
|
||||
v.l3 &= maskLow51Bits
|
||||
// Bits 204:255 (bytes 24:32, bits 192:256, shift 12, mask 51).
|
||||
// Note: not bytes 25:33, shift 4, to avoid overread.
|
||||
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
|
||||
v.l4 &= maskLow51Bits
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Bytes returns the canonical 32-byte little-endian encoding of v.
|
||||
func (v *Element) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var out [32]byte
|
||||
return v.bytes(&out)
|
||||
}
|
||||
|
||||
func (v *Element) bytes(out *[32]byte) []byte {
|
||||
t := *v
|
||||
t.reduce()
|
||||
|
||||
// Pack five 51-bit limbs into four 64-bit words:
|
||||
//
|
||||
// 255 204 153 102 51 0
|
||||
// ├──l4──┼──l3──┼──l2──┼──l1──┼──l0──┤
|
||||
// ├───u3───┼───u2───┼───u1───┼───u0───┤
|
||||
// 256 192 128 64 0
|
||||
|
||||
u0 := t.l1<<51 | t.l0
|
||||
u1 := t.l2<<(102-64) | t.l1>>(64-51)
|
||||
u2 := t.l3<<(153-128) | t.l2>>(128-102)
|
||||
u3 := t.l4<<(204-192) | t.l3>>(192-153)
|
||||
|
||||
binary.LittleEndian.PutUint64(out[0*8:], u0)
|
||||
binary.LittleEndian.PutUint64(out[1*8:], u1)
|
||||
binary.LittleEndian.PutUint64(out[2*8:], u2)
|
||||
binary.LittleEndian.PutUint64(out[3*8:], u3)
|
||||
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// Equal returns 1 if v and u are equal, and 0 otherwise.
|
||||
func (v *Element) Equal(u *Element) int {
|
||||
sa, sv := u.Bytes(), v.Bytes()
|
||||
return subtle.ConstantTimeCompare(sa, sv)
|
||||
}
|
||||
|
||||
// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise.
|
||||
func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) }
|
||||
|
||||
// Select sets v to a if cond == 1, and to b if cond == 0.
|
||||
func (v *Element) Select(a, b *Element, cond int) *Element {
|
||||
m := mask64Bits(cond)
|
||||
v.l0 = (m & a.l0) | (^m & b.l0)
|
||||
v.l1 = (m & a.l1) | (^m & b.l1)
|
||||
v.l2 = (m & a.l2) | (^m & b.l2)
|
||||
v.l3 = (m & a.l3) | (^m & b.l3)
|
||||
v.l4 = (m & a.l4) | (^m & b.l4)
|
||||
return v
|
||||
}
|
||||
|
||||
// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
|
||||
func (v *Element) Swap(u *Element, cond int) {
|
||||
m := mask64Bits(cond)
|
||||
t := m & (v.l0 ^ u.l0)
|
||||
v.l0 ^= t
|
||||
u.l0 ^= t
|
||||
t = m & (v.l1 ^ u.l1)
|
||||
v.l1 ^= t
|
||||
u.l1 ^= t
|
||||
t = m & (v.l2 ^ u.l2)
|
||||
v.l2 ^= t
|
||||
u.l2 ^= t
|
||||
t = m & (v.l3 ^ u.l3)
|
||||
v.l3 ^= t
|
||||
u.l3 ^= t
|
||||
t = m & (v.l4 ^ u.l4)
|
||||
v.l4 ^= t
|
||||
u.l4 ^= t
|
||||
}
|
||||
|
||||
// IsNegative returns 1 if v is negative, and 0 otherwise.
|
||||
func (v *Element) IsNegative() int {
|
||||
return int(v.Bytes()[0] & 1)
|
||||
}
|
||||
|
||||
// Absolute sets v to |u|, and returns v.
|
||||
func (v *Element) Absolute(u *Element) *Element {
|
||||
return v.Select(new(Element).Negate(u), u, u.IsNegative())
|
||||
}
|
||||
|
||||
// Multiply sets v = x * y, and returns v.
|
||||
func (v *Element) Multiply(x, y *Element) *Element {
|
||||
feMul(v, x, y)
|
||||
return v
|
||||
}
|
||||
|
||||
// Square sets v = x * x, and returns v.
|
||||
func (v *Element) Square(x *Element) *Element {
|
||||
feSquare(v, x)
|
||||
return v
|
||||
}
|
||||
|
||||
// Mult32 sets v = x * y, and returns v.
|
||||
func (v *Element) Mult32(x *Element, y uint32) *Element {
|
||||
x0lo, x0hi := mul51(x.l0, y)
|
||||
x1lo, x1hi := mul51(x.l1, y)
|
||||
x2lo, x2hi := mul51(x.l2, y)
|
||||
x3lo, x3hi := mul51(x.l3, y)
|
||||
x4lo, x4hi := mul51(x.l4, y)
|
||||
v.l0 = x0lo + 19*x4hi // carried over per the reduction identity
|
||||
v.l1 = x1lo + x0hi
|
||||
v.l2 = x2lo + x1hi
|
||||
v.l3 = x3lo + x2hi
|
||||
v.l4 = x4lo + x3hi
|
||||
// The hi portions are going to be only 32 bits, plus any previous excess,
|
||||
// so we can skip the carry propagation.
|
||||
return v
|
||||
}
|
||||
|
||||
// mul51 returns lo + hi * 2⁵¹ = a * b.
|
||||
func mul51(a uint64, b uint32) (lo uint64, hi uint64) {
|
||||
mh, ml := bits.Mul64(a, uint64(b))
|
||||
lo = ml & maskLow51Bits
|
||||
hi = (mh << 13) | (ml >> 51)
|
||||
return
|
||||
}
|
||||
|
||||
// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
|
||||
func (v *Element) Pow22523(x *Element) *Element {
|
||||
var t0, t1, t2 Element
|
||||
|
||||
t0.Square(x) // x^2
|
||||
t1.Square(&t0) // x^4
|
||||
t1.Square(&t1) // x^8
|
||||
t1.Multiply(x, &t1) // x^9
|
||||
t0.Multiply(&t0, &t1) // x^11
|
||||
t0.Square(&t0) // x^22
|
||||
t0.Multiply(&t1, &t0) // x^31
|
||||
t1.Square(&t0) // x^62
|
||||
for i := 1; i < 5; i++ { // x^992
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1
|
||||
t1.Square(&t0) // 2^11 - 2
|
||||
for i := 1; i < 10; i++ { // 2^20 - 2^10
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t1.Multiply(&t1, &t0) // 2^20 - 1
|
||||
t2.Square(&t1) // 2^21 - 2
|
||||
for i := 1; i < 20; i++ { // 2^40 - 2^20
|
||||
t2.Square(&t2)
|
||||
}
|
||||
t1.Multiply(&t2, &t1) // 2^40 - 1
|
||||
t1.Square(&t1) // 2^41 - 2
|
||||
for i := 1; i < 10; i++ { // 2^50 - 2^10
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // 2^50 - 1
|
||||
t1.Square(&t0) // 2^51 - 2
|
||||
for i := 1; i < 50; i++ { // 2^100 - 2^50
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t1.Multiply(&t1, &t0) // 2^100 - 1
|
||||
t2.Square(&t1) // 2^101 - 2
|
||||
for i := 1; i < 100; i++ { // 2^200 - 2^100
|
||||
t2.Square(&t2)
|
||||
}
|
||||
t1.Multiply(&t2, &t1) // 2^200 - 1
|
||||
t1.Square(&t1) // 2^201 - 2
|
||||
for i := 1; i < 50; i++ { // 2^250 - 2^50
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // 2^250 - 1
|
||||
t0.Square(&t0) // 2^251 - 2
|
||||
t0.Square(&t0) // 2^252 - 4
|
||||
return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3)
|
||||
}
|
||||
|
||||
// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
|
||||
var sqrtM1 = &Element{1718705420411056, 234908883556509,
|
||||
2233514472574048, 2117202627021982, 765476049583133}
|
||||
|
||||
// SqrtRatio sets r to the non-negative square root of the ratio of u and v.
|
||||
//
|
||||
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
|
||||
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
|
||||
// and returns r and 0.
|
||||
func (r *Element) SqrtRatio(u, v *Element) (R *Element, wasSquare int) {
|
||||
t0 := new(Element)
|
||||
|
||||
// r = (u * v3) * (u * v7)^((p-5)/8)
|
||||
v2 := new(Element).Square(v)
|
||||
uv3 := new(Element).Multiply(u, t0.Multiply(v2, v))
|
||||
uv7 := new(Element).Multiply(uv3, t0.Square(v2))
|
||||
rr := new(Element).Multiply(uv3, t0.Pow22523(uv7))
|
||||
|
||||
check := new(Element).Multiply(v, t0.Square(rr)) // check = v * r^2
|
||||
|
||||
uNeg := new(Element).Negate(u)
|
||||
correctSignSqrt := check.Equal(u)
|
||||
flippedSignSqrt := check.Equal(uNeg)
|
||||
flippedSignSqrtI := check.Equal(t0.Multiply(uNeg, sqrtM1))
|
||||
|
||||
rPrime := new(Element).Multiply(rr, sqrtM1) // r_prime = SQRT_M1 * r
|
||||
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
|
||||
rr.Select(rPrime, rr, flippedSignSqrt|flippedSignSqrtI)
|
||||
|
||||
r.Absolute(rr) // Choose the nonnegative square root.
|
||||
return r, correctSignSqrt | flippedSignSqrt
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2019 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 field
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
func checkAliasingOneArg(f func(v, x *Element) *Element) func(v, x Element) bool {
|
||||
return func(v, x Element) bool {
|
||||
x1, v1 := x, x
|
||||
|
||||
// Calculate a reference f(x) without aliasing.
|
||||
if out := f(&v, &x); out != &v && isInBounds(out) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the argument and the receiver.
|
||||
if out := f(&v1, &v1); out != &v1 || v1 != v {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the arguments was not modified.
|
||||
return x == x1
|
||||
}
|
||||
}
|
||||
|
||||
func checkAliasingTwoArgs(f func(v, x, y *Element) *Element) func(v, x, y Element) bool {
|
||||
return func(v, x, y Element) bool {
|
||||
x1, y1, v1 := x, y, Element{}
|
||||
|
||||
// Calculate a reference f(x, y) without aliasing.
|
||||
if out := f(&v, &x, &y); out != &v && isInBounds(out) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the first argument and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &v1, &y); out != &v1 || v1 != v {
|
||||
return false
|
||||
}
|
||||
// Test aliasing the second argument and the receiver.
|
||||
v1 = y
|
||||
if out := f(&v1, &x, &v1); out != &v1 || v1 != v {
|
||||
return false
|
||||
}
|
||||
|
||||
// Calculate a reference f(x, x) without aliasing.
|
||||
if out := f(&v, &x, &x); out != &v {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the first argument and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &v1, &x); out != &v1 || v1 != v {
|
||||
return false
|
||||
}
|
||||
// Test aliasing the second argument and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &x, &v1); out != &v1 || v1 != v {
|
||||
return false
|
||||
}
|
||||
// Test aliasing both arguments and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &v1, &v1); out != &v1 || v1 != v {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the arguments were not modified.
|
||||
return x == x1 && y == y1
|
||||
}
|
||||
}
|
||||
|
||||
// TestAliasing checks that receivers and arguments can alias each other without
|
||||
// leading to incorrect results. That is, it ensures that it's safe to write
|
||||
//
|
||||
// v.Invert(v)
|
||||
//
|
||||
// or
|
||||
//
|
||||
// v.Add(v, v)
|
||||
//
|
||||
// without any of the inputs getting clobbered by the output being written.
|
||||
func TestAliasing(t *testing.T) {
|
||||
type target struct {
|
||||
name string
|
||||
oneArgF func(v, x *Element) *Element
|
||||
twoArgsF func(v, x, y *Element) *Element
|
||||
}
|
||||
for _, tt := range []target{
|
||||
{name: "Absolute", oneArgF: (*Element).Absolute},
|
||||
{name: "Invert", oneArgF: (*Element).Invert},
|
||||
{name: "Negate", oneArgF: (*Element).Negate},
|
||||
{name: "Set", oneArgF: (*Element).Set},
|
||||
{name: "Square", oneArgF: (*Element).Square},
|
||||
{name: "Pow22523", oneArgF: (*Element).Pow22523},
|
||||
{
|
||||
name: "Mult32",
|
||||
oneArgF: func(v, x *Element) *Element {
|
||||
return v.Mult32(x, 0xffffffff)
|
||||
},
|
||||
},
|
||||
{name: "Multiply", twoArgsF: (*Element).Multiply},
|
||||
{name: "Add", twoArgsF: (*Element).Add},
|
||||
{name: "Subtract", twoArgsF: (*Element).Subtract},
|
||||
{
|
||||
name: "SqrtRatio",
|
||||
twoArgsF: func(v, x, y *Element) *Element {
|
||||
r, _ := v.SqrtRatio(x, y)
|
||||
return r
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Select0",
|
||||
twoArgsF: func(v, x, y *Element) *Element {
|
||||
return v.Select(x, y, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Select1",
|
||||
twoArgsF: func(v, x, y *Element) *Element {
|
||||
return v.Select(x, y, 1)
|
||||
},
|
||||
},
|
||||
} {
|
||||
var err error
|
||||
switch {
|
||||
case tt.oneArgF != nil:
|
||||
err = quick.Check(checkAliasingOneArg(tt.oneArgF), quickCheckConfig(256))
|
||||
case tt.twoArgsF != nil:
|
||||
err = quick.Check(checkAliasingTwoArgs(tt.twoArgsF), quickCheckConfig(256))
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%v: %v", tt.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
package field
|
||||
|
||||
// feMul sets out = a * b. It works like feMulGeneric.
|
||||
//
|
||||
//go:noescape
|
||||
func feMul(out *Element, a *Element, b *Element)
|
||||
|
||||
// feSquare sets out = a * a. It works like feSquareGeneric.
|
||||
//
|
||||
//go:noescape
|
||||
func feSquare(out *Element, a *Element)
|
||||
@@ -0,0 +1,398 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func feMul(out *Element, a *Element, b *Element)
|
||||
TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), BX
|
||||
|
||||
// r0 = a0×b0
|
||||
MOVQ (CX), AX
|
||||
MULQ (BX)
|
||||
MOVQ AX, DI
|
||||
MOVQ DX, SI
|
||||
|
||||
// r0 += 19×a1×b4
|
||||
MOVQ 8(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a2×b3
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a3×b2
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a4×b1
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r1 = a0×b1
|
||||
MOVQ (CX), AX
|
||||
MULQ 8(BX)
|
||||
MOVQ AX, R9
|
||||
MOVQ DX, R8
|
||||
|
||||
// r1 += a1×b0
|
||||
MOVQ 8(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a2×b4
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a3×b3
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a4×b2
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r2 = a0×b2
|
||||
MOVQ (CX), AX
|
||||
MULQ 16(BX)
|
||||
MOVQ AX, R11
|
||||
MOVQ DX, R10
|
||||
|
||||
// r2 += a1×b1
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += a2×b0
|
||||
MOVQ 16(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a3×b4
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a4×b3
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r3 = a0×b3
|
||||
MOVQ (CX), AX
|
||||
MULQ 24(BX)
|
||||
MOVQ AX, R13
|
||||
MOVQ DX, R12
|
||||
|
||||
// r3 += a1×b2
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += a2×b1
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += a3×b0
|
||||
MOVQ 24(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += 19×a4×b4
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r4 = a0×b4
|
||||
MOVQ (CX), AX
|
||||
MULQ 32(BX)
|
||||
MOVQ AX, R15
|
||||
MOVQ DX, R14
|
||||
|
||||
// r4 += a1×b3
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a2×b2
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a3×b1
|
||||
MOVQ 24(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a4×b0
|
||||
MOVQ 32(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// First reduction chain
|
||||
MOVQ $0x0007ffffffffffff, AX
|
||||
SHLQ $0x0d, DI, SI
|
||||
SHLQ $0x0d, R9, R8
|
||||
SHLQ $0x0d, R11, R10
|
||||
SHLQ $0x0d, R13, R12
|
||||
SHLQ $0x0d, R15, R14
|
||||
ANDQ AX, DI
|
||||
IMUL3Q $0x13, R14, R14
|
||||
ADDQ R14, DI
|
||||
ANDQ AX, R9
|
||||
ADDQ SI, R9
|
||||
ANDQ AX, R11
|
||||
ADDQ R8, R11
|
||||
ANDQ AX, R13
|
||||
ADDQ R10, R13
|
||||
ANDQ AX, R15
|
||||
ADDQ R12, R15
|
||||
|
||||
// Second reduction chain (carryPropagate)
|
||||
MOVQ DI, SI
|
||||
SHRQ $0x33, SI
|
||||
MOVQ R9, R8
|
||||
SHRQ $0x33, R8
|
||||
MOVQ R11, R10
|
||||
SHRQ $0x33, R10
|
||||
MOVQ R13, R12
|
||||
SHRQ $0x33, R12
|
||||
MOVQ R15, R14
|
||||
SHRQ $0x33, R14
|
||||
ANDQ AX, DI
|
||||
IMUL3Q $0x13, R14, R14
|
||||
ADDQ R14, DI
|
||||
ANDQ AX, R9
|
||||
ADDQ SI, R9
|
||||
ANDQ AX, R11
|
||||
ADDQ R8, R11
|
||||
ANDQ AX, R13
|
||||
ADDQ R10, R13
|
||||
ANDQ AX, R15
|
||||
ADDQ R12, R15
|
||||
|
||||
// Store output
|
||||
MOVQ out+0(FP), AX
|
||||
MOVQ DI, (AX)
|
||||
MOVQ R9, 8(AX)
|
||||
MOVQ R11, 16(AX)
|
||||
MOVQ R13, 24(AX)
|
||||
MOVQ R15, 32(AX)
|
||||
RET
|
||||
|
||||
// func feSquare(out *Element, a *Element)
|
||||
TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
MOVQ a+8(FP), CX
|
||||
|
||||
// r0 = l0×l0
|
||||
MOVQ (CX), AX
|
||||
MULQ (CX)
|
||||
MOVQ AX, SI
|
||||
MOVQ DX, BX
|
||||
|
||||
// r0 += 38×l1×l4
|
||||
MOVQ 8(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r0 += 38×l2×l3
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r1 = 2×l0×l1
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 8(CX)
|
||||
MOVQ AX, R8
|
||||
MOVQ DX, DI
|
||||
|
||||
// r1 += 38×l2×l4
|
||||
MOVQ 16(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r1 += 19×l3×l3
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r2 = 2×l0×l2
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 16(CX)
|
||||
MOVQ AX, R10
|
||||
MOVQ DX, R9
|
||||
|
||||
// r2 += l1×l1
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 8(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
|
||||
// r2 += 38×l3×l4
|
||||
MOVQ 24(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
|
||||
// r3 = 2×l0×l3
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 24(CX)
|
||||
MOVQ AX, R12
|
||||
MOVQ DX, R11
|
||||
|
||||
// r3 += 2×l1×l2
|
||||
MOVQ 8(CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r3 += 19×l4×l4
|
||||
MOVQ 32(CX), DX
|
||||
LEAQ (DX)(DX*8), AX
|
||||
LEAQ (DX)(AX*2), AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r4 = 2×l0×l4
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
MOVQ AX, R14
|
||||
MOVQ DX, R13
|
||||
|
||||
// r4 += 2×l1×l3
|
||||
MOVQ 8(CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
|
||||
// r4 += l2×l2
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
|
||||
// First reduction chain
|
||||
MOVQ $0x0007ffffffffffff, AX
|
||||
SHLQ $0x0d, SI, BX
|
||||
SHLQ $0x0d, R8, DI
|
||||
SHLQ $0x0d, R10, R9
|
||||
SHLQ $0x0d, R12, R11
|
||||
SHLQ $0x0d, R14, R13
|
||||
ANDQ AX, SI
|
||||
IMUL3Q $0x13, R13, R13
|
||||
ADDQ R13, SI
|
||||
ANDQ AX, R8
|
||||
ADDQ BX, R8
|
||||
ANDQ AX, R10
|
||||
ADDQ DI, R10
|
||||
ANDQ AX, R12
|
||||
ADDQ R9, R12
|
||||
ANDQ AX, R14
|
||||
ADDQ R11, R14
|
||||
|
||||
// Second reduction chain (carryPropagate)
|
||||
MOVQ SI, BX
|
||||
SHRQ $0x33, BX
|
||||
MOVQ R8, DI
|
||||
SHRQ $0x33, DI
|
||||
MOVQ R10, R9
|
||||
SHRQ $0x33, R9
|
||||
MOVQ R12, R11
|
||||
SHRQ $0x33, R11
|
||||
MOVQ R14, R13
|
||||
SHRQ $0x33, R13
|
||||
ANDQ AX, SI
|
||||
IMUL3Q $0x13, R13, R13
|
||||
ADDQ R13, SI
|
||||
ANDQ AX, R8
|
||||
ADDQ BX, R8
|
||||
ANDQ AX, R10
|
||||
ADDQ DI, R10
|
||||
ANDQ AX, R12
|
||||
ADDQ R9, R12
|
||||
ANDQ AX, R14
|
||||
ADDQ R11, R14
|
||||
|
||||
// Store output
|
||||
MOVQ out+0(FP), AX
|
||||
MOVQ SI, (AX)
|
||||
MOVQ R8, 8(AX)
|
||||
MOVQ R10, 16(AX)
|
||||
MOVQ R12, 24(AX)
|
||||
MOVQ R14, 32(AX)
|
||||
RET
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2019 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 !amd64 || purego
|
||||
|
||||
package field
|
||||
|
||||
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
|
||||
|
||||
func feSquare(v, x *Element) { feSquareGeneric(v, x) }
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2019 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 field
|
||||
|
||||
import "testing"
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
x := new(Element).One()
|
||||
y := new(Element).Add(x, x)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Add(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMultiply(b *testing.B) {
|
||||
x := new(Element).One()
|
||||
y := new(Element).Add(x, x)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Multiply(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSquare(b *testing.B) {
|
||||
x := new(Element).Add(feOne, feOne)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Square(x)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInvert(b *testing.B) {
|
||||
x := new(Element).Add(feOne, feOne)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Invert(x)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMult32(b *testing.B) {
|
||||
x := new(Element).One()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Mult32(x, 0xaa42aa42)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBytes(b *testing.B) {
|
||||
x := new(Element).One()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
x.Bytes()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 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 field
|
||||
|
||||
import "errors"
|
||||
|
||||
// This file contains additional functionality that is not included in the
|
||||
// upstream crypto/ed25519/edwards25519/field package.
|
||||
|
||||
// SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which
|
||||
// is reduced modulo the field order. If x is not of the right length,
|
||||
// SetWideBytes returns nil and an error, and the receiver is unchanged.
|
||||
//
|
||||
// SetWideBytes is not necessary to select a uniformly distributed value, and is
|
||||
// only provided for compatibility: SetBytes can be used instead as the chance
|
||||
// of bias is less than 2⁻²⁵⁰.
|
||||
func (v *Element) SetWideBytes(x []byte) (*Element, error) {
|
||||
if len(x) != 64 {
|
||||
return nil, errors.New("edwards25519: invalid SetWideBytes input size")
|
||||
}
|
||||
|
||||
// Split the 64 bytes into two elements, and extract the most significant
|
||||
// bit of each, which is ignored by SetBytes.
|
||||
lo, _ := new(Element).SetBytes(x[:32])
|
||||
loMSB := uint64(x[31] >> 7)
|
||||
hi, _ := new(Element).SetBytes(x[32:])
|
||||
hiMSB := uint64(x[63] >> 7)
|
||||
|
||||
// The output we want is
|
||||
//
|
||||
// v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹
|
||||
//
|
||||
// which applying the reduction identity comes out to
|
||||
//
|
||||
// v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19²
|
||||
//
|
||||
// l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value
|
||||
// (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value
|
||||
// (hiMSB * 2 * 19²), so it fits in a uint64.
|
||||
|
||||
v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19
|
||||
v.l1 = lo.l1 + hi.l1*2*19
|
||||
v.l2 = lo.l2 + hi.l2*2*19
|
||||
v.l3 = lo.l3 + hi.l3*2*19
|
||||
v.l4 = lo.l4 + hi.l4*2*19
|
||||
|
||||
return v.carryPropagate(), nil
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 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 field
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
var bigP = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 255), big.NewInt(19))
|
||||
|
||||
func TestSetWideBytes(t *testing.T) {
|
||||
f1 := func(in [64]byte, fe Element) bool {
|
||||
fe1 := new(Element).Set(&fe)
|
||||
|
||||
if out, err := fe.SetWideBytes([]byte{42}); err == nil || out != nil ||
|
||||
fe.Equal(fe1) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
if out, err := fe.SetWideBytes(in[:]); err != nil || out != &fe {
|
||||
return false
|
||||
}
|
||||
|
||||
b := new(big.Int).SetBytes(swapEndianness(in[:]))
|
||||
fe1.fromBig(b.Mod(b, bigP))
|
||||
|
||||
return fe.Equal(fe1) == 1 && isInBounds(&fe) && isInBounds(fe1)
|
||||
}
|
||||
if err := quick.Check(f1, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
// Copyright (c) 2017 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 field
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
|
||||
// bits.Mul64 and bits.Add64 intrinsics.
|
||||
type uint128 struct {
|
||||
lo, hi uint64
|
||||
}
|
||||
|
||||
// mul returns a * b.
|
||||
func mul(a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// addMul returns v + a * b.
|
||||
func addMul(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// mul19 returns v * 19.
|
||||
func mul19(v uint64) uint64 {
|
||||
// Using this approach seems to yield better optimizations than *19.
|
||||
return v + (v+v<<3)<<1
|
||||
}
|
||||
|
||||
// addMul19 returns v + 19 * a * b, where a and b are at most 52 bits.
|
||||
func addMul19(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(mul19(a), b)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// addMul38 returns v + 38 * a * b, where a and b are at most 52 bits.
|
||||
func addMul38(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(mul19(a), b*2)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
|
||||
func shiftRightBy51(a uint128) uint64 {
|
||||
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
||||
}
|
||||
|
||||
func feMulGeneric(v, a, b *Element) {
|
||||
a0 := a.l0
|
||||
a1 := a.l1
|
||||
a2 := a.l2
|
||||
a3 := a.l3
|
||||
a4 := a.l4
|
||||
|
||||
b0 := b.l0
|
||||
b1 := b.l1
|
||||
b2 := b.l2
|
||||
b3 := b.l3
|
||||
b4 := b.l4
|
||||
|
||||
// Limb multiplication works like pen-and-paper columnar multiplication, but
|
||||
// with 51-bit limbs instead of digits.
|
||||
//
|
||||
// a4 a3 a2 a1 a0 x
|
||||
// b4 b3 b2 b1 b0 =
|
||||
// ------------------------
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a4b1 a3b1 a2b1 a1b1 a0b1 +
|
||||
// a4b2 a3b2 a2b2 a1b2 a0b2 +
|
||||
// a4b3 a3b3 a2b3 a1b3 a0b3 +
|
||||
// a4b4 a3b4 a2b4 a1b4 a0b4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
|
||||
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
|
||||
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
|
||||
//
|
||||
// Reduction can be carried out simultaneously to multiplication. For
|
||||
// example, we do not compute r5: whenever the result of a multiplication
|
||||
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
|
||||
//
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
|
||||
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
|
||||
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
|
||||
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// Finally we add up the columns into wide, overlapping limbs.
|
||||
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
r0 := mul(a0, b0)
|
||||
r0 = addMul19(r0, a1, b4)
|
||||
r0 = addMul19(r0, a2, b3)
|
||||
r0 = addMul19(r0, a3, b2)
|
||||
r0 = addMul19(r0, a4, b1)
|
||||
|
||||
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||
r1 := mul(a0, b1)
|
||||
r1 = addMul(r1, a1, b0)
|
||||
r1 = addMul19(r1, a2, b4)
|
||||
r1 = addMul19(r1, a3, b3)
|
||||
r1 = addMul19(r1, a4, b2)
|
||||
|
||||
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||
r2 := mul(a0, b2)
|
||||
r2 = addMul(r2, a1, b1)
|
||||
r2 = addMul(r2, a2, b0)
|
||||
r2 = addMul19(r2, a3, b4)
|
||||
r2 = addMul19(r2, a4, b3)
|
||||
|
||||
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||
r3 := mul(a0, b3)
|
||||
r3 = addMul(r3, a1, b2)
|
||||
r3 = addMul(r3, a2, b1)
|
||||
r3 = addMul(r3, a3, b0)
|
||||
r3 = addMul19(r3, a4, b4)
|
||||
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
r4 := mul(a0, b4)
|
||||
r4 = addMul(r4, a1, b3)
|
||||
r4 = addMul(r4, a2, b2)
|
||||
r4 = addMul(r4, a3, b1)
|
||||
r4 = addMul(r4, a4, b0)
|
||||
|
||||
// After the multiplication, we need to reduce (carry) the five coefficients
|
||||
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
||||
// to respect the Element invariant.
|
||||
//
|
||||
// Overall, the reduction works the same as carryPropagate, except with
|
||||
// wider inputs: we take the carry for each coefficient by shifting it right
|
||||
// by 51, and add it to the limb above it. The top carry is multiplied by 19
|
||||
// according to the reduction identity and added to the lowest limb.
|
||||
//
|
||||
// The largest coefficient (r0) will be at most 111 bits, which guarantees
|
||||
// that all carries are at most 111 - 51 = 60 bits, which fits in a uint64.
|
||||
//
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
|
||||
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
|
||||
// r0 < 2⁷ × 2⁵² × 2⁵²
|
||||
// r0 < 2¹¹¹
|
||||
//
|
||||
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
|
||||
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and
|
||||
// allows us to easily apply the reduction identity.
|
||||
//
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
// r4 < 5 × 2⁵² × 2⁵²
|
||||
// r4 < 2¹⁰⁷
|
||||
//
|
||||
|
||||
c0 := shiftRightBy51(r0)
|
||||
c1 := shiftRightBy51(r1)
|
||||
c2 := shiftRightBy51(r2)
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + mul19(c4)
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
rr4 := r4.lo&maskLow51Bits + c3
|
||||
|
||||
// Now all coefficients fit into 64-bit registers but are still too large to
|
||||
// be passed around as an Element. We therefore do one last carry chain,
|
||||
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
||||
|
||||
v.l0 = rr0&maskLow51Bits + mul19(rr4>>51)
|
||||
v.l1 = rr1&maskLow51Bits + rr0>>51
|
||||
v.l2 = rr2&maskLow51Bits + rr1>>51
|
||||
v.l3 = rr3&maskLow51Bits + rr2>>51
|
||||
v.l4 = rr4&maskLow51Bits + rr3>>51
|
||||
}
|
||||
|
||||
func feSquareGeneric(v, a *Element) {
|
||||
l0 := a.l0
|
||||
l1 := a.l1
|
||||
l2 := a.l2
|
||||
l3 := a.l3
|
||||
l4 := a.l4
|
||||
|
||||
// Squaring works precisely like multiplication above, but thanks to its
|
||||
// symmetry we get to group a few terms together.
|
||||
//
|
||||
// l4 l3 l2 l1 l0 x
|
||||
// l4 l3 l2 l1 l0 =
|
||||
// ------------------------
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l4l1 l3l1 l2l1 l1l1 l0l1 +
|
||||
// l4l2 l3l2 l2l2 l1l2 l0l2 +
|
||||
// l4l3 l3l3 l2l3 l1l3 l0l3 +
|
||||
// l4l4 l3l4 l2l4 l1l4 l0l4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
|
||||
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
|
||||
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
|
||||
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
|
||||
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||
r0 := mul(l0, l0)
|
||||
r0 = addMul38(r0, l1, l4)
|
||||
r0 = addMul38(r0, l2, l3)
|
||||
|
||||
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||
r1 := mul(l0*2, l1)
|
||||
r1 = addMul38(r1, l2, l4)
|
||||
r1 = addMul19(r1, l3, l3)
|
||||
|
||||
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||
r2 := mul(l0*2, l2)
|
||||
r2 = addMul(r2, l1, l1)
|
||||
r2 = addMul38(r2, l3, l4)
|
||||
|
||||
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||
r3 := mul(l0*2, l3)
|
||||
r3 = addMul(r3, l1*2, l2)
|
||||
r3 = addMul19(r3, l4, l4)
|
||||
|
||||
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||
r4 := mul(l0*2, l4)
|
||||
r4 = addMul(r4, l1*2, l3)
|
||||
r4 = addMul(r4, l2, l2)
|
||||
|
||||
c0 := shiftRightBy51(r0)
|
||||
c1 := shiftRightBy51(r1)
|
||||
c2 := shiftRightBy51(r2)
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + mul19(c4)
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
rr4 := r4.lo&maskLow51Bits + c3
|
||||
|
||||
v.l0 = rr0&maskLow51Bits + mul19(rr4>>51)
|
||||
v.l1 = rr1&maskLow51Bits + rr0>>51
|
||||
v.l2 = rr2&maskLow51Bits + rr1>>51
|
||||
v.l3 = rr3&maskLow51Bits + rr2>>51
|
||||
v.l4 = rr4&maskLow51Bits + rr3>>51
|
||||
}
|
||||
|
||||
// carryPropagate brings the limbs below 52 bits by applying the reduction
|
||||
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
// (l4>>51) is at most 64 - 51 = 13 bits, so (l4>>51)*19 is at most 18 bits, and
|
||||
// the final l0 will be at most 52 bits. Similarly for the rest.
|
||||
l0 := v.l0
|
||||
v.l0 = v.l0&maskLow51Bits + mul19(v.l4>>51)
|
||||
v.l4 = v.l4&maskLow51Bits + v.l3>>51
|
||||
v.l3 = v.l3&maskLow51Bits + v.l2>>51
|
||||
v.l2 = v.l2&maskLow51Bits + v.l1>>51
|
||||
v.l1 = v.l1&maskLow51Bits + l0>>51
|
||||
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
// Copyright (c) 2017 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 field
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
mathrand "math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
func (v Element) String() string {
|
||||
return hex.EncodeToString(v.Bytes())
|
||||
}
|
||||
|
||||
// quickCheckConfig returns a quick.Config that scales the max count by the
|
||||
// given factor if the -short flag is not set.
|
||||
func quickCheckConfig(slowScale int) *quick.Config {
|
||||
cfg := new(quick.Config)
|
||||
if !testing.Short() {
|
||||
cfg.MaxCountScale = float64(slowScale)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func generateFieldElement(rand *mathrand.Rand) Element {
|
||||
const maskLow52Bits = (1 << 52) - 1
|
||||
return Element{
|
||||
rand.Uint64() & maskLow52Bits,
|
||||
rand.Uint64() & maskLow52Bits,
|
||||
rand.Uint64() & maskLow52Bits,
|
||||
rand.Uint64() & maskLow52Bits,
|
||||
rand.Uint64() & maskLow52Bits,
|
||||
}
|
||||
}
|
||||
|
||||
// weirdLimbs can be combined to generate a range of edge-case field elements.
|
||||
// 0 and -1 are intentionally more weighted, as they combine well.
|
||||
var (
|
||||
weirdLimbs51 = []uint64{
|
||||
0, 0, 0, 0,
|
||||
1,
|
||||
19 - 1,
|
||||
19,
|
||||
0x2aaaaaaaaaaaa,
|
||||
0x5555555555555,
|
||||
(1 << 51) - 20,
|
||||
(1 << 51) - 19,
|
||||
(1 << 51) - 1, (1 << 51) - 1,
|
||||
(1 << 51) - 1, (1 << 51) - 1,
|
||||
}
|
||||
weirdLimbs52 = []uint64{
|
||||
0, 0, 0, 0, 0, 0,
|
||||
1,
|
||||
19 - 1,
|
||||
19,
|
||||
0x2aaaaaaaaaaaa,
|
||||
0x5555555555555,
|
||||
(1 << 51) - 20,
|
||||
(1 << 51) - 19,
|
||||
(1 << 51) - 1, (1 << 51) - 1,
|
||||
(1 << 51) - 1, (1 << 51) - 1,
|
||||
(1 << 51) - 1, (1 << 51) - 1,
|
||||
1 << 51,
|
||||
(1 << 51) + 1,
|
||||
(1 << 52) - 19,
|
||||
(1 << 52) - 1,
|
||||
}
|
||||
)
|
||||
|
||||
func generateWeirdFieldElement(rand *mathrand.Rand) Element {
|
||||
return Element{
|
||||
weirdLimbs52[rand.Intn(len(weirdLimbs52))],
|
||||
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||
}
|
||||
}
|
||||
|
||||
func (Element) Generate(rand *mathrand.Rand, size int) reflect.Value {
|
||||
if rand.Intn(2) == 0 {
|
||||
return reflect.ValueOf(generateWeirdFieldElement(rand))
|
||||
}
|
||||
return reflect.ValueOf(generateFieldElement(rand))
|
||||
}
|
||||
|
||||
// isInBounds returns whether the element is within the expected bit size bounds
|
||||
// after a light reduction.
|
||||
func isInBounds(x *Element) bool {
|
||||
return bits.Len64(x.l0) <= 52 &&
|
||||
bits.Len64(x.l1) <= 52 &&
|
||||
bits.Len64(x.l2) <= 52 &&
|
||||
bits.Len64(x.l3) <= 52 &&
|
||||
bits.Len64(x.l4) <= 52
|
||||
}
|
||||
|
||||
func TestMultiplyDistributesOverAdd(t *testing.T) {
|
||||
multiplyDistributesOverAdd := func(x, y, z Element) bool {
|
||||
// Compute t1 = (x+y)*z
|
||||
t1 := new(Element)
|
||||
t1.Add(&x, &y)
|
||||
t1.Multiply(t1, &z)
|
||||
|
||||
// Compute t2 = x*z + y*z
|
||||
t2 := new(Element)
|
||||
t3 := new(Element)
|
||||
t2.Multiply(&x, &z)
|
||||
t3.Multiply(&y, &z)
|
||||
t2.Add(t2, t3)
|
||||
|
||||
return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2)
|
||||
}
|
||||
|
||||
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig(1024)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMul64to128(t *testing.T) {
|
||||
a := uint64(5)
|
||||
b := uint64(5)
|
||||
r := mul(a, b)
|
||||
if r.lo != 0x19 || r.hi != 0 {
|
||||
t.Errorf("lo-range wide mult failed, got %d + %d*(2**64)", r.lo, r.hi)
|
||||
}
|
||||
|
||||
a = uint64(18014398509481983) // 2^54 - 1
|
||||
b = uint64(18014398509481983) // 2^54 - 1
|
||||
r = mul(a, b)
|
||||
if r.lo != 0xff80000000000001 || r.hi != 0xfffffffffff {
|
||||
t.Errorf("hi-range wide mult failed, got %d + %d*(2**64)", r.lo, r.hi)
|
||||
}
|
||||
|
||||
a = uint64(1125899906842661)
|
||||
b = uint64(2097155)
|
||||
r = mul(a, b)
|
||||
r = addMul(r, a, b)
|
||||
r = addMul(r, a, b)
|
||||
r = addMul(r, a, b)
|
||||
r = addMul(r, a, b)
|
||||
if r.lo != 16888498990613035 || r.hi != 640 {
|
||||
t.Errorf("wrong answer: %d + %d*(2**64)", r.lo, r.hi)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetBytesRoundTrip(t *testing.T) {
|
||||
f1 := func(in [32]byte, fe Element) bool {
|
||||
fe.SetBytes(in[:])
|
||||
|
||||
// Mask the most significant bit as it's ignored by SetBytes. (Now
|
||||
// instead of earlier so we check the masking in SetBytes is working.)
|
||||
in[len(in)-1] &= (1 << 7) - 1
|
||||
|
||||
return bytes.Equal(in[:], fe.Bytes()) && isInBounds(&fe)
|
||||
}
|
||||
if err := quick.Check(f1, nil); err != nil {
|
||||
t.Errorf("failed bytes->FE->bytes round-trip: %v", err)
|
||||
}
|
||||
|
||||
f2 := func(fe, r Element) bool {
|
||||
r.SetBytes(fe.Bytes())
|
||||
|
||||
// Intentionally not using Equal not to go through Bytes again.
|
||||
// Calling reduce because both Generate and SetBytes can produce
|
||||
// non-canonical representations.
|
||||
fe.reduce()
|
||||
r.reduce()
|
||||
return fe == r
|
||||
}
|
||||
if err := quick.Check(f2, nil); err != nil {
|
||||
t.Errorf("failed FE->bytes->FE round-trip: %v", err)
|
||||
}
|
||||
|
||||
// Check some fixed vectors from dalek
|
||||
type feRTTest struct {
|
||||
fe Element
|
||||
b []byte
|
||||
}
|
||||
var tests = []feRTTest{
|
||||
{
|
||||
fe: Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676},
|
||||
b: []byte{74, 209, 69, 197, 70, 70, 161, 222, 56, 226, 229, 19, 112, 60, 25, 92, 187, 74, 222, 56, 50, 153, 51, 233, 40, 74, 57, 6, 160, 185, 213, 31},
|
||||
},
|
||||
{
|
||||
fe: Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972},
|
||||
b: []byte{199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, 44, 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 122},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
b := tt.fe.Bytes()
|
||||
fe, _ := new(Element).SetBytes(tt.b)
|
||||
if !bytes.Equal(b, tt.b) || fe.Equal(&tt.fe) != 1 {
|
||||
t.Errorf("Failed fixed roundtrip: %v", tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func swapEndianness(buf []byte) []byte {
|
||||
for i := 0; i < len(buf)/2; i++ {
|
||||
buf[i], buf[len(buf)-i-1] = buf[len(buf)-i-1], buf[i]
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func TestBytesBigEquivalence(t *testing.T) {
|
||||
f1 := func(in [32]byte, fe, fe1 Element) bool {
|
||||
fe.SetBytes(in[:])
|
||||
|
||||
in[len(in)-1] &= (1 << 7) - 1 // mask the most significant bit
|
||||
b := new(big.Int).SetBytes(swapEndianness(in[:]))
|
||||
fe1.fromBig(b)
|
||||
|
||||
if fe != fe1 {
|
||||
return false
|
||||
}
|
||||
|
||||
buf := make([]byte, 32)
|
||||
buf = swapEndianness(fe1.toBig().FillBytes(buf))
|
||||
|
||||
return bytes.Equal(fe.Bytes(), buf) && isInBounds(&fe) && isInBounds(&fe1)
|
||||
}
|
||||
if err := quick.Check(f1, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// fromBig sets v = n, and returns v. The bit length of n must not exceed 256.
|
||||
func (v *Element) fromBig(n *big.Int) *Element {
|
||||
if n.BitLen() > 32*8 {
|
||||
panic("edwards25519: invalid field element input size")
|
||||
}
|
||||
|
||||
buf := make([]byte, 0, 32)
|
||||
for _, word := range n.Bits() {
|
||||
for i := 0; i < bits.UintSize; i += 8 {
|
||||
if len(buf) >= cap(buf) {
|
||||
break
|
||||
}
|
||||
buf = append(buf, byte(word))
|
||||
word >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
v.SetBytes(buf[:32])
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Element) fromDecimal(s string) *Element {
|
||||
n, ok := new(big.Int).SetString(s, 10)
|
||||
if !ok {
|
||||
panic("not a valid decimal: " + s)
|
||||
}
|
||||
return v.fromBig(n)
|
||||
}
|
||||
|
||||
// toBig returns v as a big.Int.
|
||||
func (v *Element) toBig() *big.Int {
|
||||
buf := v.Bytes()
|
||||
|
||||
words := make([]big.Word, 32*8/bits.UintSize)
|
||||
for n := range words {
|
||||
for i := 0; i < bits.UintSize; i += 8 {
|
||||
if len(buf) == 0 {
|
||||
break
|
||||
}
|
||||
words[n] |= big.Word(buf[0]) << big.Word(i)
|
||||
buf = buf[1:]
|
||||
}
|
||||
}
|
||||
|
||||
return new(big.Int).SetBits(words)
|
||||
}
|
||||
|
||||
func TestDecimalConstants(t *testing.T) {
|
||||
sqrtM1String := "19681161376707505956807079304988542015446066515923890162744021073123829784752"
|
||||
if exp := new(Element).fromDecimal(sqrtM1String); sqrtM1.Equal(exp) != 1 {
|
||||
t.Errorf("sqrtM1 is %v, expected %v", sqrtM1, exp)
|
||||
}
|
||||
// d is in the parent package, and we don't want to expose d or fromDecimal.
|
||||
// dString := "37095705934669439343138083508754565189542113879843219016388785533085940283555"
|
||||
// if exp := new(Element).fromDecimal(dString); d.Equal(exp) != 1 {
|
||||
// t.Errorf("d is %v, expected %v", d, exp)
|
||||
// }
|
||||
}
|
||||
|
||||
func TestSetBytesRoundTripEdgeCases(t *testing.T) {
|
||||
// TODO: values close to 0, close to 2^255-19, between 2^255-19 and 2^255-1,
|
||||
// and between 2^255 and 2^256-1. Test both the documented SetBytes
|
||||
// behavior, and that Bytes reduces them.
|
||||
}
|
||||
|
||||
// Tests self-consistency between Multiply and Square.
|
||||
func TestConsistency(t *testing.T) {
|
||||
var x Element
|
||||
var x2, x2sq Element
|
||||
|
||||
x = Element{1, 1, 1, 1, 1}
|
||||
x2.Multiply(&x, &x)
|
||||
x2sq.Square(&x)
|
||||
|
||||
if x2 != x2sq {
|
||||
t.Fatalf("all ones failed\nmul: %x\nsqr: %x\n", x2, x2sq)
|
||||
}
|
||||
|
||||
var bytes [32]byte
|
||||
|
||||
_, err := io.ReadFull(rand.Reader, bytes[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x.SetBytes(bytes[:])
|
||||
|
||||
x2.Multiply(&x, &x)
|
||||
x2sq.Square(&x)
|
||||
|
||||
if x2 != x2sq {
|
||||
t.Fatalf("all ones failed\nmul: %x\nsqr: %x\n", x2, x2sq)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
x := Element{1, 1, 1, 1, 1}
|
||||
y := Element{5, 4, 3, 2, 1}
|
||||
|
||||
eq := x.Equal(&x)
|
||||
if eq != 1 {
|
||||
t.Errorf("wrong about equality")
|
||||
}
|
||||
|
||||
eq = x.Equal(&y)
|
||||
if eq != 0 {
|
||||
t.Errorf("wrong about inequality")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvert(t *testing.T) {
|
||||
x := Element{1, 1, 1, 1, 1}
|
||||
one := Element{1, 0, 0, 0, 0}
|
||||
var xinv, r Element
|
||||
|
||||
xinv.Invert(&x)
|
||||
r.Multiply(&x, &xinv)
|
||||
r.reduce()
|
||||
|
||||
if one != r {
|
||||
t.Errorf("inversion identity failed, got: %x", r)
|
||||
}
|
||||
|
||||
var bytes [32]byte
|
||||
|
||||
_, err := io.ReadFull(rand.Reader, bytes[:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
x.SetBytes(bytes[:])
|
||||
|
||||
xinv.Invert(&x)
|
||||
r.Multiply(&x, &xinv)
|
||||
r.reduce()
|
||||
|
||||
if one != r {
|
||||
t.Errorf("random inversion identity failed, got: %x for field element %x", r, x)
|
||||
}
|
||||
|
||||
zero := Element{}
|
||||
x.Set(&zero)
|
||||
if xx := xinv.Invert(&x); xx != &xinv {
|
||||
t.Errorf("inverting zero did not return the receiver")
|
||||
} else if xinv.Equal(&zero) != 1 {
|
||||
t.Errorf("inverting zero did not return zero")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectSwap(t *testing.T) {
|
||||
a := Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676}
|
||||
b := Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972}
|
||||
|
||||
var c, d Element
|
||||
|
||||
c.Select(&a, &b, 1)
|
||||
d.Select(&a, &b, 0)
|
||||
|
||||
if c.Equal(&a) != 1 || d.Equal(&b) != 1 {
|
||||
t.Errorf("Select failed")
|
||||
}
|
||||
|
||||
c.Swap(&d, 0)
|
||||
|
||||
if c.Equal(&a) != 1 || d.Equal(&b) != 1 {
|
||||
t.Errorf("Swap failed")
|
||||
}
|
||||
|
||||
c.Swap(&d, 1)
|
||||
|
||||
if c.Equal(&b) != 1 || d.Equal(&a) != 1 {
|
||||
t.Errorf("Swap failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMult32(t *testing.T) {
|
||||
mult32EquivalentToMul := func(x Element, y uint32) bool {
|
||||
t1 := new(Element)
|
||||
for i := 0; i < 100; i++ {
|
||||
t1.Mult32(&x, y)
|
||||
}
|
||||
|
||||
ty := new(Element)
|
||||
ty.l0 = uint64(y)
|
||||
|
||||
t2 := new(Element)
|
||||
for i := 0; i < 100; i++ {
|
||||
t2.Multiply(&x, ty)
|
||||
}
|
||||
|
||||
return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2)
|
||||
}
|
||||
|
||||
if err := quick.Check(mult32EquivalentToMul, quickCheckConfig(1024)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSqrtRatio(t *testing.T) {
|
||||
// From draft-irtf-cfrg-ristretto255-decaf448-00, Appendix A.4.
|
||||
type test struct {
|
||||
u, v []byte
|
||||
wasSquare int
|
||||
r []byte
|
||||
}
|
||||
var tests = []test{
|
||||
// If u is 0, the function is defined to return (0, TRUE), even if v
|
||||
// is zero. Note that where used in this package, the denominator v
|
||||
// is never zero.
|
||||
{
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
1, decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
// 0/1 == 0²
|
||||
{
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
1, decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
// If u is non-zero and v is zero, defined to return (0, FALSE).
|
||||
{
|
||||
decodeHex("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
0, decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
// 2/1 is not square in this field.
|
||||
{
|
||||
decodeHex("0200000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
0, decodeHex("3c5ff1b5d8e4113b871bd052f9e7bcd0582804c266ffb2d4f4203eb07fdb7c54"),
|
||||
},
|
||||
// 4/1 == 2²
|
||||
{
|
||||
decodeHex("0400000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
1, decodeHex("0200000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
// 1/4 == (2⁻¹)² == (2^(p-2))² per Euler's theorem
|
||||
{
|
||||
decodeHex("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
decodeHex("0400000000000000000000000000000000000000000000000000000000000000"),
|
||||
1, decodeHex("f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
u, _ := new(Element).SetBytes(tt.u)
|
||||
v, _ := new(Element).SetBytes(tt.v)
|
||||
want, _ := new(Element).SetBytes(tt.r)
|
||||
got, wasSquare := new(Element).SqrtRatio(u, v)
|
||||
if got.Equal(want) == 0 || wasSquare != tt.wasSquare {
|
||||
t.Errorf("%d: got (%v, %v), want (%v, %v)", i, got, wasSquare, want, tt.wasSquare)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeSquare(t *testing.T) {
|
||||
asmLikeGeneric := func(a Element) bool {
|
||||
t1 := a
|
||||
t2 := a
|
||||
|
||||
feSquareGeneric(&t1, &t1)
|
||||
feSquare(&t2, &t2)
|
||||
|
||||
if t1 != t2 {
|
||||
t.Logf("got: %#v,\nexpected: %#v", t1, t2)
|
||||
}
|
||||
|
||||
return t1 == t2 && isInBounds(&t2)
|
||||
}
|
||||
|
||||
if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeMul(t *testing.T) {
|
||||
asmLikeGeneric := func(a, b Element) bool {
|
||||
a1 := a
|
||||
a2 := a
|
||||
b1 := b
|
||||
b2 := b
|
||||
|
||||
feMulGeneric(&a1, &a1, &b1)
|
||||
feMul(&a2, &a2, &b2)
|
||||
|
||||
if a1 != a2 || b1 != b2 {
|
||||
t.Logf("got: %#v,\nexpected: %#v", a1, a2)
|
||||
t.Logf("got: %#v,\nexpected: %#v", b1, b2)
|
||||
}
|
||||
|
||||
return a1 == a2 && isInBounds(&a2) &&
|
||||
b1 == b2 && isInBounds(&b2)
|
||||
}
|
||||
|
||||
if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeHex(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module filippo.io/edwards25519
|
||||
|
||||
go 1.24.0
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <tag>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TAG="$1"
|
||||
TMPDIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TMPDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
command -v git >/dev/null
|
||||
command -v git-filter-repo >/dev/null
|
||||
|
||||
if [ -d "$HOME/go/.git" ]; then
|
||||
REFERENCE=(--reference "$HOME/go" --dissociate)
|
||||
else
|
||||
REFERENCE=()
|
||||
fi
|
||||
|
||||
git -c advice.detachedHead=false clone --no-checkout "${REFERENCE[@]}" \
|
||||
-b "$TAG" https://go.googlesource.com/go.git "$TMPDIR"
|
||||
|
||||
# Simplify the history graph by removing the dev.boringcrypto branches, whose
|
||||
# merges end up empty after grafting anyway. This also fixes a weird quirk
|
||||
# (maybe a git-filter-repo bug?) where only one file from an old path,
|
||||
# src/crypto/ed25519/internal/edwards25519/const.go, would still exist in the
|
||||
# filtered repo.
|
||||
git -C "$TMPDIR" replace --graft f771edd7f9 99f1bf54eb
|
||||
git -C "$TMPDIR" replace --graft 109c13b64f c2f96e686f
|
||||
git -C "$TMPDIR" replace --graft aa4da4f189 912f075047
|
||||
|
||||
git -C "$TMPDIR" filter-repo --force \
|
||||
--paths-from-file /dev/stdin \
|
||||
--prune-empty always \
|
||||
--prune-degenerate always \
|
||||
--tag-callback 'tag.skip()' <<'EOF'
|
||||
src/crypto/internal/fips140/edwards25519
|
||||
src/crypto/internal/edwards25519
|
||||
src/crypto/ed25519/internal/edwards25519
|
||||
EOF
|
||||
|
||||
git fetch "$TMPDIR"
|
||||
git update-ref "refs/heads/upstream/$TAG" FETCH_HEAD
|
||||
|
||||
echo
|
||||
echo "Fetched upstream history up to $TAG. Merge with:"
|
||||
echo -e "\tgit merge --no-ff --no-commit --allow-unrelated-histories upstream/$TAG"
|
||||
@@ -0,0 +1,352 @@
|
||||
// Copyright (c) 2016 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 edwards25519
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// A Scalar is an integer modulo
|
||||
//
|
||||
// l = 2^252 + 27742317777372353535851937790883648493
|
||||
//
|
||||
// which is the prime order of the edwards25519 group.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and
|
||||
// receivers are allowed to alias.
|
||||
//
|
||||
// The zero value is a valid zero element.
|
||||
type Scalar struct {
|
||||
// s is the scalar in the Montgomery domain, in the format of the
|
||||
// fiat-crypto implementation.
|
||||
s fiatScalarMontgomeryDomainFieldElement
|
||||
}
|
||||
|
||||
// The field implementation in scalar_fiat.go is generated by the fiat-crypto
|
||||
// project (https://github.com/mit-plv/fiat-crypto) at version v0.0.9 (23d2dbc)
|
||||
// from a formally verified model.
|
||||
//
|
||||
// fiat-crypto code comes under the following license.
|
||||
//
|
||||
// Copyright (c) 2015-2020 The fiat-crypto Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
|
||||
// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
// NewScalar returns a new zero Scalar.
|
||||
func NewScalar() *Scalar {
|
||||
return &Scalar{}
|
||||
}
|
||||
|
||||
// MultiplyAdd sets s = x * y + z mod l, and returns s. It is equivalent to
|
||||
// using Multiply and then Add.
|
||||
func (s *Scalar) MultiplyAdd(x, y, z *Scalar) *Scalar {
|
||||
// Make a copy of z in case it aliases s.
|
||||
zCopy := new(Scalar).Set(z)
|
||||
return s.Multiply(x, y).Add(s, zCopy)
|
||||
}
|
||||
|
||||
// Add sets s = x + y mod l, and returns s.
|
||||
func (s *Scalar) Add(x, y *Scalar) *Scalar {
|
||||
// s = 1 * x + y mod l
|
||||
fiatScalarAdd(&s.s, &x.s, &y.s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Subtract sets s = x - y mod l, and returns s.
|
||||
func (s *Scalar) Subtract(x, y *Scalar) *Scalar {
|
||||
// s = -1 * y + x mod l
|
||||
fiatScalarSub(&s.s, &x.s, &y.s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Negate sets s = -x mod l, and returns s.
|
||||
func (s *Scalar) Negate(x *Scalar) *Scalar {
|
||||
// s = -1 * x + 0 mod l
|
||||
fiatScalarOpp(&s.s, &x.s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Multiply sets s = x * y mod l, and returns s.
|
||||
func (s *Scalar) Multiply(x, y *Scalar) *Scalar {
|
||||
// s = x * y + 0 mod l
|
||||
fiatScalarMul(&s.s, &x.s, &y.s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Set sets s = x, and returns s.
|
||||
func (s *Scalar) Set(x *Scalar) *Scalar {
|
||||
*s = *x
|
||||
return s
|
||||
}
|
||||
|
||||
// SetUniformBytes sets s = x mod l, where x is a 64-byte little-endian integer.
|
||||
// If x is not of the right length, SetUniformBytes returns nil and an error,
|
||||
// and the receiver is unchanged.
|
||||
//
|
||||
// SetUniformBytes can be used to set s to a uniformly distributed value given
|
||||
// 64 uniformly distributed random bytes.
|
||||
func (s *Scalar) SetUniformBytes(x []byte) (*Scalar, error) {
|
||||
if len(x) != 64 {
|
||||
return nil, errors.New("edwards25519: invalid SetUniformBytes input length")
|
||||
}
|
||||
|
||||
// We have a value x of 512 bits, but our fiatScalarFromBytes function
|
||||
// expects an input lower than l, which is a little over 252 bits.
|
||||
//
|
||||
// Instead of writing a reduction function that operates on wider inputs, we
|
||||
// can interpret x as the sum of three shorter values a, b, and c.
|
||||
//
|
||||
// x = a + b * 2^168 + c * 2^336 mod l
|
||||
//
|
||||
// We then precompute 2^168 and 2^336 modulo l, and perform the reduction
|
||||
// with two multiplications and two additions.
|
||||
|
||||
s.setShortBytes(x[:21])
|
||||
t := new(Scalar).setShortBytes(x[21:42])
|
||||
s.Add(s, t.Multiply(t, scalarTwo168))
|
||||
t.setShortBytes(x[42:])
|
||||
s.Add(s, t.Multiply(t, scalarTwo336))
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// scalarTwo168 and scalarTwo336 are 2^168 and 2^336 modulo l, encoded as a
|
||||
// fiatScalarMontgomeryDomainFieldElement, which is a little-endian 4-limb value
|
||||
// in the 2^256 Montgomery domain.
|
||||
var scalarTwo168 = &Scalar{s: [4]uint64{0x5b8ab432eac74798, 0x38afddd6de59d5d7,
|
||||
0xa2c131b399411b7c, 0x6329a7ed9ce5a30}}
|
||||
var scalarTwo336 = &Scalar{s: [4]uint64{0xbd3d108e2b35ecc5, 0x5c3a3718bdf9c90b,
|
||||
0x63aa97a331b4f2ee, 0x3d217f5be65cb5c}}
|
||||
|
||||
// setShortBytes sets s = x mod l, where x is a little-endian integer shorter
|
||||
// than 32 bytes.
|
||||
func (s *Scalar) setShortBytes(x []byte) *Scalar {
|
||||
if len(x) >= 32 {
|
||||
panic("edwards25519: internal error: setShortBytes called with a long string")
|
||||
}
|
||||
var buf [32]byte
|
||||
copy(buf[:], x)
|
||||
fiatScalarFromBytes((*[4]uint64)(&s.s), &buf)
|
||||
fiatScalarToMontgomery(&s.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&s.s))
|
||||
return s
|
||||
}
|
||||
|
||||
// SetCanonicalBytes sets s = x, where x is a 32-byte little-endian encoding of
|
||||
// s, and returns s. If x is not a canonical encoding of s, SetCanonicalBytes
|
||||
// returns nil and an error, and the receiver is unchanged.
|
||||
func (s *Scalar) SetCanonicalBytes(x []byte) (*Scalar, error) {
|
||||
if len(x) != 32 {
|
||||
return nil, errors.New("invalid scalar length")
|
||||
}
|
||||
if !isReduced(x) {
|
||||
return nil, errors.New("invalid scalar encoding")
|
||||
}
|
||||
|
||||
fiatScalarFromBytes((*[4]uint64)(&s.s), (*[32]byte)(x))
|
||||
fiatScalarToMontgomery(&s.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&s.s))
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// scalarMinusOneBytes is l - 1 in little endian.
|
||||
var scalarMinusOneBytes = [32]byte{236, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16}
|
||||
|
||||
// isReduced returns whether the given scalar in 32-byte little endian encoded
|
||||
// form is reduced modulo l.
|
||||
func isReduced(s []byte) bool {
|
||||
if len(s) != 32 {
|
||||
return false
|
||||
}
|
||||
|
||||
s0 := binary.LittleEndian.Uint64(s[:8])
|
||||
s1 := binary.LittleEndian.Uint64(s[8:16])
|
||||
s2 := binary.LittleEndian.Uint64(s[16:24])
|
||||
s3 := binary.LittleEndian.Uint64(s[24:])
|
||||
|
||||
l0 := binary.LittleEndian.Uint64(scalarMinusOneBytes[:8])
|
||||
l1 := binary.LittleEndian.Uint64(scalarMinusOneBytes[8:16])
|
||||
l2 := binary.LittleEndian.Uint64(scalarMinusOneBytes[16:24])
|
||||
l3 := binary.LittleEndian.Uint64(scalarMinusOneBytes[24:])
|
||||
|
||||
// Do a constant time subtraction chain scalarMinusOneBytes - s. If there is
|
||||
// a borrow at the end, then s > scalarMinusOneBytes.
|
||||
_, b := bits.Sub64(l0, s0, 0)
|
||||
_, b = bits.Sub64(l1, s1, b)
|
||||
_, b = bits.Sub64(l2, s2, b)
|
||||
_, b = bits.Sub64(l3, s3, b)
|
||||
return b == 0
|
||||
}
|
||||
|
||||
// SetBytesWithClamping applies the buffer pruning described in RFC 8032,
|
||||
// Section 5.1.5 (also known as clamping) and sets s to the result. The input
|
||||
// must be 32 bytes, and it is not modified. If x is not of the right length,
|
||||
// SetBytesWithClamping returns nil and an error, and the receiver is unchanged.
|
||||
//
|
||||
// Note that since Scalar values are always reduced modulo the prime order of
|
||||
// the curve, the resulting value will not preserve any of the cofactor-clearing
|
||||
// properties that clamping is meant to provide. It will however work as
|
||||
// expected as long as it is applied to points on the prime order subgroup, like
|
||||
// in Ed25519. In fact, it is lost to history why RFC 8032 adopted the
|
||||
// irrelevant RFC 7748 clamping, but it is now required for compatibility.
|
||||
func (s *Scalar) SetBytesWithClamping(x []byte) (*Scalar, error) {
|
||||
// The description above omits the purpose of the high bits of the clamping
|
||||
// for brevity, but those are also lost to reductions, and are also
|
||||
// irrelevant to edwards25519 as they protect against a specific
|
||||
// implementation bug that was once observed in a generic Montgomery ladder.
|
||||
if len(x) != 32 {
|
||||
return nil, errors.New("edwards25519: invalid SetBytesWithClamping input length")
|
||||
}
|
||||
|
||||
// We need to use the wide reduction from SetUniformBytes, since clamping
|
||||
// sets the 2^254 bit, making the value higher than the order.
|
||||
var wideBytes [64]byte
|
||||
copy(wideBytes[:], x[:])
|
||||
wideBytes[0] &= 248
|
||||
wideBytes[31] &= 63
|
||||
wideBytes[31] |= 64
|
||||
return s.SetUniformBytes(wideBytes[:])
|
||||
}
|
||||
|
||||
// Bytes returns the canonical 32-byte little-endian encoding of s.
|
||||
func (s *Scalar) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var encoded [32]byte
|
||||
return s.bytes(&encoded)
|
||||
}
|
||||
|
||||
func (s *Scalar) bytes(out *[32]byte) []byte {
|
||||
var ss fiatScalarNonMontgomeryDomainFieldElement
|
||||
fiatScalarFromMontgomery(&ss, &s.s)
|
||||
fiatScalarToBytes(out, (*[4]uint64)(&ss))
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// Equal returns 1 if s and t are equal, and 0 otherwise.
|
||||
func (s *Scalar) Equal(t *Scalar) int {
|
||||
var diff fiatScalarMontgomeryDomainFieldElement
|
||||
fiatScalarSub(&diff, &s.s, &t.s)
|
||||
var nonzero uint64
|
||||
fiatScalarNonzero(&nonzero, (*[4]uint64)(&diff))
|
||||
nonzero |= nonzero >> 32
|
||||
nonzero |= nonzero >> 16
|
||||
nonzero |= nonzero >> 8
|
||||
nonzero |= nonzero >> 4
|
||||
nonzero |= nonzero >> 2
|
||||
nonzero |= nonzero >> 1
|
||||
return int(^nonzero) & 1
|
||||
}
|
||||
|
||||
// nonAdjacentForm computes a width-w non-adjacent form for this scalar.
|
||||
//
|
||||
// w must be between 2 and 8, or nonAdjacentForm will panic.
|
||||
func (s *Scalar) nonAdjacentForm(w uint) [256]int8 {
|
||||
// This implementation is adapted from the one
|
||||
// in curve25519-dalek and is documented there:
|
||||
// https://github.com/dalek-cryptography/curve25519-dalek/blob/f630041af28e9a405255f98a8a93adca18e4315b/src/scalar.rs#L800-L871
|
||||
b := s.Bytes()
|
||||
if b[31] > 127 {
|
||||
panic("scalar has high bit set illegally")
|
||||
}
|
||||
if w < 2 {
|
||||
panic("w must be at least 2 by the definition of NAF")
|
||||
} else if w > 8 {
|
||||
panic("NAF digits must fit in int8")
|
||||
}
|
||||
|
||||
var naf [256]int8
|
||||
var digits [5]uint64
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
digits[i] = binary.LittleEndian.Uint64(b[i*8:])
|
||||
}
|
||||
|
||||
width := uint64(1 << w)
|
||||
windowMask := uint64(width - 1)
|
||||
|
||||
pos := uint(0)
|
||||
carry := uint64(0)
|
||||
for pos < 256 {
|
||||
indexU64 := pos / 64
|
||||
indexBit := pos % 64
|
||||
var bitBuf uint64
|
||||
if indexBit < 64-w {
|
||||
// This window's bits are contained in a single u64
|
||||
bitBuf = digits[indexU64] >> indexBit
|
||||
} else {
|
||||
// Combine the current 64 bits with bits from the next 64
|
||||
bitBuf = (digits[indexU64] >> indexBit) | (digits[1+indexU64] << (64 - indexBit))
|
||||
}
|
||||
|
||||
// Add carry into the current window
|
||||
window := carry + (bitBuf & windowMask)
|
||||
|
||||
if window&1 == 0 {
|
||||
// If the window value is even, preserve the carry and continue.
|
||||
// Why is the carry preserved?
|
||||
// If carry == 0 and window & 1 == 0,
|
||||
// then the next carry should be 0
|
||||
// If carry == 1 and window & 1 == 0,
|
||||
// then bit_buf & 1 == 1 so the next carry should be 1
|
||||
pos += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if window < width/2 {
|
||||
carry = 0
|
||||
naf[pos] = int8(window)
|
||||
} else {
|
||||
carry = 1
|
||||
naf[pos] = int8(window) - int8(width)
|
||||
}
|
||||
|
||||
pos += w
|
||||
}
|
||||
return naf
|
||||
}
|
||||
|
||||
func (s *Scalar) signedRadix16() [64]int8 {
|
||||
b := s.Bytes()
|
||||
if b[31] > 127 {
|
||||
panic("scalar has high bit set illegally")
|
||||
}
|
||||
|
||||
var digits [64]int8
|
||||
|
||||
// Compute unsigned radix-16 digits:
|
||||
for i := 0; i < 32; i++ {
|
||||
digits[2*i] = int8(b[i] & 15)
|
||||
digits[2*i+1] = int8((b[i] >> 4) & 15)
|
||||
}
|
||||
|
||||
// Recenter coefficients:
|
||||
for i := 0; i < 63; i++ {
|
||||
carry := (digits[i] + 8) >> 4
|
||||
digits[i] -= carry << 4
|
||||
digits[i+1] += carry
|
||||
}
|
||||
|
||||
return digits
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2019 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 edwards25519
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
func TestScalarAliasing(t *testing.T) {
|
||||
checkAliasingOneArg := func(f func(v, x *Scalar) *Scalar, v, x Scalar) bool {
|
||||
x1, v1 := x, x
|
||||
|
||||
// Calculate a reference f(x) without aliasing.
|
||||
if out := f(&v, &x); out != &v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the argument and the receiver.
|
||||
if out := f(&v1, &v1); out != &v1 || v1 != v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the arguments was not modified.
|
||||
return x == x1
|
||||
}
|
||||
|
||||
checkAliasingTwoArgs := func(f func(v, x, y *Scalar) *Scalar, v, x, y Scalar) bool {
|
||||
x1, y1, v1 := x, y, Scalar{}
|
||||
|
||||
// Calculate a reference f(x, y) without aliasing.
|
||||
if out := f(&v, &x, &y); out != &v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the first argument and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &v1, &y); out != &v1 || v1 != v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
// Test aliasing the second argument and the receiver.
|
||||
v1 = y
|
||||
if out := f(&v1, &x, &v1); out != &v1 || v1 != v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Calculate a reference f(x, x) without aliasing.
|
||||
if out := f(&v, &x, &x); out != &v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Test aliasing the first argument and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &v1, &x); out != &v1 || v1 != v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
// Test aliasing the second argument and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &x, &v1); out != &v1 || v1 != v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
// Test aliasing both arguments and the receiver.
|
||||
v1 = x
|
||||
if out := f(&v1, &v1, &v1); out != &v1 || v1 != v || !isReduced(out.Bytes()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the arguments were not modified.
|
||||
return x == x1 && y == y1
|
||||
}
|
||||
|
||||
for name, f := range map[string]any{
|
||||
"Negate": func(v, x Scalar) bool {
|
||||
return checkAliasingOneArg((*Scalar).Negate, v, x)
|
||||
},
|
||||
"Invert": func(v, x Scalar) bool {
|
||||
return checkAliasingOneArg((*Scalar).Invert, v, x)
|
||||
},
|
||||
"Multiply": func(v, x, y Scalar) bool {
|
||||
return checkAliasingTwoArgs((*Scalar).Multiply, v, x, y)
|
||||
},
|
||||
"Add": func(v, x, y Scalar) bool {
|
||||
return checkAliasingTwoArgs((*Scalar).Add, v, x, y)
|
||||
},
|
||||
"Subtract": func(v, x, y Scalar) bool {
|
||||
return checkAliasingTwoArgs((*Scalar).Subtract, v, x, y)
|
||||
},
|
||||
"MultiplyAdd1": func(v, x, y, fixed Scalar) bool {
|
||||
return checkAliasingTwoArgs(func(v, x, y *Scalar) *Scalar {
|
||||
return v.MultiplyAdd(&fixed, x, y)
|
||||
}, v, x, y)
|
||||
},
|
||||
"MultiplyAdd2": func(v, x, y, fixed Scalar) bool {
|
||||
return checkAliasingTwoArgs(func(v, x, y *Scalar) *Scalar {
|
||||
return v.MultiplyAdd(x, &fixed, y)
|
||||
}, v, x, y)
|
||||
},
|
||||
"MultiplyAdd3": func(v, x, y, fixed Scalar) bool {
|
||||
return checkAliasingTwoArgs(func(v, x, y *Scalar) *Scalar {
|
||||
return v.MultiplyAdd(x, y, &fixed)
|
||||
}, v, x, y)
|
||||
},
|
||||
} {
|
||||
err := quick.Check(f, quickCheckConfig(32))
|
||||
if err != nil {
|
||||
t.Errorf("%v: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
// Copyright (c) 2019 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 edwards25519
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
mathrand "math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
// quickCheckConfig returns a quick.Config that scales the max count by the
|
||||
// given factor if the -short flag is not set.
|
||||
func quickCheckConfig(slowScale int) *quick.Config {
|
||||
cfg := new(quick.Config)
|
||||
if !testing.Short() {
|
||||
cfg.MaxCountScale = float64(slowScale)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
var scOneBytes = [32]byte{1}
|
||||
var scOne, _ = new(Scalar).SetCanonicalBytes(scOneBytes[:])
|
||||
var scMinusOne = new(Scalar).Subtract(new(Scalar), scOne)
|
||||
|
||||
// Generate returns a valid (reduced modulo l) Scalar with a distribution
|
||||
// weighted towards high, low, and edge values.
|
||||
func (Scalar) Generate(rand *mathrand.Rand, size int) reflect.Value {
|
||||
var s [32]byte
|
||||
diceRoll := rand.Intn(100)
|
||||
switch {
|
||||
case diceRoll == 0:
|
||||
case diceRoll == 1:
|
||||
s = scOneBytes
|
||||
case diceRoll == 2:
|
||||
s = [32]byte(scMinusOne.Bytes())
|
||||
case diceRoll < 5:
|
||||
// Generate a low scalar in [0, 2^125).
|
||||
rand.Read(s[:16])
|
||||
s[15] &= (1 << 5) - 1
|
||||
case diceRoll < 10:
|
||||
// Generate a high scalar in [2^252, 2^252 + 2^124).
|
||||
s[31] = 1 << 4
|
||||
rand.Read(s[:16])
|
||||
s[15] &= (1 << 4) - 1
|
||||
default:
|
||||
// Generate a valid scalar in [0, l) by returning [0, 2^252) which has a
|
||||
// negligibly different distribution (the former has a 2^-127.6 chance
|
||||
// of being out of the latter range).
|
||||
rand.Read(s[:])
|
||||
s[31] &= (1 << 4) - 1
|
||||
}
|
||||
|
||||
val := Scalar{}
|
||||
fiatScalarFromBytes((*[4]uint64)(&val.s), &s)
|
||||
fiatScalarToMontgomery(&val.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&val.s))
|
||||
|
||||
return reflect.ValueOf(val)
|
||||
}
|
||||
|
||||
func TestScalarGenerate(t *testing.T) {
|
||||
f := func(sc Scalar) bool {
|
||||
return isReduced(sc.Bytes())
|
||||
}
|
||||
if err := quick.Check(f, quickCheckConfig(1024)); err != nil {
|
||||
t.Errorf("generated unreduced scalar: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarSetCanonicalBytes(t *testing.T) {
|
||||
f1 := func(in [32]byte, sc Scalar) bool {
|
||||
// Mask out top 4 bits to guarantee value falls in [0, l).
|
||||
in[len(in)-1] &= (1 << 4) - 1
|
||||
if _, err := sc.SetCanonicalBytes(in[:]); err != nil {
|
||||
return false
|
||||
}
|
||||
repr := sc.Bytes()
|
||||
return bytes.Equal(in[:], repr) && isReduced(repr)
|
||||
}
|
||||
if err := quick.Check(f1, quickCheckConfig(1024)); err != nil {
|
||||
t.Errorf("failed bytes->scalar->bytes round-trip: %v", err)
|
||||
}
|
||||
|
||||
f2 := func(sc1, sc2 Scalar) bool {
|
||||
if _, err := sc2.SetCanonicalBytes(sc1.Bytes()); err != nil {
|
||||
return false
|
||||
}
|
||||
return sc1 == sc2
|
||||
}
|
||||
if err := quick.Check(f2, quickCheckConfig(1024)); err != nil {
|
||||
t.Errorf("failed scalar->bytes->scalar round-trip: %v", err)
|
||||
}
|
||||
|
||||
expectReject := func(b []byte) {
|
||||
t.Helper()
|
||||
s := scOne
|
||||
if out, err := s.SetCanonicalBytes(b[:]); err == nil {
|
||||
t.Errorf("SetCanonicalBytes worked on a non-canonical value")
|
||||
} else if s != scOne {
|
||||
t.Errorf("SetCanonicalBytes modified its receiver")
|
||||
} else if out != nil {
|
||||
t.Errorf("SetCanonicalBytes did not return nil with an error")
|
||||
}
|
||||
}
|
||||
|
||||
b := scMinusOne.Bytes()
|
||||
b[0] += 1
|
||||
expectReject(b)
|
||||
|
||||
b = scMinusOne.Bytes()
|
||||
b[31] += 1
|
||||
expectReject(b)
|
||||
|
||||
b = scMinusOne.Bytes()
|
||||
b[31] |= 0b1000_0000
|
||||
expectReject(b)
|
||||
}
|
||||
|
||||
func TestScalarSetUniformBytes(t *testing.T) {
|
||||
mod, _ := new(big.Int).SetString("27742317777372353535851937790883648493", 10)
|
||||
mod.Add(mod, new(big.Int).Lsh(big.NewInt(1), 252))
|
||||
f := func(in [64]byte, sc Scalar) bool {
|
||||
sc.SetUniformBytes(in[:])
|
||||
repr := sc.Bytes()
|
||||
if !isReduced(repr) {
|
||||
return false
|
||||
}
|
||||
scBig := bigIntFromLittleEndianBytes(repr[:])
|
||||
inBig := bigIntFromLittleEndianBytes(in[:])
|
||||
return inBig.Mod(inBig, mod).Cmp(scBig) == 0
|
||||
}
|
||||
if err := quick.Check(f, quickCheckConfig(1024)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarSetBytesWithClamping(t *testing.T) {
|
||||
// Generated with libsodium.js 1.0.18 crypto_scalarmult_ed25519_base.
|
||||
|
||||
random := "633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e"
|
||||
s, _ := new(Scalar).SetBytesWithClamping(decodeHex(random))
|
||||
p := new(Point).ScalarBaseMult(s)
|
||||
want := "1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94"
|
||||
if got := hex.EncodeToString(p.Bytes()); got != want {
|
||||
t.Errorf("random: got %q, want %q", got, want)
|
||||
}
|
||||
|
||||
zero := "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
s, _ = new(Scalar).SetBytesWithClamping(decodeHex(zero))
|
||||
p = new(Point).ScalarBaseMult(s)
|
||||
want = "693e47972caf527c7883ad1b39822f026f47db2ab0e1919955b8993aa04411d1"
|
||||
if got := hex.EncodeToString(p.Bytes()); got != want {
|
||||
t.Errorf("zero: got %q, want %q", got, want)
|
||||
}
|
||||
|
||||
one := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
s, _ = new(Scalar).SetBytesWithClamping(decodeHex(one))
|
||||
p = new(Point).ScalarBaseMult(s)
|
||||
want = "12e9a68b73fd5aacdbcaf3e88c46fea6ebedb1aa84eed1842f07f8edab65e3a7"
|
||||
if got := hex.EncodeToString(p.Bytes()); got != want {
|
||||
t.Errorf("one: got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func bigIntFromLittleEndianBytes(b []byte) *big.Int {
|
||||
bb := make([]byte, len(b))
|
||||
for i := range b {
|
||||
bb[i] = b[len(b)-i-1]
|
||||
}
|
||||
return new(big.Int).SetBytes(bb)
|
||||
}
|
||||
|
||||
func TestScalarMultiplyDistributesOverAdd(t *testing.T) {
|
||||
multiplyDistributesOverAdd := func(x, y, z Scalar) bool {
|
||||
// Compute t1 = (x+y)*z
|
||||
var t1 Scalar
|
||||
t1.Add(&x, &y)
|
||||
t1.Multiply(&t1, &z)
|
||||
|
||||
// Compute t2 = x*z + y*z
|
||||
var t2 Scalar
|
||||
var t3 Scalar
|
||||
t2.Multiply(&x, &z)
|
||||
t3.Multiply(&y, &z)
|
||||
t2.Add(&t2, &t3)
|
||||
|
||||
reprT1, reprT2 := t1.Bytes(), t2.Bytes()
|
||||
|
||||
return t1 == t2 && isReduced(reprT1) && isReduced(reprT2)
|
||||
}
|
||||
|
||||
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig(1024)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarAddLikeSubNeg(t *testing.T) {
|
||||
addLikeSubNeg := func(x, y Scalar) bool {
|
||||
// Compute t1 = x - y
|
||||
var t1 Scalar
|
||||
t1.Subtract(&x, &y)
|
||||
|
||||
// Compute t2 = -y + x
|
||||
var t2 Scalar
|
||||
t2.Negate(&y)
|
||||
t2.Add(&t2, &x)
|
||||
|
||||
return t1 == t2 && isReduced(t1.Bytes())
|
||||
}
|
||||
|
||||
if err := quick.Check(addLikeSubNeg, quickCheckConfig(1024)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarNonAdjacentForm(t *testing.T) {
|
||||
s, _ := (&Scalar{}).SetCanonicalBytes([]byte{
|
||||
0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d,
|
||||
0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d,
|
||||
0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1,
|
||||
0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09,
|
||||
})
|
||||
|
||||
expectedNaf := [256]int8{
|
||||
0, 13, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, -11, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1,
|
||||
0, 0, 0, 0, 9, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 11, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0,
|
||||
-9, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 9, 0,
|
||||
0, 0, 0, -15, 0, 0, 0, 0, -7, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, -3, 0,
|
||||
0, 0, 0, -11, 0, 0, 0, 0, -7, 0, 0, 0, 0, -13, 0, 0, 0, 0, 11, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 0, 0, -15, 0, 0, 0, 0, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 0, 11, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 7,
|
||||
0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
sNaf := s.nonAdjacentForm(5)
|
||||
|
||||
for i := 0; i < 256; i++ {
|
||||
if expectedNaf[i] != sNaf[i] {
|
||||
t.Errorf("Wrong digit at position %d, got %d, expected %d", i, sNaf[i], expectedNaf[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type notZeroScalar Scalar
|
||||
|
||||
func (notZeroScalar) Generate(rand *mathrand.Rand, size int) reflect.Value {
|
||||
var s Scalar
|
||||
var isNonZero uint64
|
||||
for isNonZero == 0 {
|
||||
s = Scalar{}.Generate(rand, size).Interface().(Scalar)
|
||||
fiatScalarNonzero(&isNonZero, (*[4]uint64)(&s.s))
|
||||
}
|
||||
return reflect.ValueOf(notZeroScalar(s))
|
||||
}
|
||||
|
||||
func TestScalarEqual(t *testing.T) {
|
||||
if scOne.Equal(scMinusOne) == 1 {
|
||||
t.Errorf("scOne.Equal(&scMinusOne) is true")
|
||||
}
|
||||
if scMinusOne.Equal(scMinusOne) == 0 {
|
||||
t.Errorf("scMinusOne.Equal(&scMinusOne) is false")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
// Copyright (c) 2019 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 edwards25519
|
||||
|
||||
import "sync"
|
||||
|
||||
// basepointTable is a set of 32 affineLookupTables, where table i is generated
|
||||
// from 256i * basepoint. It is precomputed the first time it's used.
|
||||
func basepointTable() *[32]affineLookupTable {
|
||||
basepointTablePrecomp.initOnce.Do(func() {
|
||||
p := NewGeneratorPoint()
|
||||
for i := 0; i < 32; i++ {
|
||||
basepointTablePrecomp.table[i].FromP3(p)
|
||||
for j := 0; j < 8; j++ {
|
||||
p.Add(p, p)
|
||||
}
|
||||
}
|
||||
})
|
||||
return &basepointTablePrecomp.table
|
||||
}
|
||||
|
||||
var basepointTablePrecomp struct {
|
||||
table [32]affineLookupTable
|
||||
initOnce sync.Once
|
||||
}
|
||||
|
||||
// ScalarBaseMult sets v = x * B, where B is the canonical generator, and
|
||||
// returns v.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
func (v *Point) ScalarBaseMult(x *Scalar) *Point {
|
||||
basepointTable := basepointTable()
|
||||
|
||||
// Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i )
|
||||
// as described in the Ed25519 paper
|
||||
//
|
||||
// Group even and odd coefficients
|
||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||
// + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B
|
||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||
// + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B)
|
||||
//
|
||||
// We use a lookup table for each i to get x_i*16^(2*i)*B
|
||||
// and do four doublings to multiply by 16.
|
||||
digits := x.signedRadix16()
|
||||
|
||||
multiple := &affineCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
|
||||
// Accumulate the odd components first
|
||||
v.Set(NewIdentityPoint())
|
||||
for i := 1; i < 64; i += 2 {
|
||||
basepointTable[i/2].SelectInto(multiple, digits[i])
|
||||
tmp1.AddAffine(v, multiple)
|
||||
v.fromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
// Multiply by 16
|
||||
tmp2.FromP3(v) // tmp2 = v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 2*v in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 4*v in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 8*v in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 16*v in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // now v = 16*(odd components)
|
||||
|
||||
// Accumulate the even components
|
||||
for i := 0; i < 64; i += 2 {
|
||||
basepointTable[i/2].SelectInto(multiple, digits[i])
|
||||
tmp1.AddAffine(v, multiple)
|
||||
v.fromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// ScalarMult sets v = x * q, and returns v.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
func (v *Point) ScalarMult(x *Scalar, q *Point) *Point {
|
||||
checkInitialized(q)
|
||||
|
||||
var table projLookupTable
|
||||
table.FromP3(q)
|
||||
|
||||
// Write x = sum(x_i * 16^i)
|
||||
// so x*Q = sum( Q*x_i*16^i )
|
||||
// = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... )
|
||||
// <------compute inside out---------
|
||||
//
|
||||
// We use the lookup table to get the x_i*Q values
|
||||
// and do four doublings to compute 16*Q
|
||||
digits := x.signedRadix16()
|
||||
|
||||
// Unwrap first loop iteration to save computing 16*identity
|
||||
multiple := &projCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
table.SelectInto(multiple, digits[63])
|
||||
|
||||
v.Set(NewIdentityPoint())
|
||||
tmp1.Add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords
|
||||
for i := 62; i >= 0; i-- {
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = (prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
||||
table.SelectInto(multiple, digits[i])
|
||||
tmp1.Add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords
|
||||
}
|
||||
v.fromP1xP1(tmp1)
|
||||
return v
|
||||
}
|
||||
|
||||
// basepointNafTable is the nafLookupTable8 for the basepoint.
|
||||
// It is precomputed the first time it's used.
|
||||
func basepointNafTable() *nafLookupTable8 {
|
||||
basepointNafTablePrecomp.initOnce.Do(func() {
|
||||
basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint())
|
||||
})
|
||||
return &basepointNafTablePrecomp.table
|
||||
}
|
||||
|
||||
var basepointNafTablePrecomp struct {
|
||||
table nafLookupTable8
|
||||
initOnce sync.Once
|
||||
}
|
||||
|
||||
// VarTimeDoubleScalarBaseMult sets v = a * A + b * B, where B is the canonical
|
||||
// generator, and returns v.
|
||||
//
|
||||
// Execution time depends on the inputs.
|
||||
func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point {
|
||||
checkInitialized(A)
|
||||
|
||||
// Similarly to the single variable-base approach, we compute
|
||||
// digits and use them with a lookup table. However, because
|
||||
// we are allowed to do variable-time operations, we don't
|
||||
// need constant-time lookups or constant-time digit
|
||||
// computations.
|
||||
//
|
||||
// So we use a non-adjacent form of some width w instead of
|
||||
// radix 16. This is like a binary representation (one digit
|
||||
// for each binary place) but we allow the digits to grow in
|
||||
// magnitude up to 2^{w-1} so that the nonzero digits are as
|
||||
// sparse as possible. Intuitively, this "condenses" the
|
||||
// "mass" of the scalar onto sparse coefficients (meaning
|
||||
// fewer additions).
|
||||
|
||||
basepointNafTable := basepointNafTable()
|
||||
var aTable nafLookupTable5
|
||||
aTable.FromP3(A)
|
||||
// Because the basepoint is fixed, we can use a wider NAF
|
||||
// corresponding to a bigger table.
|
||||
aNaf := a.nonAdjacentForm(5)
|
||||
bNaf := b.nonAdjacentForm(8)
|
||||
|
||||
// Find the first nonzero coefficient.
|
||||
i := 255
|
||||
for j := i; j >= 0; j-- {
|
||||
if aNaf[j] != 0 || bNaf[j] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
multA := &projCached{}
|
||||
multB := &affineCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
tmp2.Zero()
|
||||
|
||||
// Move from high to low bits, doubling the accumulator
|
||||
// at each iteration and checking whether there is a nonzero
|
||||
// coefficient to look up a multiple of.
|
||||
for ; i >= 0; i-- {
|
||||
tmp1.Double(tmp2)
|
||||
|
||||
// Only update v if we have a nonzero coeff to add in.
|
||||
if aNaf[i] > 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
aTable.SelectInto(multA, aNaf[i])
|
||||
tmp1.Add(v, multA)
|
||||
} else if aNaf[i] < 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
aTable.SelectInto(multA, -aNaf[i])
|
||||
tmp1.Sub(v, multA)
|
||||
}
|
||||
|
||||
if bNaf[i] > 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
basepointNafTable.SelectInto(multB, bNaf[i])
|
||||
tmp1.AddAffine(v, multB)
|
||||
} else if bNaf[i] < 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
basepointNafTable.SelectInto(multB, -bNaf[i])
|
||||
tmp1.SubAffine(v, multB)
|
||||
}
|
||||
|
||||
tmp2.FromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
v.fromP2(tmp2)
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
// Copyright (c) 2019 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 edwards25519
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
var (
|
||||
// a random scalar generated using dalek.
|
||||
dalekScalar, _ = (&Scalar{}).SetCanonicalBytes([]byte{219, 106, 114, 9, 174, 249, 155, 89, 69, 203, 201, 93, 92, 116, 234, 187, 78, 115, 103, 172, 182, 98, 62, 103, 187, 136, 13, 100, 248, 110, 12, 4})
|
||||
// the above, times the edwards25519 basepoint.
|
||||
dalekScalarBasepoint, _ = new(Point).SetBytes([]byte{0xf4, 0xef, 0x7c, 0xa, 0x34, 0x55, 0x7b, 0x9f, 0x72, 0x3b, 0xb6, 0x1e, 0xf9, 0x46, 0x9, 0x91, 0x1c, 0xb9, 0xc0, 0x6c, 0x17, 0x28, 0x2d, 0x8b, 0x43, 0x2b, 0x5, 0x18, 0x6a, 0x54, 0x3e, 0x48})
|
||||
)
|
||||
|
||||
func TestScalarMultSmallScalars(t *testing.T) {
|
||||
var z Scalar
|
||||
var p Point
|
||||
p.ScalarMult(&z, B)
|
||||
if I.Equal(&p) != 1 {
|
||||
t.Error("0*B != 0")
|
||||
}
|
||||
checkOnCurve(t, &p)
|
||||
|
||||
scEight, _ := (&Scalar{}).SetCanonicalBytes([]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
p.ScalarMult(scEight, B)
|
||||
if B.Equal(&p) != 1 {
|
||||
t.Error("1*B != 1")
|
||||
}
|
||||
checkOnCurve(t, &p)
|
||||
}
|
||||
|
||||
func TestScalarMultVsDalek(t *testing.T) {
|
||||
var p Point
|
||||
p.ScalarMult(dalekScalar, B)
|
||||
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||
t.Error("Scalar mul does not match dalek")
|
||||
}
|
||||
checkOnCurve(t, &p)
|
||||
}
|
||||
|
||||
func TestBaseMultVsDalek(t *testing.T) {
|
||||
var p Point
|
||||
p.ScalarBaseMult(dalekScalar)
|
||||
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||
t.Error("Scalar mul does not match dalek")
|
||||
}
|
||||
checkOnCurve(t, &p)
|
||||
}
|
||||
|
||||
func TestVarTimeDoubleBaseMultVsDalek(t *testing.T) {
|
||||
var p Point
|
||||
var z Scalar
|
||||
p.VarTimeDoubleScalarBaseMult(dalekScalar, B, &z)
|
||||
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||
t.Error("VarTimeDoubleScalarBaseMult fails with b=0")
|
||||
}
|
||||
checkOnCurve(t, &p)
|
||||
p.VarTimeDoubleScalarBaseMult(&z, B, dalekScalar)
|
||||
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||
t.Error("VarTimeDoubleScalarBaseMult fails with a=0")
|
||||
}
|
||||
checkOnCurve(t, &p)
|
||||
}
|
||||
|
||||
func TestScalarMultDistributesOverAdd(t *testing.T) {
|
||||
scalarMultDistributesOverAdd := func(x, y Scalar) bool {
|
||||
var z Scalar
|
||||
z.Add(&x, &y)
|
||||
var p, q, r, check Point
|
||||
p.ScalarMult(&x, B)
|
||||
q.ScalarMult(&y, B)
|
||||
r.ScalarMult(&z, B)
|
||||
check.Add(&p, &q)
|
||||
checkOnCurve(t, &p, &q, &r, &check)
|
||||
return check.Equal(&r) == 1
|
||||
}
|
||||
|
||||
if err := quick.Check(scalarMultDistributesOverAdd, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultNonIdentityPoint(t *testing.T) {
|
||||
// Check whether p.ScalarMult and q.ScalaBaseMult give the same,
|
||||
// when p and q are originally set to the base point.
|
||||
|
||||
scalarMultNonIdentityPoint := func(x Scalar) bool {
|
||||
var p, q Point
|
||||
p.Set(B)
|
||||
q.Set(B)
|
||||
|
||||
p.ScalarMult(&x, B)
|
||||
q.ScalarBaseMult(&x)
|
||||
|
||||
checkOnCurve(t, &p, &q)
|
||||
|
||||
return p.Equal(&q) == 1
|
||||
}
|
||||
|
||||
if err := quick.Check(scalarMultNonIdentityPoint, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasepointTableGeneration(t *testing.T) {
|
||||
// The basepoint table is 32 affineLookupTables,
|
||||
// corresponding to (16^2i)*B for table i.
|
||||
basepointTable := basepointTable()
|
||||
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
tmp3 := &Point{}
|
||||
tmp3.Set(B)
|
||||
table := make([]affineLookupTable, 32)
|
||||
for i := 0; i < 32; i++ {
|
||||
// Build the table
|
||||
table[i].FromP3(tmp3)
|
||||
// Assert equality with the hardcoded one
|
||||
if table[i] != basepointTable[i] {
|
||||
t.Errorf("Basepoint table %d does not match", i)
|
||||
}
|
||||
|
||||
// Set p = (16^2)*p = 256*p = 2^8*p
|
||||
tmp2.FromP3(tmp3)
|
||||
for j := 0; j < 7; j++ {
|
||||
tmp1.Double(tmp2)
|
||||
tmp2.FromP1xP1(tmp1)
|
||||
}
|
||||
tmp1.Double(tmp2)
|
||||
tmp3.fromP1xP1(tmp1)
|
||||
checkOnCurve(t, tmp3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultMatchesBaseMult(t *testing.T) {
|
||||
scalarMultMatchesBaseMult := func(x Scalar) bool {
|
||||
var p, q Point
|
||||
p.ScalarMult(&x, B)
|
||||
q.ScalarBaseMult(&x)
|
||||
checkOnCurve(t, &p, &q)
|
||||
return p.Equal(&q) == 1
|
||||
}
|
||||
|
||||
if err := quick.Check(scalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasepointNafTableGeneration(t *testing.T) {
|
||||
var table nafLookupTable8
|
||||
table.FromP3(B)
|
||||
|
||||
if table != *basepointNafTable() {
|
||||
t.Error("BasepointNafTable does not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVarTimeDoubleBaseMultMatchesBaseMult(t *testing.T) {
|
||||
varTimeDoubleBaseMultMatchesBaseMult := func(x, y Scalar) bool {
|
||||
var p, q1, q2, check Point
|
||||
|
||||
p.VarTimeDoubleScalarBaseMult(&x, B, &y)
|
||||
|
||||
q1.ScalarBaseMult(&x)
|
||||
q2.ScalarBaseMult(&y)
|
||||
check.Add(&q1, &q2)
|
||||
|
||||
checkOnCurve(t, &p, &check, &q1, &q2)
|
||||
return p.Equal(&check) == 1
|
||||
}
|
||||
|
||||
if err := quick.Check(varTimeDoubleBaseMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks.
|
||||
|
||||
func BenchmarkScalarBaseMult(b *testing.B) {
|
||||
var p Point
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.ScalarBaseMult(dalekScalar)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarMult(b *testing.B) {
|
||||
var p Point
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.ScalarMult(dalekScalar, B)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkVarTimeDoubleScalarBaseMult(b *testing.B) {
|
||||
var p Point
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.VarTimeDoubleScalarBaseMult(dalekScalar, B, dalekScalar)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2019 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 edwards25519
|
||||
|
||||
import "crypto/subtle"
|
||||
|
||||
// A dynamic lookup table for variable-base, constant-time scalar muls.
|
||||
type projLookupTable struct {
|
||||
points [8]projCached
|
||||
}
|
||||
|
||||
// A precomputed lookup table for fixed-base, constant-time scalar muls.
|
||||
type affineLookupTable struct {
|
||||
points [8]affineCached
|
||||
}
|
||||
|
||||
// A dynamic lookup table for variable-base, variable-time scalar muls.
|
||||
type nafLookupTable5 struct {
|
||||
points [8]projCached
|
||||
}
|
||||
|
||||
// A precomputed lookup table for fixed-base, variable-time scalar muls.
|
||||
type nafLookupTable8 struct {
|
||||
points [64]affineCached
|
||||
}
|
||||
|
||||
// Constructors.
|
||||
|
||||
// Builds a lookup table at runtime. Fast.
|
||||
func (v *projLookupTable) FromP3(q *Point) {
|
||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||
v.points[0].FromP3(q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
// Compute (i+1)*Q as Q + i*Q and convert to a projCached
|
||||
// This is needlessly complicated because the API has explicit
|
||||
// receivers instead of creating stack objects and relying on RVO
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||
func (v *affineLookupTable) FromP3(q *Point) {
|
||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||
v.points[0].FromP3(q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
// Compute (i+1)*Q as Q + i*Q and convert to affineCached
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// Builds a lookup table at runtime. Fast.
|
||||
func (v *nafLookupTable5) FromP3(q *Point) {
|
||||
// Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q
|
||||
// This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q
|
||||
v.points[0].FromP3(q)
|
||||
q2 := Point{}
|
||||
q2.Add(q, q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(&q2, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||
func (v *nafLookupTable8) FromP3(q *Point) {
|
||||
v.points[0].FromP3(q)
|
||||
q2 := Point{}
|
||||
q2.Add(q, q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 63; i++ {
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// Selectors.
|
||||
|
||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||
func (v *projLookupTable) SelectInto(dest *projCached, x int8) {
|
||||
// Compute xabs = |x|
|
||||
xmask := x >> 7
|
||||
xabs := uint8((x + xmask) ^ xmask)
|
||||
|
||||
dest.Zero()
|
||||
for j := 1; j <= 8; j++ {
|
||||
// Set dest = j*Q if |x| = j
|
||||
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||
dest.Select(&v.points[j-1], dest, cond)
|
||||
}
|
||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||
dest.CondNeg(int(xmask & 1))
|
||||
}
|
||||
|
||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||
func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) {
|
||||
// Compute xabs = |x|
|
||||
xmask := x >> 7
|
||||
xabs := uint8((x + xmask) ^ xmask)
|
||||
|
||||
dest.Zero()
|
||||
for j := 1; j <= 8; j++ {
|
||||
// Set dest = j*Q if |x| = j
|
||||
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||
dest.Select(&v.points[j-1], dest, cond)
|
||||
}
|
||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||
dest.CondNeg(int(xmask & 1))
|
||||
}
|
||||
|
||||
// Given odd x with 0 < x < 2^4, return x*Q (in variable time).
|
||||
func (v *nafLookupTable5) SelectInto(dest *projCached, x int8) {
|
||||
*dest = v.points[x/2]
|
||||
}
|
||||
|
||||
// Given odd x with 0 < x < 2^7, return x*Q (in variable time).
|
||||
func (v *nafLookupTable8) SelectInto(dest *affineCached, x int8) {
|
||||
*dest = v.points[x/2]
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2019 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 edwards25519
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProjLookupTable(t *testing.T) {
|
||||
var table projLookupTable
|
||||
table.FromP3(B)
|
||||
|
||||
var tmp1, tmp2, tmp3 projCached
|
||||
table.SelectInto(&tmp1, 6)
|
||||
table.SelectInto(&tmp2, -2)
|
||||
table.SelectInto(&tmp3, -4)
|
||||
// Expect T1 + T2 + T3 = identity
|
||||
|
||||
var accP1xP1 projP1xP1
|
||||
accP3 := NewIdentityPoint()
|
||||
|
||||
accP1xP1.Add(accP3, &tmp1)
|
||||
accP3.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.Add(accP3, &tmp2)
|
||||
accP3.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.Add(accP3, &tmp3)
|
||||
accP3.fromP1xP1(&accP1xP1)
|
||||
|
||||
if accP3.Equal(I) != 1 {
|
||||
t.Errorf("Consistency check on ProjLookupTable.SelectInto failed! %x %x %x", tmp1, tmp2, tmp3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAffineLookupTable(t *testing.T) {
|
||||
var table affineLookupTable
|
||||
table.FromP3(B)
|
||||
|
||||
var tmp1, tmp2, tmp3 affineCached
|
||||
table.SelectInto(&tmp1, 3)
|
||||
table.SelectInto(&tmp2, -7)
|
||||
table.SelectInto(&tmp3, 4)
|
||||
// Expect T1 + T2 + T3 = identity
|
||||
|
||||
var accP1xP1 projP1xP1
|
||||
accP3 := NewIdentityPoint()
|
||||
|
||||
accP1xP1.AddAffine(accP3, &tmp1)
|
||||
accP3.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.AddAffine(accP3, &tmp2)
|
||||
accP3.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.AddAffine(accP3, &tmp3)
|
||||
accP3.fromP1xP1(&accP1xP1)
|
||||
|
||||
if accP3.Equal(I) != 1 {
|
||||
t.Errorf("Consistency check on ProjLookupTable.SelectInto failed! %x %x %x", tmp1, tmp2, tmp3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNafLookupTable5(t *testing.T) {
|
||||
var table nafLookupTable5
|
||||
table.FromP3(B)
|
||||
|
||||
var tmp1, tmp2, tmp3, tmp4 projCached
|
||||
table.SelectInto(&tmp1, 9)
|
||||
table.SelectInto(&tmp2, 11)
|
||||
table.SelectInto(&tmp3, 7)
|
||||
table.SelectInto(&tmp4, 13)
|
||||
// Expect T1 + T2 = T3 + T4
|
||||
|
||||
var accP1xP1 projP1xP1
|
||||
lhs := NewIdentityPoint()
|
||||
rhs := NewIdentityPoint()
|
||||
|
||||
accP1xP1.Add(lhs, &tmp1)
|
||||
lhs.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.Add(lhs, &tmp2)
|
||||
lhs.fromP1xP1(&accP1xP1)
|
||||
|
||||
accP1xP1.Add(rhs, &tmp3)
|
||||
rhs.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.Add(rhs, &tmp4)
|
||||
rhs.fromP1xP1(&accP1xP1)
|
||||
|
||||
if lhs.Equal(rhs) != 1 {
|
||||
t.Errorf("Consistency check on nafLookupTable5 failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNafLookupTable8(t *testing.T) {
|
||||
var table nafLookupTable8
|
||||
table.FromP3(B)
|
||||
|
||||
var tmp1, tmp2, tmp3, tmp4 affineCached
|
||||
table.SelectInto(&tmp1, 49)
|
||||
table.SelectInto(&tmp2, 11)
|
||||
table.SelectInto(&tmp3, 35)
|
||||
table.SelectInto(&tmp4, 25)
|
||||
// Expect T1 + T2 = T3 + T4
|
||||
|
||||
var accP1xP1 projP1xP1
|
||||
lhs := NewIdentityPoint()
|
||||
rhs := NewIdentityPoint()
|
||||
|
||||
accP1xP1.AddAffine(lhs, &tmp1)
|
||||
lhs.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.AddAffine(lhs, &tmp2)
|
||||
lhs.fromP1xP1(&accP1xP1)
|
||||
|
||||
accP1xP1.AddAffine(rhs, &tmp3)
|
||||
rhs.fromP1xP1(&accP1xP1)
|
||||
accP1xP1.AddAffine(rhs, &tmp4)
|
||||
rhs.fromP1xP1(&accP1xP1)
|
||||
|
||||
if lhs.Equal(rhs) != 1 {
|
||||
t.Errorf("Consistency check on nafLookupTable8 failed")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user