mirror of https://github.com/gtank/ristretto255
Implement an elliptic.Curve for ed25519
This commit is contained in:
commit
19d7f4d83d
|
@ -0,0 +1,22 @@
|
|||
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)
|
||||
)
|
|
@ -0,0 +1,242 @@
|
|||
// Implements an elliptic.Curve interface over the ed25519 curve.
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"github.com/gtank/ed25519/internal/edwards25519"
|
||||
)
|
||||
|
||||
type ed25519Curve struct {
|
||||
*elliptic.CurveParams
|
||||
}
|
||||
|
||||
var once sync.Once
|
||||
var ed25519Params = &elliptic.CurveParams{Name: "ed25519"}
|
||||
var ed25519 = ed25519Curve{ed25519Params}
|
||||
|
||||
// Ed25519 uses a twisted Edwards curve -x^2 + y^2 = 1 + dx^2y^2 with the following params:
|
||||
// The field prime is 2^255 - 19.
|
||||
// The order of the base point is 2^252 + 27742317777372353535851937790883648493.
|
||||
// And since B is irrelevant here, we're going to pretend that B is d = -(121665/121666).
|
||||
func initEd25519Params() {
|
||||
ed25519Params.P, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", 16)
|
||||
ed25519Params.N, _ = new(big.Int).SetString("7237005577332262213973186563042994240857116359379907606001950938285454250989", 10)
|
||||
ed25519Params.B, _ = new(big.Int).SetString("37095705934669439343138083508754565189542113879843219016388785533085940283555", 10)
|
||||
ed25519Params.Gx, _ = new(big.Int).SetString("15112221349535400772501151409588531511454012693041857206046113283949847762202", 10)
|
||||
ed25519Params.Gy, _ = new(big.Int).SetString("46316835694926478169428394003475163141307993866256225615783033603165251855960", 10)
|
||||
ed25519Params.BitSize = 256
|
||||
}
|
||||
|
||||
func Ed25519() elliptic.Curve {
|
||||
once.Do(initEd25519Params)
|
||||
return ed25519
|
||||
}
|
||||
|
||||
// Params returns the parameters for the curve.
|
||||
func (curve ed25519Curve) Params() *elliptic.CurveParams {
|
||||
return curve.CurveParams
|
||||
}
|
||||
|
||||
// IsOnCurve reports whether the given (x,y) lies on the curve by checking that
|
||||
// -x^2 + y^2 - 1 - dx^2y^2 = 0.
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
affineToExtended(&p1, x1, y1)
|
||||
affineToExtended(&p2, x2, y2)
|
||||
extendedAdd(&p3, &p1, &p2)
|
||||
|
||||
return extendedToAffine(&p3) // 1I + 2M
|
||||
}
|
||||
|
||||
// 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).
|
||||
// TODO: cheaper to reimplement? the typed path is aff->proj->completed->proj->aff
|
||||
func (curve ed25519Curve) Double(x1, y1 *big.Int) (x, y *big.Int) {
|
||||
var p edwards25519.ProjectiveGroupElement
|
||||
var r edwards25519.CompletedGroupElement
|
||||
affineToProjective(&p, x1, y1)
|
||||
p.Double(&r)
|
||||
r.ToProjective(&p) // 3M
|
||||
return projectiveToAffine(&p) // 1I + 2M
|
||||
}
|
||||
|
||||
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
|
||||
func (curve ed25519Curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) {
|
||||
// if either coordinate is nil, return the point at infinity
|
||||
if x1 == nil || y1 == nil {
|
||||
x = new(big.Int).Set(bigZero)
|
||||
y = new(big.Int).Set(bigOne)
|
||||
return
|
||||
}
|
||||
|
||||
var r0, r1 edwards25519.ExtendedGroupElement
|
||||
var h edwards25519.CompletedGroupElement
|
||||
var s [32]byte
|
||||
|
||||
curve.scalarFromBytes(&s, k)
|
||||
|
||||
// Montgomery ladder init:
|
||||
// R_0 = O, R_1 = P
|
||||
r0.Zero()
|
||||
affineToExtended(&r1, x1, y1)
|
||||
|
||||
// Montgomery ladder step:
|
||||
// R_{1-b} = R_{1-b} + R_{b}
|
||||
// R_{b} = 2*R_{b}
|
||||
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)
|
||||
} else {
|
||||
extendedAdd(&r0, &r0, &r1)
|
||||
r1.Double(&h)
|
||||
h.ToExtended(&r1)
|
||||
}
|
||||
}
|
||||
|
||||
return extendedToAffine(&r0)
|
||||
}
|
||||
|
||||
// scalarFromBytes converts a big-endian value to a fixed-size little-endian
|
||||
// representation. If the value is larger than the scalar group order, we
|
||||
// reduce it before returning.
|
||||
func (curve ed25519Curve) scalarFromBytes(out *[32]byte, in []byte) {
|
||||
scalar := new(big.Int).SetBytes(in)
|
||||
if scalar.Cmp(curve.N) >= 0 {
|
||||
scalar.Mod(scalar, curve.N)
|
||||
}
|
||||
buf := make([]byte, 32)
|
||||
scBytes := scalar.Bytes()
|
||||
copy(buf[32-len(scBytes):], scBytes)
|
||||
for i := 0; i < 32; i++ {
|
||||
out[i] = buf[31-i]
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
package ed25519
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/gtank/ed25519/internal/edwards25519"
|
||||
)
|
||||
|
||||
// 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 out = new(big.Int)
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
n, err := rand.Int(rand.Reader, curve.P)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
feFromBig(&fe, n)
|
||||
feToBig(out, &fe)
|
||||
if n.Cmp(out) != 0 {
|
||||
t.Errorf("fe<>bn failed for %x", n.Bytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsOnCurve(t *testing.T) {
|
||||
ed := Ed25519()
|
||||
if !ed.IsOnCurve(ed.Params().Gx, ed.Params().Gy) {
|
||||
t.Error("ed25519 base point not on curve")
|
||||
}
|
||||
|
||||
x, y := new(big.Int), new(big.Int)
|
||||
if ed.IsOnCurve(x, y) {
|
||||
t.Error("(0,0) is not on the curve")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
c := Ed25519().(ed25519Curve)
|
||||
Bx, By := c.Params().Gx, c.Params().Gy
|
||||
B2x, B2y := c.Add(Bx, By, Bx, By)
|
||||
|
||||
if !c.IsOnCurve(B2x, B2y) {
|
||||
t.Error("B+B is not on the curve")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDouble(t *testing.T) {
|
||||
c := Ed25519().(ed25519Curve)
|
||||
Gx, Gy := c.Params().Gx, c.Params().Gy
|
||||
G2x, G2y := c.Double(Gx, Gy)
|
||||
|
||||
Ax, Ay := c.Add(Gx, Gy, Gx, Gy)
|
||||
|
||||
if Ax.Cmp(G2x) != 0 || Ay.Cmp(G2y) != 0 {
|
||||
t.Errorf("double(B) != B+B")
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
for i := 0; i < b.N; i++ {
|
||||
Gx, Gy = ed.Double(Gx, Gy)
|
||||
}
|
||||
}
|
||||
|
||||
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[:])
|
||||
}
|
||||
}
|
|
@ -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.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue