312 lines
9.2 KiB
Go
312 lines
9.2 KiB
Go
// 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 {
|
|
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)
|
|
}
|
|
}
|
|
}
|