//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Source: https://sources.truenas.cloud/code // Import: sources.truenas.cloud/code/edwards25519 // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2019 The Go Authors. All rights Reserved. // Use of this source code is goverened by a BSD-style // license that can be found in the LICENSE file. // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// package edwards25519 import ( "crypto/rand" "encoding/hex" "testing" "testing/quick" ) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TestBytesMontgomery tests the SetBytesWithClamping+BytesMontgomery path // equivalence to curve25519.X25519 for basepoint scalar multiplications. // // Note that you can't actually implement X25519 with this package because // there is no SetBytesMontgomery, and it would not be possible to implement // it properly: points on the twist would get rejected, and the Scalar returned // by SetBytesWithClamping does not preserve its cofactor-clearing properties. // // Disabled to avoid the golang.org/x/crypto module dependency. /* func TestBytesMontgomery(t *testing.T) { f := func(scalar [32]byte) bool { s := NewScalar().SetBytesWithClamping(scalar[:]) p := (&Point{}).ScalarBaseMult(s) got := p.BytesMontgomery() want, _ := curve25519.X25519(scalar[:], curve25519.Basepoint) return bytes.Equal(got, want) } if err := quick.Check(f, nil); err != nil { t.Error(err) } } */ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestBytesMontgomerySodium(t *testing.T) { // Generated with libsodium.js 1.0.18 // crypto_sign_keypair().publicKey publicKey := "3bf918ffc2c955dc895bf145f566fb96623c1cadbe040091175764b5fde322c0" p, err := (&Point{}).SetBytes(decodeHex(publicKey)) if err != nil { t.Fatal(err) } // crypto_sign_ed25519_pk_to_curve25519(publicKey) want := "efc6c9d0738e9ea18d738ad4a2653631558931b0f1fde4dd58c436d19686dc28" if got := hex.EncodeToString(p.BytesMontgomery()); got != want { t.Errorf("got %q, want %q", got, want) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestBytesMontgomeryInfinity(t *testing.T) { p := NewIdentityPoint() want := "0000000000000000000000000000000000000000000000000000000000000000" if got := hex.EncodeToString(p.BytesMontgomery()); got != want { t.Errorf("got %q, want %q", got, want) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestMultByCofactor(t *testing.T) { lowOrderBytes := "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85" lowOrder, err := (&Point{}).SetBytes(decodeHex(lowOrderBytes)) if err != nil { t.Fatal(err) } if p := (&Point{}).MultByCofactor(lowOrder); p.Equal(NewIdentityPoint()) != 1 { t.Errorf("expected low order point * cofactor to be the identity") } f := func(scalar [64]byte) bool { s, _ := NewScalar().SetUniformBytes(scalar[:]) p := (&Point{}).ScalarBaseMult(s) p8 := (&Point{}).MultByCofactor(p) checkOnCurve(t, p8) // 8 * p == (8 * s) * B reprEight := [32]byte{8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} scEight, _ := (&Scalar{}).SetCanonicalBytes(reprEight[:]) s.Multiply(s, scEight) pp := (&Point{}).ScalarBaseMult(s) if p8.Equal(pp) != 1 { return false } // 8 * p == 8 * (lowOrder + p) pp.Add(p, lowOrder) pp.MultByCofactor(pp) if p8.Equal(pp) != 1 { return false } // 8 * p == p + p + p + p + p + p + p + p pp.Set(NewIdentityPoint()) for i := 0; i < 8; i++ { pp.Add(pp, p) } return p8.Equal(pp) == 1 } if err := quick.Check(f, nil); err != nil { t.Error(err) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestScalarInvert(t *testing.T) { invertWorks := func(xInv Scalar, x notZeroScalar) bool { xInv.Invert((*Scalar)(&x)) var check Scalar check.Multiply((*Scalar)(&x), &xInv) return check.Equal(scOne) == 1 && isReduced(xInv.Bytes()) } if err := quick.Check(invertWorks, quickCheckConfig(32)); err != nil { t.Error(err) } randomScalar := *dalekScalar randomInverse := NewScalar().Invert(&randomScalar) var check Scalar check.Multiply(&randomScalar, randomInverse) if check.Equal(scOne) == 0 || !isReduced(randomInverse.Bytes()) { t.Error("inversion did not work") } zero := NewScalar() if xx := NewScalar().Invert(zero); xx.Equal(zero) != 1 { t.Errorf("inverting zero did not return zero") } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestMultiScalarMultMatchesBaseMult(t *testing.T) { multiScalarMultMatchesBaseMult := func(x, y, z Scalar) bool { var p, q1, q2, q3, check Point p.MultiScalarMult([]*Scalar{&x, &y, &z}, []*Point{B, B, B}) q1.ScalarBaseMult(&x) q2.ScalarBaseMult(&y) q3.ScalarBaseMult(&z) check.Add(&q1, &q2).Add(&check, &q3) checkOnCurve(t, &p, &check, &q1, &q2, &q3) return p.Equal(&check) == 1 } if err := quick.Check(multiScalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil { t.Error(err) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestMultiScalarMultZeroReceiver(t *testing.T) { // A zero-value (uninitialized) receiver should be handled correctly, // producing a valid point on the curve. var p Point p.MultiScalarMult([]*Scalar{dalekScalar}, []*Point{B}) var check Point check.ScalarBaseMult(dalekScalar) checkOnCurve(t, &p, &check) if p.Equal(&check) != 1 { t.Error("MultiScalarMult with zero-value receiver did not match ScalarBaseMult") } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestMultiScalarMultReceiverAliasing(t *testing.T) { // The receiver v aliasing one of the input points should produce // the correct result. p := NewGeneratorPoint() p.MultiScalarMult([]*Scalar{dalekScalar}, []*Point{p}) var check Point check.ScalarBaseMult(dalekScalar) checkOnCurve(t, p, &check) if p.Equal(&check) != 1 { t.Error("MultiScalarMult with aliased receiver did not match ScalarBaseMult") } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestVarTimeMultiScalarMultMatchesBaseMult(t *testing.T) { varTimeMultiScalarMultMatchesBaseMult := func(x, y, z Scalar) bool { var p, q1, q2, q3, check Point p.VarTimeMultiScalarMult([]*Scalar{&x, &y, &z}, []*Point{B, B, B}) q1.ScalarBaseMult(&x) q2.ScalarBaseMult(&y) q3.ScalarBaseMult(&z) check.Add(&q1, &q2).Add(&check, &q3) checkOnCurve(t, &p, &check, &q1, &q2, &q3) return p.Equal(&check) == 1 } if err := quick.Check(varTimeMultiScalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil { t.Error(err) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestMultiScalarMult2NoAllocs(t *testing.T) { p := NewIdentityPoint() if allocs := testing.AllocsPerRun(100, func() { p.MultiScalarMult([]*Scalar{dalekScalar, dalekScalar}, []*Point{B, B}) }); allocs != 0 { t.Errorf("MultiScalarMult allocated %v times, expected 0", allocs) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func TestScalarMultSlowMatchesMult(t *testing.T) { scalarMultSlowMatchesMult := func(x, y Scalar) bool { p := NewGeneratorPoint() p.ScalarMultSlow(&x, p) p.ScalarMultSlow(&y, p) q := NewGeneratorPoint() q.ScalarMult(&x, B) q.ScalarMult(&y, q) checkOnCurve(t, p, q) return p.Equal(q) == 1 } if err := quick.Check(scalarMultSlowMatchesMult, quickCheckConfig(32)); err != nil { t.Error(err) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func BenchmarkScalarMultSlow(b *testing.B) { var p Point x := dalekScalar for i := 0; i < b.N; i++ { p.ScalarMultSlow(x, B) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func BenchmarkMultiScalarMultSize8(t *testing.B) { var p Point x := dalekScalar for i := 0; i < t.N; i++ { p.MultiScalarMult([]*Scalar{x, x, x, x, x, x, x, x}, []*Point{B, B, B, B, B, B, B, B}) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func BenchmarkScalarAddition(b *testing.B) { var rnd [128]byte rand.Read(rnd[:]) s1, _ := (&Scalar{}).SetUniformBytes(rnd[0:64]) s2, _ := (&Scalar{}).SetUniformBytes(rnd[64:128]) t := &Scalar{} b.ResetTimer() for i := 0; i < b.N; i++ { t.Add(s1, s2) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func BenchmarkScalarMultiplication(b *testing.B) { var rnd [128]byte rand.Read(rnd[:]) s1, _ := (&Scalar{}).SetUniformBytes(rnd[0:64]) s2, _ := (&Scalar{}).SetUniformBytes(rnd[64:128]) t := &Scalar{} b.ResetTimer() for i := 0; i < b.N; i++ { t.Multiply(s1, s2) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func BenchmarkScalarInversion(b *testing.B) { var rnd [64]byte rand.Read(rnd[:]) s1, _ := (&Scalar{}).SetUniformBytes(rnd[0:64]) b.ResetTimer() for i := 0; i < b.N; i++ { s1.Invert(s1) } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// func BenchmarkBytesMontgomery(b *testing.B) { publicKey := "3bf918ffc2c955dc895bf145f566fb96623c1cadbe040091175764b5fde322c0" p, err := (&Point{}).SetBytes(decodeHex(publicKey)) if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { _ = p.BytesMontgomery() } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////