mirror of https://github.com/gtank/ristretto255
group logic WIP
This commit is contained in:
parent
7ae6271616
commit
4d11c7b5a6
17
const.go
17
const.go
|
@ -6,21 +6,6 @@ package ed25519
|
|||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/gtank/ed25519/internal/edwards25519"
|
||||
)
|
||||
|
||||
var (
|
||||
// d2 is 2*d, a curve-specific constant used in our addition formula.
|
||||
d2 = edwards25519.FieldElement{
|
||||
-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199,
|
||||
}
|
||||
|
||||
// the number 2 as a field element
|
||||
feTwo = edwards25519.FieldElement{
|
||||
2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
bigZero = big.NewInt(0)
|
||||
bigOne = big.NewInt(1)
|
||||
)
|
||||
var ()
|
||||
|
|
220
ed25519.go
220
ed25519.go
|
@ -9,9 +9,13 @@ import (
|
|||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/gtank/ed25519/internal/edwards25519"
|
||||
"github.com/gtank/ed25519/internal/group"
|
||||
field "github.com/gtank/ed25519/internal/radix51"
|
||||
)
|
||||
|
||||
var bigZero *big.Int
|
||||
var bigOne *big.Int
|
||||
|
||||
type ed25519Curve struct {
|
||||
*elliptic.CurveParams
|
||||
}
|
||||
|
@ -31,6 +35,8 @@ func initEd25519Params() {
|
|||
ed25519Params.Gx, _ = new(big.Int).SetString("15112221349535400772501151409588531511454012693041857206046113283949847762202", 10)
|
||||
ed25519Params.Gy, _ = new(big.Int).SetString("46316835694926478169428394003475163141307993866256225615783033603165251855960", 10)
|
||||
ed25519Params.BitSize = 256
|
||||
bigZero = big.NewInt(0)
|
||||
bigOne = big.NewInt(1)
|
||||
}
|
||||
|
||||
// Ed25519 returns a Curve that implements Ed25519.
|
||||
|
@ -45,98 +51,44 @@ func (curve ed25519Curve) Params() *elliptic.CurveParams {
|
|||
}
|
||||
|
||||
// IsOnCurve reports whether the given (x,y) lies on the curve by checking that
|
||||
// -x^2 + y^2 - 1 - dx^2y^2 = 0.
|
||||
// -x^2 + y^2 - 1 - dx^2y^2 = 0 (mod p).
|
||||
func (curve ed25519Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
lh := new(big.Int).Mul(x, x) // x^2
|
||||
y2 := new(big.Int).Mul(y, y) // y^2
|
||||
rh := new(big.Int).Mul(lh, y2) // x^2y^2
|
||||
rh.Mul(rh, curve.B) // dx^2y^2 with B repurposed as d
|
||||
rh.Add(rh, bigOne) // 1 + dx^2y^2
|
||||
lh.Neg(lh) // -x^2
|
||||
lh.Add(lh, y2) // -x^2 + y^2
|
||||
lh.Sub(lh, rh) // -x^2 + y^2 - 1 - dx^2y^2
|
||||
lh.Mod(lh, curve.P) // -x^2 + y^2 - 1 - dx^2y^2 mod p
|
||||
return lh.Cmp(bigZero) == 0
|
||||
var feX, feY, feB field.FieldElement
|
||||
field.FeFromBig(&feX, x)
|
||||
field.FeFromBig(&feY, y)
|
||||
field.FeFromBig(&feB, curve.B)
|
||||
|
||||
var lh, y2, rh field.FieldElement
|
||||
field.FeSquare(&lh, &feX) // x^2
|
||||
field.FeSquare(&y2, &feY) // y^2
|
||||
field.FeMul(&rh, &lh, &y2) // x^2*y^2
|
||||
field.FeMul(&rh, &rh, &feB) // d*x^2*y^2
|
||||
field.FeAdd(&rh, &rh, &field.FieldOne) // 1 + d*x^2*y^2
|
||||
field.FeNeg(&lh, &lh) // -x^2
|
||||
field.FeAdd(&lh, &lh, &y2) // -x^2 + y^2
|
||||
field.FeSub(&lh, &lh, &rh) // -x^2 + y^2 - 1 - dx^2y^2
|
||||
field.FeReduce(&lh, &lh) // mod p
|
||||
|
||||
return field.FeEqual(&lh, &field.FieldZero)
|
||||
}
|
||||
|
||||
// Add returns the sum of (x1, y1) and (x2, y2).
|
||||
func (curve ed25519Curve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
|
||||
var p1, p2, p3 edwards25519.ExtendedGroupElement
|
||||
var p1, p2 group.ExtendedGroupElement
|
||||
|
||||
affineToExtended(&p1, x1, y1)
|
||||
affineToExtended(&p2, x2, y2)
|
||||
extendedAdd(&p3, &p1, &p2)
|
||||
p1.FromAffine(x1, y1)
|
||||
p2.FromAffine(x2, y2)
|
||||
|
||||
return extendedToAffine(&p3) // 1I + 2M
|
||||
return p2.Add(&p1, &p2).ToAffine()
|
||||
}
|
||||
|
||||
// The internal edwards25519 package optimizes for a particular sequence of
|
||||
// operations, so we reimplement addition (add-2008-hwcd-3) here to avoid the
|
||||
// cost of converting between intermediate representations.
|
||||
// TODO We know Z1=1 and Z2=1 here, so mmadd-2008-hwcd-3 (6M + 1S + 1*k + 9add) could apply
|
||||
func extendedAdd(out, p1, p2 *edwards25519.ExtendedGroupElement) {
|
||||
var tmp1, tmp2, A, B, C, D, E, F, G, H edwards25519.FieldElement
|
||||
edwards25519.FeSub(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1-X1
|
||||
edwards25519.FeSub(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2-X2
|
||||
edwards25519.FeMul(&A, &tmp1, &tmp2) // A <-- tmp1*tmp2 = (Y1-X1)*(Y2-X2)
|
||||
edwards25519.FeAdd(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1+X1
|
||||
edwards25519.FeAdd(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2+X2
|
||||
edwards25519.FeMul(&B, &tmp1, &tmp2) // B <-- tmp1*tmp2 = (Y1+X1)*(Y2+X2)
|
||||
edwards25519.FeMul(&tmp1, &p1.T, &p2.T) // tmp1 <-- T1*T2
|
||||
edwards25519.FeMul(&C, &tmp1, &d2) // C <-- tmp1*2d = T1*2d*T2
|
||||
edwards25519.FeMul(&tmp1, &p1.Z, &p2.Z) // tmp1 <-- Z1*Z2
|
||||
edwards25519.FeAdd(&D, &tmp1, &tmp1) // D <-- tmp1 + tmp1 = 2*Z1*Z2
|
||||
edwards25519.FeSub(&E, &B, &A) // E <-- B-A
|
||||
edwards25519.FeSub(&F, &D, &C) // F <-- D-C
|
||||
edwards25519.FeAdd(&G, &D, &C) // G <-- D+C
|
||||
edwards25519.FeAdd(&H, &B, &A) // H <-- B+A
|
||||
edwards25519.FeMul(&out.X, &E, &F) // X3 <-- E*F
|
||||
edwards25519.FeMul(&out.Y, &G, &H) // Y3 <-- G*H
|
||||
edwards25519.FeMul(&out.T, &E, &H) // T3 <-- E*H
|
||||
edwards25519.FeMul(&out.Z, &F, &G) // Z3 <-- F*G
|
||||
}
|
||||
|
||||
// Double returns 2*(x,y). Because we are always converting from affine, we can
|
||||
// use "mdbl-2008-bbjlp" which assumes Z1=1. Reusing some intermediate
|
||||
// values, the cost is 3S + 2M + 9*add + 1*a.
|
||||
// Double returns 2*(x,y).
|
||||
func (curve ed25519Curve) Double(x1, y1 *big.Int) (x, y *big.Int) {
|
||||
var p, q edwards25519.ProjectiveGroupElement
|
||||
var t0, t1 edwards25519.FieldElement
|
||||
var p group.ProjectiveGroupElement
|
||||
|
||||
affineToProjective(&p, x1, y1)
|
||||
p.FromAffine(x1, y1)
|
||||
|
||||
// C = X1^2, D = Y1^2
|
||||
edwards25519.FeSquare(&t0, &p.X)
|
||||
edwards25519.FeSquare(&t1, &p.Y)
|
||||
edwards25519.FeMul(&p.Z, &p.X, &p.Y)
|
||||
|
||||
// B = (X1+Y1)^2 = X1^2 + Y1^2 + 2*(X*Y)
|
||||
edwards25519.FeAdd(&q.X, &t0, &t1)
|
||||
edwards25519.FeAdd(&q.X, &q.X, &p.Z)
|
||||
edwards25519.FeAdd(&q.X, &q.X, &p.Z)
|
||||
|
||||
// E = a*C where a = -1
|
||||
edwards25519.FeNeg(&q.Z, &t0)
|
||||
|
||||
// F = E + D
|
||||
edwards25519.FeAdd(&p.X, &q.Z, &t1)
|
||||
|
||||
// X3 = (B-C-D)*(F-2)
|
||||
edwards25519.FeSub(&p.Y, &q.X, &t0)
|
||||
edwards25519.FeSub(&p.Y, &p.Y, &t1)
|
||||
edwards25519.FeSub(&p.Z, &p.X, &feTwo)
|
||||
edwards25519.FeMul(&q.X, &p.Y, &p.Z)
|
||||
|
||||
// Y3 = F*(E-D)
|
||||
edwards25519.FeSub(&p.Y, &q.Z, &t1)
|
||||
edwards25519.FeMul(&q.Y, &p.X, &p.Y)
|
||||
|
||||
// Z3 = F^2 - 2*F
|
||||
edwards25519.FeSquare(&q.Z, &p.X)
|
||||
edwards25519.FeSub(&q.Z, &q.Z, &p.X)
|
||||
edwards25519.FeSub(&q.Z, &q.Z, &p.X)
|
||||
|
||||
return projectiveToAffine(&q)
|
||||
return p.Double().ToAffine()
|
||||
}
|
||||
|
||||
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
|
||||
|
@ -148,8 +100,7 @@ func (curve ed25519Curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int)
|
|||
return
|
||||
}
|
||||
|
||||
var r0, r1 edwards25519.ExtendedGroupElement
|
||||
var h edwards25519.CompletedGroupElement
|
||||
var r0, r1 group.ExtendedGroupElement
|
||||
var s [32]byte
|
||||
|
||||
curve.scalarFromBytes(&s, k)
|
||||
|
@ -157,7 +108,7 @@ func (curve ed25519Curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int)
|
|||
// Montgomery ladder init:
|
||||
// R_0 = O, R_1 = P
|
||||
r0.Zero()
|
||||
affineToExtended(&r1, x1, y1)
|
||||
r1.FromAffine(x1, y1)
|
||||
|
||||
// Montgomery ladder step:
|
||||
// R_{1-b} = R_{1-b} + R_{b}
|
||||
|
@ -165,17 +116,15 @@ func (curve ed25519Curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int)
|
|||
for i := 255; i >= 0; i-- {
|
||||
var b = int32((s[i/8] >> uint(i&7)) & 1)
|
||||
if b == 0 {
|
||||
extendedAdd(&r1, &r0, &r1)
|
||||
r0.Double(&h)
|
||||
h.ToExtended(&r0)
|
||||
r1.Add(&r0, &r1)
|
||||
r0.Double()
|
||||
} else {
|
||||
extendedAdd(&r0, &r0, &r1)
|
||||
r1.Double(&h)
|
||||
h.ToExtended(&r1)
|
||||
r0.Add(&r0, &r1)
|
||||
r1.Double()
|
||||
}
|
||||
}
|
||||
|
||||
return extendedToAffine(&r0)
|
||||
return r0.ToAffine()
|
||||
}
|
||||
|
||||
// scalarFromBytes converts a big-endian value to a fixed-size little-endian
|
||||
|
@ -194,84 +143,13 @@ func (curve ed25519Curve) scalarFromBytes(out *[32]byte, in []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
// ScalarBaseMult returns k*G, where G is the base point of the group and k is
|
||||
// an integer in big-endian form.
|
||||
func (curve ed25519Curve) ScalarBaseMult(k []byte) (x, y *big.Int) {
|
||||
var p edwards25519.ExtendedGroupElement
|
||||
var scBytes [32]byte
|
||||
// // ScalarBaseMult returns k*G, where G is the base point of the group and k is
|
||||
// // an integer in big-endian form.
|
||||
// func (curve ed25519Curve) ScalarBaseMult(k []byte) (x, y *big.Int) {
|
||||
// var p edwards25519.ExtendedGroupElement
|
||||
// var scBytes [32]byte
|
||||
|
||||
curve.scalarFromBytes(&scBytes, k)
|
||||
edwards25519.GeScalarMultBase(&p, &scBytes)
|
||||
return extendedToAffine(&p)
|
||||
}
|
||||
|
||||
// Converts (x,y) to (X:Y:T:Z) extended coordinates, or "P3" in ref10. As
|
||||
// described in "Twisted Edwards Curves Revisited", Hisil-Wong-Carter-Dawson
|
||||
// 2008, Section 3.1 (https://eprint.iacr.org/2008/522.pdf)
|
||||
// See also https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
||||
func affineToExtended(out *edwards25519.ExtendedGroupElement, x, y *big.Int) {
|
||||
feFromBig(&out.X, x)
|
||||
feFromBig(&out.Y, y)
|
||||
edwards25519.FeMul(&out.T, &out.X, &out.Y)
|
||||
edwards25519.FeOne(&out.Z)
|
||||
}
|
||||
|
||||
// Extended coordinates are XYZT with x = X/Z, y = Y/Z, or the "P3"
|
||||
// representation in ref10. Extended->affine is the same operation as moving
|
||||
// from projective to affine. Per HWCD, it is safe to move from extended to
|
||||
// projective by simply ignoring T.
|
||||
func extendedToAffine(in *edwards25519.ExtendedGroupElement) (*big.Int, *big.Int) {
|
||||
var x, y, zinv edwards25519.FieldElement
|
||||
var bigX, bigY = new(big.Int), new(big.Int)
|
||||
|
||||
edwards25519.FeInvert(&zinv, &in.Z)
|
||||
edwards25519.FeMul(&x, &in.X, &zinv)
|
||||
edwards25519.FeMul(&y, &in.Y, &zinv)
|
||||
|
||||
feToBig(bigX, &x)
|
||||
feToBig(bigY, &y)
|
||||
|
||||
return bigX, bigY
|
||||
}
|
||||
|
||||
// Projective coordinates are XYZ with x = X/Z, y = Y/Z, or the "P2" representation in ref10.
|
||||
func affineToProjective(out *edwards25519.ProjectiveGroupElement, x, y *big.Int) {
|
||||
feFromBig(&out.X, x)
|
||||
feFromBig(&out.Y, y)
|
||||
edwards25519.FeOne(&out.Z)
|
||||
}
|
||||
|
||||
func projectiveToAffine(in *edwards25519.ProjectiveGroupElement) (*big.Int, *big.Int) {
|
||||
var x, y, zinv edwards25519.FieldElement
|
||||
var bigX, bigY = new(big.Int), new(big.Int)
|
||||
|
||||
edwards25519.FeInvert(&zinv, &in.Z)
|
||||
edwards25519.FeMul(&x, &in.X, &zinv)
|
||||
edwards25519.FeMul(&y, &in.Y, &zinv)
|
||||
|
||||
feToBig(bigX, &x)
|
||||
feToBig(bigY, &y)
|
||||
|
||||
return bigX, bigY
|
||||
}
|
||||
|
||||
func feFromBig(h *edwards25519.FieldElement, in *big.Int) {
|
||||
tmp := new(big.Int).Mod(in, ed25519.P)
|
||||
tmpBytes := tmp.Bytes()
|
||||
var buf, reverse [32]byte
|
||||
copy(buf[32-len(tmpBytes):], tmpBytes)
|
||||
for i := 0; i < 32; i++ {
|
||||
reverse[i] = buf[31-i]
|
||||
}
|
||||
edwards25519.FeFromBytes(h, &reverse)
|
||||
}
|
||||
|
||||
func feToBig(out *big.Int, h *edwards25519.FieldElement) {
|
||||
var buf, reverse [32]byte
|
||||
edwards25519.FeToBytes(&buf, h)
|
||||
for i := 0; i < 32; i++ {
|
||||
reverse[i] = buf[31-i]
|
||||
}
|
||||
out.SetBytes(reverse[:])
|
||||
out.Mod(out, ed25519.P)
|
||||
}
|
||||
// curve.scalarFromBytes(&scBytes, k)
|
||||
// edwards25519.GeScalarMultBase(&p, &scBytes)
|
||||
// return extendedToAffine(&p)
|
||||
// }
|
||||
|
|
685
ed25519_test.go
685
ed25519_test.go
|
@ -5,93 +5,20 @@
|
|||
package ed25519
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/gtank/ed25519/internal/edwards25519"
|
||||
"github.com/gtank/ed25519/internal/radix51"
|
||||
field "github.com/gtank/ed25519/internal/radix51"
|
||||
)
|
||||
|
||||
// TEST MATH
|
||||
|
||||
// d is a constant from the curve equation. it's easy to find the value in multiple formats.
|
||||
var d_fe = edwards25519.FieldElement{
|
||||
-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116,
|
||||
}
|
||||
var d_bn, _ = new(big.Int).SetString("37095705934669439343138083508754565189542113879843219016388785533085940283555", 10)
|
||||
|
||||
// 2*d
|
||||
var d2_fe = edwards25519.FieldElement{
|
||||
-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199,
|
||||
}
|
||||
var d2_bn = new(big.Int).Mul(big.NewInt(2), d_bn)
|
||||
|
||||
// A is a constant from the Montgomery form of curve25519.
|
||||
var A_fe = edwards25519.FieldElement{
|
||||
486662, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
var A_bn, _ = new(big.Int).SetString("486662", 10)
|
||||
|
||||
var feConvTable = []struct {
|
||||
name string
|
||||
fe edwards25519.FieldElement
|
||||
bn *big.Int
|
||||
}{
|
||||
{
|
||||
name: "d",
|
||||
fe: d_fe,
|
||||
bn: d_bn,
|
||||
},
|
||||
{
|
||||
name: "d2",
|
||||
fe: d2_fe,
|
||||
bn: d2_bn,
|
||||
},
|
||||
{
|
||||
name: "A",
|
||||
fe: A_fe,
|
||||
bn: A_bn,
|
||||
},
|
||||
}
|
||||
|
||||
func TestFeFromBig(t *testing.T) {
|
||||
var _ = Ed25519().(ed25519Curve)
|
||||
var fe edwards25519.FieldElement
|
||||
for _, c := range feConvTable {
|
||||
feFromBig(&fe, c.bn)
|
||||
for i := 0; i < len(fe); i++ {
|
||||
if fe[i] != c.fe[i] {
|
||||
t.Error("bn->fe radix conversion failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFeToBig(t *testing.T) {
|
||||
var curve = Ed25519().(ed25519Curve)
|
||||
var bn, expectedBN = new(big.Int), new(big.Int)
|
||||
for _, c := range feConvTable {
|
||||
expectedBN.Mod(c.bn, curve.P)
|
||||
feToBig(bn, &c.fe)
|
||||
if bn.Cmp(expectedBN) != 0 {
|
||||
t.Errorf("fe->bn radix conversion failed: %s", c.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRadixRoundTrip(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var curve = Ed25519().(ed25519Curve)
|
||||
var fe edwards25519.FieldElement
|
||||
var fe field.FieldElement
|
||||
var out = new(big.Int)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
|
@ -99,8 +26,8 @@ func TestRadixRoundTrip(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
feFromBig(&fe, n)
|
||||
feToBig(out, &fe)
|
||||
field.FeFromBig(&fe, n)
|
||||
out = field.FeToBig(&fe)
|
||||
if n.Cmp(out) != 0 {
|
||||
t.Errorf("fe<>bn failed for %x", n.Bytes())
|
||||
}
|
||||
|
@ -119,6 +46,16 @@ func TestIsOnCurve(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsOnCurve(b *testing.B) {
|
||||
ed := Ed25519()
|
||||
Gx, Gy := ed.Params().Gx, ed.Params().Gy
|
||||
for i := 0; i < b.N; i++ {
|
||||
if !ed.IsOnCurve(Gx, Gy) {
|
||||
b.Error("ed25519 base point not on curve")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
c := Ed25519().(ed25519Curve)
|
||||
Bx, By := c.Params().Gx, c.Params().Gy
|
||||
|
@ -129,6 +66,14 @@ func TestAdd(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
c := Ed25519()
|
||||
Gx, Gy := c.Params().Gx, c.Params().Gy
|
||||
for i := 0; i < b.N; i++ {
|
||||
Gx, Gy = c.Add(Gx, Gy, Gx, Gy)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDouble(t *testing.T) {
|
||||
c := Ed25519()
|
||||
Gx, Gy := c.Params().Gx, c.Params().Gy
|
||||
|
@ -148,321 +93,303 @@ func TestDouble(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test vector generated by instrumenting x/crypto/ed25519 GenerateKey
|
||||
// seed: c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa
|
||||
// expanded: f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276
|
||||
// public: 65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd
|
||||
|
||||
func TestScalarMultBase(t *testing.T) {
|
||||
c := Ed25519()
|
||||
|
||||
// endian-swapped because x/crypto assumes this is always little endian from raw bytes
|
||||
// and scalarFromBytes assumes it's coming from big.Int Bytes()
|
||||
a, _ := hex.DecodeString("76e2ac4396a53598ff3a00c5bf6fd1bd49108ace4e21764cbb6309d8b95441f0")
|
||||
if len(a) != 32 {
|
||||
t.Errorf("failed decoding")
|
||||
}
|
||||
|
||||
Ax, Ay := c.ScalarBaseMult(a)
|
||||
|
||||
if !c.IsOnCurve(Ax, Ay) {
|
||||
t.Error("scalarmultbase result was off-curve")
|
||||
}
|
||||
|
||||
var A edwards25519.ExtendedGroupElement
|
||||
pub, _ := hex.DecodeString("65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd")
|
||||
var pubBytes [32]byte
|
||||
copy(pubBytes[:], pub)
|
||||
A.FromBytes(&pubBytes)
|
||||
Bx, By := extendedToAffine(&A)
|
||||
|
||||
if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
t.Error("scalarmultbase disagrees with x/crypto/ed25519")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultBaseIdentity(t *testing.T) {
|
||||
var c = Ed25519()
|
||||
var one = new(big.Int).Set(bigOne)
|
||||
Ax, Ay := c.ScalarBaseMult(one.Bytes())
|
||||
|
||||
if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 {
|
||||
t.Errorf("precomputed 1*B != B")
|
||||
}
|
||||
|
||||
Ax, Ay = c.ScalarMult(Ax, Ay, one.Bytes())
|
||||
|
||||
if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 {
|
||||
t.Errorf("arbitrary 1*B != B")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultBaseInfinity(t *testing.T) {
|
||||
c := Ed25519()
|
||||
a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
if len(a) != 32 {
|
||||
t.Errorf("failed decoding")
|
||||
}
|
||||
|
||||
Ax, Ay := c.ScalarBaseMult(a)
|
||||
|
||||
if !c.IsOnCurve(Ax, Ay) {
|
||||
t.Error("scalarmultbase result was off-curve")
|
||||
}
|
||||
|
||||
if Ax.Cmp(bigZero) != 0 || Ay.Cmp(bigOne) != 0 {
|
||||
t.Error("scalarmultbase by 0 was not point at infinity")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultsAgreeAtInfinity(t *testing.T) {
|
||||
c := Ed25519()
|
||||
a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
if len(a) != 32 {
|
||||
t.Errorf("failed decoding")
|
||||
}
|
||||
|
||||
Ax, Ay := c.ScalarBaseMult(a)
|
||||
Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a)
|
||||
|
||||
if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
t.Error("scalarmultbase disagrees with scalarmult")
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultsAgreeElsewhere(t *testing.T) {
|
||||
c := Ed25519()
|
||||
// head -c 32 /dev/urandom | sha256sum
|
||||
a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0")
|
||||
|
||||
Ax, Ay := c.ScalarBaseMult(a)
|
||||
Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a)
|
||||
|
||||
if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
t.Error("scalarmultbase disagrees with scalarmult")
|
||||
}
|
||||
|
||||
if !c.IsOnCurve(Bx, By) {
|
||||
t.Error("scalarmult is returning off-curve points")
|
||||
}
|
||||
}
|
||||
|
||||
// TEST INTERFACE
|
||||
|
||||
func TestMarshalingRoundTrip(t *testing.T) {
|
||||
ed := Ed25519()
|
||||
|
||||
a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0")
|
||||
Ax, Ay := ed.ScalarBaseMult(a)
|
||||
|
||||
if !ed.IsOnCurve(Ax, Ay) {
|
||||
t.Error("scalarBaseMult is returning off-curve points")
|
||||
}
|
||||
|
||||
sec1A := elliptic.Marshal(ed, Ax, Ay)
|
||||
Bx, By := elliptic.Unmarshal(ed, sec1A)
|
||||
|
||||
if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
t.Error("point did not survive elliptic.Marshal roundtrip")
|
||||
}
|
||||
|
||||
if !testing.Short() {
|
||||
for i := 0; i < 100; i++ {
|
||||
_, err := io.ReadFull(rand.Reader, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
Ax, Ay := ed.ScalarBaseMult(a)
|
||||
|
||||
if !ed.IsOnCurve(Ax, Ay) {
|
||||
t.Error("scalarBaseMult is returning off-curve points")
|
||||
}
|
||||
|
||||
sec1A := elliptic.Marshal(ed, Ax, Ay)
|
||||
Bx, By := elliptic.Unmarshal(ed, sec1A)
|
||||
|
||||
if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
t.Error("point did not survive elliptic.Marshal roundtrip")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TEST APPLICATION
|
||||
|
||||
func generateKey(r io.Reader) (sk *[32]byte, pk []byte, err error) {
|
||||
if r == nil {
|
||||
r = rand.Reader
|
||||
}
|
||||
|
||||
ed := Ed25519()
|
||||
|
||||
sk = new([32]byte)
|
||||
_, err = io.ReadFull(r, sk[:])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
digest := sha512.Sum512(sk[:])
|
||||
digest[0] &= 248
|
||||
digest[31] &= 127
|
||||
digest[31] |= 64
|
||||
|
||||
// take the first half of expanded bytes & reverse; big.Int expects big-endian
|
||||
reverseDigest := reverse32(digest[:32])
|
||||
scalar := new(big.Int).SetBytes(reverseDigest)
|
||||
|
||||
// A = a*B
|
||||
Ax, Ay := ed.ScalarBaseMult(scalar.Bytes())
|
||||
|
||||
// This works with the standard elliptic package marshaling
|
||||
publicKey := elliptic.Marshal(ed, Ax, Ay)
|
||||
|
||||
// but we can also render the compressed Edwards format
|
||||
compressedEdwardsY := make([]byte, 32)
|
||||
|
||||
// x, y here will be big-endian byte strings
|
||||
x, y := publicKey[1:33], publicKey[33:]
|
||||
|
||||
// RFC 8032: To form the encoding of the point, copy the least significant
|
||||
// bit of the x-coordinate to the most significant bit of the final octet.
|
||||
copy(compressedEdwardsY[:], y)
|
||||
compressedEdwardsY[0] |= x[31] << 7
|
||||
compressedEdwardsY = reverse32(compressedEdwardsY)
|
||||
|
||||
return sk, compressedEdwardsY, err
|
||||
}
|
||||
|
||||
func reverse32(b []byte) []byte {
|
||||
var tmp = make([]byte, 32)
|
||||
for i := 0; i <= 31; i++ {
|
||||
tmp[i] = b[31-i]
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
// Test vector generated by instrumenting x/crypto/ed25519 GenerateKey(). These
|
||||
// are raw values. The edwards code interprets them as little-endian, so they
|
||||
// need to be reversed before use with big.Int.
|
||||
var genKeyTest = struct {
|
||||
seed, expanded, public string
|
||||
}{
|
||||
seed: "c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa",
|
||||
expanded: "f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276",
|
||||
public: "65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd",
|
||||
}
|
||||
|
||||
func TestEdDSAGenerateKey(t *testing.T) {
|
||||
fakeRandom, _ := hex.DecodeString(genKeyTest.seed)
|
||||
fakeReader := bytes.NewBuffer(fakeRandom)
|
||||
|
||||
sk, pk, err := generateKey(fakeReader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedPK, _ := hex.DecodeString(genKeyTest.public)
|
||||
if !bytes.Equal(sk[:], fakeRandom) || !bytes.Equal(pk, expectedPK) {
|
||||
t.Error("generateKey output did not match test vector")
|
||||
}
|
||||
}
|
||||
|
||||
// BENCHMARKS
|
||||
|
||||
func BenchmarkIsOnCurve(b *testing.B) {
|
||||
ed := Ed25519()
|
||||
Gx, Gy := ed.Params().Gx, ed.Params().Gy
|
||||
for i := 0; i < b.N; i++ {
|
||||
if !ed.IsOnCurve(Gx, Gy) {
|
||||
b.Error("ed25519 base point not on curve")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
ed := Ed25519()
|
||||
Gx, Gy := ed.Params().Gx, ed.Params().Gy
|
||||
for i := 0; i < b.N; i++ {
|
||||
Gx, Gy = ed.Add(Gx, Gy, Gx, Gy)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDouble(b *testing.B) {
|
||||
ed := Ed25519()
|
||||
Gx, Gy := ed.Params().Gx, ed.Params().Gy
|
||||
c := Ed25519()
|
||||
Gx, Gy := c.Params().Gx, c.Params().Gy
|
||||
for i := 0; i < b.N; i++ {
|
||||
Gx, Gy = ed.Double(Gx, Gy)
|
||||
Gx, Gy = c.Double(Gx, Gy)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarBaseMult(b *testing.B) {
|
||||
ed := Ed25519()
|
||||
// // Test vector generated by instrumenting x/crypto/ed25519 GenerateKey
|
||||
// // seed: c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa
|
||||
// // expanded: f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276
|
||||
// // public: 65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd
|
||||
|
||||
var k [32]byte
|
||||
_, err := io.ReadFull(rand.Reader, k[:])
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
k[0] &= 248
|
||||
k[31] &= 127
|
||||
k[31] |= 64
|
||||
// func TestScalarMultBase(t *testing.T) {
|
||||
// c := Ed25519()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = ed.ScalarBaseMult(k[:])
|
||||
}
|
||||
}
|
||||
// // endian-swapped because x/crypto assumes this is always little endian from raw bytes
|
||||
// // and scalarFromBytes assumes it's coming from big.Int Bytes()
|
||||
// a, _ := hex.DecodeString("76e2ac4396a53598ff3a00c5bf6fd1bd49108ace4e21764cbb6309d8b95441f0")
|
||||
// if len(a) != 32 {
|
||||
// t.Errorf("failed decoding")
|
||||
// }
|
||||
|
||||
func BenchmarkScalarMult(b *testing.B) {
|
||||
ed := Ed25519()
|
||||
Bx, By := ed.Params().Gx, ed.Params().Gy
|
||||
// Ax, Ay := c.ScalarBaseMult(a)
|
||||
|
||||
var k [32]byte
|
||||
_, err := io.ReadFull(rand.Reader, k[:])
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
k[0] &= 248
|
||||
k[31] &= 127
|
||||
k[31] |= 64
|
||||
// if !c.IsOnCurve(Ax, Ay) {
|
||||
// t.Error("scalarmultbase result was off-curve")
|
||||
// }
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = ed.ScalarMult(Bx, By, k[:])
|
||||
}
|
||||
}
|
||||
// var A edwards25519.ExtendedGroupElement
|
||||
// pub, _ := hex.DecodeString("65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd")
|
||||
// var pubBytes [32]byte
|
||||
// copy(pubBytes[:], pub)
|
||||
// A.FromBytes(&pubBytes)
|
||||
// Bx, By := extendedToAffine(&A)
|
||||
|
||||
// A is a constant from the Montgomery form of curve25519.
|
||||
var radix25A = edwards25519.FieldElement{
|
||||
486662, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
// t.Error("scalarmultbase disagrees with x/crypto/ed25519")
|
||||
// }
|
||||
// }
|
||||
|
||||
var radix51A = radix51.FieldElement{
|
||||
486662, 0, 0, 0, 0,
|
||||
}
|
||||
// func TestScalarMultBaseIdentity(t *testing.T) {
|
||||
// var c = Ed25519()
|
||||
// var one = new(big.Int).Set(bigOne)
|
||||
// Ax, Ay := c.ScalarBaseMult(one.Bytes())
|
||||
|
||||
func BenchmarkFeMul25(b *testing.B) {
|
||||
var h edwards25519.FieldElement
|
||||
for i := 0; i < b.N; i++ {
|
||||
edwards25519.FeMul(&h, &radix25A, &radix25A)
|
||||
}
|
||||
}
|
||||
// if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 {
|
||||
// t.Errorf("precomputed 1*B != B")
|
||||
// }
|
||||
|
||||
func BenchmarkFeMul51(b *testing.B) {
|
||||
var h radix51.FieldElement
|
||||
for i := 0; i < b.N; i++ {
|
||||
radix51.FeMul(&h, &radix51A, &radix51A)
|
||||
}
|
||||
}
|
||||
// Ax, Ay = c.ScalarMult(Ax, Ay, one.Bytes())
|
||||
|
||||
func BenchmarkFeSquare25(b *testing.B) {
|
||||
var h edwards25519.FieldElement
|
||||
for i := 0; i < b.N; i++ {
|
||||
edwards25519.FeSquare(&h, &radix25A)
|
||||
}
|
||||
}
|
||||
// if Ax.Cmp(c.Params().Gx) != 0 || Ay.Cmp(c.Params().Gy) != 0 {
|
||||
// t.Errorf("arbitrary 1*B != B")
|
||||
// }
|
||||
// }
|
||||
|
||||
func BenchmarkFeSquare51(b *testing.B) {
|
||||
var h radix51.FieldElement
|
||||
for i := 0; i < b.N; i++ {
|
||||
radix51.FeSquare(&h, &radix51A)
|
||||
}
|
||||
}
|
||||
// func TestScalarMultBaseInfinity(t *testing.T) {
|
||||
// c := Ed25519()
|
||||
// a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
// if len(a) != 32 {
|
||||
// t.Errorf("failed decoding")
|
||||
// }
|
||||
|
||||
// Ax, Ay := c.ScalarBaseMult(a)
|
||||
|
||||
// if !c.IsOnCurve(Ax, Ay) {
|
||||
// t.Error("scalarmultbase result was off-curve")
|
||||
// }
|
||||
|
||||
// if Ax.Cmp(bigZero) != 0 || Ay.Cmp(bigOne) != 0 {
|
||||
// t.Error("scalarmultbase by 0 was not point at infinity")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestScalarMultsAgreeAtInfinity(t *testing.T) {
|
||||
// c := Ed25519()
|
||||
// a, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
// if len(a) != 32 {
|
||||
// t.Errorf("failed decoding")
|
||||
// }
|
||||
|
||||
// Ax, Ay := c.ScalarBaseMult(a)
|
||||
// Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a)
|
||||
|
||||
// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
// t.Error("scalarmultbase disagrees with scalarmult")
|
||||
// }
|
||||
// }
|
||||
|
||||
// func TestScalarMultsAgreeElsewhere(t *testing.T) {
|
||||
// c := Ed25519()
|
||||
// // head -c 32 /dev/urandom | sha256sum
|
||||
// a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0")
|
||||
|
||||
// Ax, Ay := c.ScalarBaseMult(a)
|
||||
// Bx, By := c.ScalarMult(c.Params().Gx, c.Params().Gy, a)
|
||||
|
||||
// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
// t.Error("scalarmultbase disagrees with scalarmult")
|
||||
// }
|
||||
|
||||
// if !c.IsOnCurve(Bx, By) {
|
||||
// t.Error("scalarmult is returning off-curve points")
|
||||
// }
|
||||
// }
|
||||
|
||||
// // TEST INTERFACE
|
||||
|
||||
// func TestMarshalingRoundTrip(t *testing.T) {
|
||||
// ed := Ed25519()
|
||||
|
||||
// a, _ := hex.DecodeString("c07eea55b3322f15099b6cf4d2b7e99d3d0fa6807f6fc7a46b5f7cb78daad4e0")
|
||||
// Ax, Ay := ed.ScalarBaseMult(a)
|
||||
|
||||
// if !ed.IsOnCurve(Ax, Ay) {
|
||||
// t.Error("scalarBaseMult is returning off-curve points")
|
||||
// }
|
||||
|
||||
// sec1A := elliptic.Marshal(ed, Ax, Ay)
|
||||
// Bx, By := elliptic.Unmarshal(ed, sec1A)
|
||||
|
||||
// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
// t.Error("point did not survive elliptic.Marshal roundtrip")
|
||||
// }
|
||||
|
||||
// if !testing.Short() {
|
||||
// for i := 0; i < 100; i++ {
|
||||
// _, err := io.ReadFull(rand.Reader, a)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// Ax, Ay := ed.ScalarBaseMult(a)
|
||||
|
||||
// if !ed.IsOnCurve(Ax, Ay) {
|
||||
// t.Error("scalarBaseMult is returning off-curve points")
|
||||
// }
|
||||
|
||||
// sec1A := elliptic.Marshal(ed, Ax, Ay)
|
||||
// Bx, By := elliptic.Unmarshal(ed, sec1A)
|
||||
|
||||
// if Ax.Cmp(Bx) != 0 || Ay.Cmp(By) != 0 {
|
||||
// t.Error("point did not survive elliptic.Marshal roundtrip")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // TEST APPLICATION
|
||||
|
||||
// func generateKey(r io.Reader) (sk *[32]byte, pk []byte, err error) {
|
||||
// if r == nil {
|
||||
// r = rand.Reader
|
||||
// }
|
||||
|
||||
// ed := Ed25519()
|
||||
|
||||
// sk = new([32]byte)
|
||||
// _, err = io.ReadFull(r, sk[:])
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
|
||||
// digest := sha512.Sum512(sk[:])
|
||||
// digest[0] &= 248
|
||||
// digest[31] &= 127
|
||||
// digest[31] |= 64
|
||||
|
||||
// // take the first half of expanded bytes & reverse; big.Int expects big-endian
|
||||
// reverseDigest := reverse32(digest[:32])
|
||||
// scalar := new(big.Int).SetBytes(reverseDigest)
|
||||
|
||||
// // A = a*B
|
||||
// Ax, Ay := ed.ScalarBaseMult(scalar.Bytes())
|
||||
|
||||
// // This works with the standard elliptic package marshaling
|
||||
// publicKey := elliptic.Marshal(ed, Ax, Ay)
|
||||
|
||||
// // but we can also render the compressed Edwards format
|
||||
// compressedEdwardsY := make([]byte, 32)
|
||||
|
||||
// // x, y here will be big-endian byte strings
|
||||
// x, y := publicKey[1:33], publicKey[33:]
|
||||
|
||||
// // RFC 8032: To form the encoding of the point, copy the least significant
|
||||
// // bit of the x-coordinate to the most significant bit of the final octet.
|
||||
// copy(compressedEdwardsY[:], y)
|
||||
// compressedEdwardsY[0] |= x[31] << 7
|
||||
// compressedEdwardsY = reverse32(compressedEdwardsY)
|
||||
|
||||
// return sk, compressedEdwardsY, err
|
||||
// }
|
||||
|
||||
// func reverse32(b []byte) []byte {
|
||||
// var tmp = make([]byte, 32)
|
||||
// for i := 0; i <= 31; i++ {
|
||||
// tmp[i] = b[31-i]
|
||||
// }
|
||||
// return tmp
|
||||
// }
|
||||
|
||||
// // Test vector generated by instrumenting x/crypto/ed25519 GenerateKey(). These
|
||||
// // are raw values. The edwards code interprets them as little-endian, so they
|
||||
// // need to be reversed before use with big.Int.
|
||||
// var genKeyTest = struct {
|
||||
// seed, expanded, public string
|
||||
// }{
|
||||
// seed: "c240344fcc6615dda52da98149377ad2b13fdba2bc39a50ba9f3afb2cbd4abaa",
|
||||
// expanded: "f04154b9d80963bb4c76214ece8a1049bdd16fbfc5003aff9835a59643ace276",
|
||||
// public: "65a8343a83ec15e55050f12fc22f2c81a4fe7327c8da1524441f9ce5e5bc27dd",
|
||||
// }
|
||||
|
||||
// func TestEdDSAGenerateKey(t *testing.T) {
|
||||
// fakeRandom, _ := hex.DecodeString(genKeyTest.seed)
|
||||
// fakeReader := bytes.NewBuffer(fakeRandom)
|
||||
|
||||
// sk, pk, err := generateKey(fakeReader)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// expectedPK, _ := hex.DecodeString(genKeyTest.public)
|
||||
// if !bytes.Equal(sk[:], fakeRandom) || !bytes.Equal(pk, expectedPK) {
|
||||
// t.Error("generateKey output did not match test vector")
|
||||
// }
|
||||
// }
|
||||
|
||||
// // BENCHMARKS
|
||||
|
||||
// func BenchmarkScalarBaseMult(b *testing.B) {
|
||||
// ed := Ed25519()
|
||||
|
||||
// var k [32]byte
|
||||
// _, err := io.ReadFull(rand.Reader, k[:])
|
||||
// if err != nil {
|
||||
// b.Fatal(err)
|
||||
// }
|
||||
// k[0] &= 248
|
||||
// k[31] &= 127
|
||||
// k[31] |= 64
|
||||
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// _, _ = ed.ScalarBaseMult(k[:])
|
||||
// }
|
||||
// }
|
||||
|
||||
// func BenchmarkScalarMult(b *testing.B) {
|
||||
// ed := Ed25519()
|
||||
// Bx, By := ed.Params().Gx, ed.Params().Gy
|
||||
|
||||
// var k [32]byte
|
||||
// _, err := io.ReadFull(rand.Reader, k[:])
|
||||
// if err != nil {
|
||||
// b.Fatal(err)
|
||||
// }
|
||||
// k[0] &= 248
|
||||
// k[31] &= 127
|
||||
// k[31] |= 64
|
||||
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// _, _ = ed.ScalarMult(Bx, By, k[:])
|
||||
// }
|
||||
// }
|
||||
|
||||
// // A is a constant from the Montgomery form of curve25519.
|
||||
// var radix25A = edwards25519.FieldElement{
|
||||
// 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// }
|
||||
|
||||
// var radix51A = radix51.FieldElement{
|
||||
// 486662, 0, 0, 0, 0,
|
||||
// }
|
||||
|
||||
// func BenchmarkFeMul25(b *testing.B) {
|
||||
// var h edwards25519.FieldElement
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// edwards25519.FeMul(&h, &radix25A, &radix25A)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func BenchmarkFeMul51(b *testing.B) {
|
||||
// var h radix51.FieldElement
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// radix51.FeMul(&h, &radix51A, &radix51A)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func BenchmarkFeSquare25(b *testing.B) {
|
||||
// var h edwards25519.FieldElement
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// edwards25519.FeSquare(&h, &radix25A)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func BenchmarkFeSquare51(b *testing.B) {
|
||||
// var h radix51.FieldElement
|
||||
// for i := 0; i < b.N; i++ {
|
||||
// radix51.FeSquare(&h, &radix51A)
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
// Implements group logic for the Ed25519 curve.
|
||||
|
||||
package group
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
field "github.com/gtank/ed25519/internal/radix51"
|
||||
)
|
||||
|
||||
// From EFD https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
|
||||
// An elliptic curve in twisted Edwards form has parameters a, d and coordinates
|
||||
// x, y satisfying the following equations:
|
||||
//
|
||||
// a * x^2 + y^2 = 1 + d * x^2 * y^2
|
||||
//
|
||||
// Extended coordinates assume a = -1 and represent x, y as (X, Y, Z, T)
|
||||
// satisfying the following equations:
|
||||
//
|
||||
// x = X / Z
|
||||
// y = Y / Z
|
||||
// x * y = T / Z
|
||||
//
|
||||
// This representation was introduced in the HisilWongCarterDawson paper "Twisted
|
||||
// Edwards curves revisited" (Asiacrypt 2008).
|
||||
type ExtendedGroupElement struct {
|
||||
X, Y, Z, T field.FieldElement
|
||||
}
|
||||
|
||||
// Converts (x,y) to (X:Y:T:Z) extended coordinates, or "P3" in ref10. As
|
||||
// described in "Twisted Edwards Curves Revisited", Hisil-Wong-Carter-Dawson
|
||||
// 2008, Section 3.1 (https://eprint.iacr.org/2008/522.pdf)
|
||||
// See also https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
||||
func (v *ExtendedGroupElement) FromAffine(x, y *big.Int) {
|
||||
field.FeFromBig(&v.X, x)
|
||||
field.FeFromBig(&v.Y, y)
|
||||
field.FeMul(&v.T, &v.X, &v.Y)
|
||||
field.FeOne(&v.Z)
|
||||
}
|
||||
|
||||
// Extended coordinates are XYZT with x = X/Z, y = Y/Z, or the "P3"
|
||||
// representation in ref10. Extended->affine is the same operation as moving
|
||||
// from projective to affine. Per HWCD, it is safe to move from extended to
|
||||
// projective by simply ignoring T.
|
||||
func (v *ExtendedGroupElement) ToAffine() (*big.Int, *big.Int) {
|
||||
var x, y, zinv field.FieldElement
|
||||
|
||||
field.FeInvert(&zinv, &v.Z)
|
||||
field.FeMul(&x, &v.X, &zinv)
|
||||
field.FeMul(&y, &v.Y, &zinv)
|
||||
|
||||
return field.FeToBig(&x), field.FeToBig(&y)
|
||||
}
|
||||
|
||||
// Per HWCD, it is safe to move from extended to projective by simply ignoring T.
|
||||
func (v *ExtendedGroupElement) ToProjective() *ProjectiveGroupElement {
|
||||
var p ProjectiveGroupElement
|
||||
|
||||
field.FeCopy(&p.X, &v.X)
|
||||
field.FeCopy(&p.Y, &v.Y)
|
||||
field.FeCopy(&p.Z, &v.Z)
|
||||
|
||||
return &p
|
||||
}
|
||||
|
||||
func (v *ExtendedGroupElement) Zero() *ExtendedGroupElement {
|
||||
field.FeZero(&v.X)
|
||||
field.FeOne(&v.Y)
|
||||
field.FeOne(&v.Z)
|
||||
field.FeZero(&v.T)
|
||||
return v
|
||||
}
|
||||
|
||||
// This is the same addition formula everyone uses, "add-2008-hwcd-3".
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
||||
// TODO We know Z1=1 and Z2=1 here, so mmadd-2008-hwcd-3 (6M + 1S + 1*k + 9add) could apply
|
||||
func (v *ExtendedGroupElement) Add(p1, p2 *ExtendedGroupElement) *ExtendedGroupElement {
|
||||
var tmp1, tmp2, A, B, C, D, E, F, G, H field.FieldElement
|
||||
field.FeSub(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1-X1
|
||||
field.FeSub(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2-X2
|
||||
field.FeMul(&A, &tmp1, &tmp2) // A <-- tmp1*tmp2 = (Y1-X1)*(Y2-X2)
|
||||
field.FeAdd(&tmp1, &p1.Y, &p1.X) // tmp1 <-- Y1+X1
|
||||
field.FeAdd(&tmp2, &p2.Y, &p2.X) // tmp2 <-- Y2+X2
|
||||
field.FeMul(&B, &tmp1, &tmp2) // B <-- tmp1*tmp2 = (Y1+X1)*(Y2+X2)
|
||||
field.FeMul(&tmp1, &p1.T, &p2.T) // tmp1 <-- T1*T2
|
||||
field.FeMul(&C, &tmp1, &field.D2) // C <-- tmp1*2d = T1*2d*T2
|
||||
field.FeMul(&tmp1, &p1.Z, &p2.Z) // tmp1 <-- Z1*Z2
|
||||
field.FeAdd(&D, &tmp1, &tmp1) // D <-- tmp1 + tmp1 = 2*Z1*Z2
|
||||
field.FeSub(&E, &B, &A) // E <-- B-A
|
||||
field.FeSub(&F, &D, &C) // F <-- D-C
|
||||
field.FeAdd(&G, &D, &C) // G <-- D+C
|
||||
field.FeAdd(&H, &B, &A) // H <-- B+A
|
||||
field.FeMul(&v.X, &E, &F) // X3 <-- E*F
|
||||
field.FeMul(&v.Y, &G, &H) // Y3 <-- G*H
|
||||
field.FeMul(&v.T, &E, &H) // T3 <-- E*H
|
||||
field.FeMul(&v.Z, &F, &G) // Z3 <-- F*G
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *ExtendedGroupElement) Double() *ExtendedGroupElement {
|
||||
var p ProjectiveGroupElement = v.ToProjective()
|
||||
return p.Double().ToExtended()
|
||||
}
|
||||
|
||||
// Projective coordinates are XYZ with x = X/Z, y = Y/Z, or the "P2"
|
||||
// representation in ref10. This representation has a cheaper doubling formula
|
||||
// than extended coordinates.
|
||||
type ProjectiveGroupElement struct {
|
||||
X, Y, Z field.FieldElement
|
||||
}
|
||||
|
||||
func (v *ProjectiveGroupElement) FromAffine(x, y *big.Int) {
|
||||
field.FeFromBig(&v.X, x)
|
||||
field.FeFromBig(&v.Y, y)
|
||||
field.FeOne(&v.Z)
|
||||
}
|
||||
|
||||
func (v *ProjectiveGroupElement) ToAffine() (*big.Int, *big.Int) {
|
||||
var x, y, zinv field.FieldElement
|
||||
|
||||
field.FeInvert(&zinv, &v.Z)
|
||||
field.FeMul(&x, &v.X, &zinv)
|
||||
field.FeMul(&y, &v.Y, &zinv)
|
||||
|
||||
return field.FeToBig(&x), field.FeToBig(&y)
|
||||
}
|
||||
|
||||
func (v *ProjectiveGroupElement) ToExtended() *ExtendedGroupElement {
|
||||
var r ExtendedGroupElement
|
||||
var zinv field.FieldElement
|
||||
|
||||
field.FeCopy(&r.X, &v.X)
|
||||
field.FeCopy(&r.Y, &v.Y)
|
||||
field.FeCopy(&r.Z, &v.Z)
|
||||
|
||||
field.FeInvert(&zinv, &v.Z)
|
||||
field.FeMul(&r.T, &v.X, &v.Y) // XY = ZT
|
||||
field.FeMul(&r.T, &r.T, &zinv) // T = ZT/Z
|
||||
|
||||
return &r
|
||||
}
|
||||
|
||||
func (v *ProjectiveGroupElement) Zero() *ProjectiveGroupElement {
|
||||
field.FeZero(&v.X)
|
||||
field.FeOne(&v.Y)
|
||||
field.FeOne(&v.Z)
|
||||
return v
|
||||
}
|
||||
|
||||
// Because we are often converting from affine, we can use "mdbl-2008-bbjlp"
|
||||
// which assumes Z1=1. We also assume a = -1.
|
||||
//
|
||||
// Assumptions: Z1 = 1.
|
||||
// Cost: 2M + 4S + 1*a + 7add + 1*2.
|
||||
// Source: 2008 BernsteinBirknerJoyeLangePeters
|
||||
// http://eprint.iacr.org/2008/013, plus Z1=1, plus standard simplification.
|
||||
// Explicit formulas:
|
||||
//
|
||||
// B = (X1+Y1)^2
|
||||
// C = X1^2
|
||||
// D = Y1^2
|
||||
// E = a*C
|
||||
// F = E+D
|
||||
// X3 = (B-C-D)*(F-2)
|
||||
// Y3 = F*(E-D)
|
||||
// Z3 = F^2-2*F
|
||||
//
|
||||
// This assumption is one reason why this package is internal. For instance, it
|
||||
// will not hold during a Montgomery ladder using extended coordinates.
|
||||
// TODO: Hand off or switch entirely to dbl-2008-bbjlp when package is public.
|
||||
func (v *ProjectiveGroupElement) DoubleZ1() *ProjectiveGroupElement {
|
||||
var p, q ProjectiveGroupElement
|
||||
var t0, t1 field.FieldElement
|
||||
|
||||
p = *v
|
||||
|
||||
// C = X1^2, D = Y1^2
|
||||
field.FeSquare(&t0, &p.X)
|
||||
field.FeSquare(&t1, &p.Y)
|
||||
field.FeMul(&p.Z, &p.X, &p.Y)
|
||||
|
||||
// B = (X1+Y1)^2 = X1^2 + Y1^2 + 2*(X*Y)
|
||||
field.FeAdd(&q.X, &t0, &t1)
|
||||
field.FeAdd(&q.X, &q.X, &p.Z)
|
||||
field.FeAdd(&q.X, &q.X, &p.Z)
|
||||
|
||||
// E = a*C where a = -1
|
||||
field.FeNeg(&q.Z, &t0)
|
||||
|
||||
// F = E + D
|
||||
field.FeAdd(&p.X, &q.Z, &t1)
|
||||
|
||||
// X3 = (B-C-D)*(F-2)
|
||||
field.FeSub(&p.Y, &q.X, &t0)
|
||||
field.FeSub(&p.Y, &p.Y, &t1)
|
||||
field.FeSub(&p.Z, &p.X, &field.FieldTwo)
|
||||
field.FeMul(&q.X, &p.Y, &p.Z)
|
||||
|
||||
// Y3 = F*(E-D)
|
||||
field.FeSub(&p.Y, &q.Z, &t1)
|
||||
field.FeMul(&q.Y, &p.X, &p.Y)
|
||||
|
||||
// Z3 = F^2 - 2*F
|
||||
field.FeSquare(&q.Z, &p.X)
|
||||
field.FeSub(&q.Z, &q.Z, &p.X)
|
||||
field.FeSub(&q.Z, &q.Z, &p.X)
|
||||
|
||||
return &q
|
||||
}
|
|
@ -11,4 +11,8 @@ const (
|
|||
var (
|
||||
FieldZero FieldElement = [5]uint64{0, 0, 0, 0, 0}
|
||||
FieldOne FieldElement = [5]uint64{1, 0, 0, 0, 0}
|
||||
FieldTwo FieldElement = [5]uint64{2, 0, 0, 0, 0}
|
||||
|
||||
// 2*d, used in addition formula
|
||||
D2 FieldElement = [5]uint64{1859910466990425, 932731440258426, 1072319116312658, 1815898335770999, 633789495995903}
|
||||
)
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
// public domain amd64-51-30k version of ed25519 from SUPERCOP.
|
||||
package radix51
|
||||
|
||||
import "math/big"
|
||||
|
||||
// FieldElement represents an element of the field GF(2^255-19). An element t
|
||||
// represents the integer t[0] + t[1]*2^51 + t[2]*2^102 + t[3]*2^153 +
|
||||
// t[4]*2^204.
|
||||
type FieldElement [5]uint64
|
||||
|
||||
func (v *FieldElement) FeZero() {
|
||||
func FeZero(v *FieldElement) {
|
||||
v[0] = 0
|
||||
v[1] = 0
|
||||
v[2] = 0
|
||||
|
@ -17,7 +19,7 @@ func (v *FieldElement) FeZero() {
|
|||
v[4] = 0
|
||||
}
|
||||
|
||||
func (v *FieldElement) FeOne() {
|
||||
func FeOne(v *FieldElement) {
|
||||
v[0] = 1
|
||||
v[1] = 0
|
||||
v[2] = 0
|
||||
|
@ -26,7 +28,7 @@ func (v *FieldElement) FeOne() {
|
|||
}
|
||||
|
||||
// SetInt sets the receiving FieldElement to the specified small integer.
|
||||
func (v *FieldElement) SetInt(x uint64) {
|
||||
func SetInt(v *FieldElement, x uint64) {
|
||||
v[0] = x
|
||||
v[1] = 0
|
||||
v[2] = 0
|
||||
|
@ -116,7 +118,7 @@ func FeSub(out, a, b *FieldElement) {
|
|||
// FeNeg sets out = -a
|
||||
func FeNeg(out, a *FieldElement) {
|
||||
var t FieldElement
|
||||
t.SetInt(0)
|
||||
FeZero(&t)
|
||||
FeSub(out, &t, a)
|
||||
}
|
||||
|
||||
|
@ -210,12 +212,17 @@ func FeCSwap(a, b *FieldElement, c uint64) {
|
|||
b[4] ^= t[4]
|
||||
}
|
||||
|
||||
func FeEqual(a, b *FieldElement) uint64 {
|
||||
// Returns true if two field elements are equal.
|
||||
func FeEqual(a, b *FieldElement) bool {
|
||||
var result uint64
|
||||
for i := 0; i < 5; i++ {
|
||||
result |= a[i] ^ b[i]
|
||||
}
|
||||
return (result ^ 0)
|
||||
return result == 0
|
||||
}
|
||||
|
||||
func FeCopy(out, in *FieldElement) {
|
||||
copy(out[:], in[:])
|
||||
}
|
||||
|
||||
func FeFromBytes(v *FieldElement, x *[32]byte) {
|
||||
|
@ -306,3 +313,24 @@ func FeToBytes(r *[32]byte, v *FieldElement) {
|
|||
r[30] = byte((t[4] >> 36) & 0xff)
|
||||
r[31] = byte((t[4] >> 44))
|
||||
}
|
||||
|
||||
func FeFromBig(h *FieldElement, in *big.Int) {
|
||||
tmpBytes := in.Bytes()
|
||||
var buf, reverse [32]byte
|
||||
copy(buf[32-len(tmpBytes):], tmpBytes)
|
||||
for i := 0; i < 32; i++ {
|
||||
reverse[i] = buf[31-i]
|
||||
}
|
||||
FeFromBytes(h, &reverse)
|
||||
}
|
||||
|
||||
func FeToBig(h *FieldElement) *big.Int {
|
||||
var buf, reverse [32]byte
|
||||
FeToBytes(&buf, h) // does inline reduction
|
||||
for i := 0; i < 32; i++ {
|
||||
reverse[i] = buf[31-i]
|
||||
}
|
||||
out := new(big.Int)
|
||||
out.SetBytes(reverse[:])
|
||||
return out
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue