group logic WIP

This commit is contained in:
George Tankersley 2017-06-26 02:12:45 -04:00
parent 7ae6271616
commit 4d11c7b5a6
6 changed files with 603 additions and 572 deletions

View File

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

View File

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

View File

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

209
internal/group/ge.go Normal file
View File

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

View File

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

View File

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