1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ed25519
6
7import (
8	"bufio"
9	"bytes"
10	"compress/gzip"
11	"crypto/rand"
12	"encoding/hex"
13	"io"
14	"os"
15	"strings"
16	"testing"
17
18	"github.com/agl/ed25519/edwards25519"
19)
20
21type zeroReader struct{}
22
23func (zeroReader) Read(buf []byte) (int, error) {
24	for i := range buf {
25		buf[i] = 0
26	}
27	return len(buf), nil
28}
29
30func TestUnmarshalMarshal(t *testing.T) {
31	pub, _, _ := GenerateKey(rand.Reader)
32
33	var A edwards25519.ExtendedGroupElement
34	if !A.FromBytes(pub) {
35		t.Fatalf("ExtendedGroupElement.FromBytes failed")
36	}
37
38	var pub2 [32]byte
39	A.ToBytes(&pub2)
40
41	if *pub != pub2 {
42		t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", *pub, pub2)
43	}
44}
45
46func TestSignVerify(t *testing.T) {
47	var zero zeroReader
48	public, private, _ := GenerateKey(zero)
49
50	message := []byte("test message")
51	sig := Sign(private, message)
52	if !Verify(public, message, sig) {
53		t.Errorf("valid signature rejected")
54	}
55
56	wrongMessage := []byte("wrong message")
57	if Verify(public, wrongMessage, sig) {
58		t.Errorf("signature of different message accepted")
59	}
60}
61
62func TestGolden(t *testing.T) {
63	// sign.input.gz is a selection of test cases from
64	// http://ed25519.cr.yp.to/python/sign.input
65	testDataZ, err := os.Open("testdata/sign.input.gz")
66	if err != nil {
67		t.Fatal(err)
68	}
69	defer testDataZ.Close()
70	testData, err := gzip.NewReader(testDataZ)
71	if err != nil {
72		t.Fatal(err)
73	}
74	defer testData.Close()
75
76	in := bufio.NewReaderSize(testData, 1<<12)
77	lineNo := 0
78	for {
79		lineNo++
80		lineBytes, isPrefix, err := in.ReadLine()
81		if isPrefix {
82			t.Fatal("bufio buffer too small")
83		}
84		if err != nil {
85			if err == io.EOF {
86				break
87			}
88			t.Fatalf("error reading test data: %s", err)
89		}
90
91		line := string(lineBytes)
92		parts := strings.Split(line, ":")
93		if len(parts) != 5 {
94			t.Fatalf("bad number of parts on line %d", lineNo)
95		}
96
97		privBytes, _ := hex.DecodeString(parts[0])
98		pubKeyBytes, _ := hex.DecodeString(parts[1])
99		msg, _ := hex.DecodeString(parts[2])
100		sig, _ := hex.DecodeString(parts[3])
101		// The signatures in the test vectors also include the message
102		// at the end, but we just want R and S.
103		sig = sig[:SignatureSize]
104
105		if l := len(pubKeyBytes); l != PublicKeySize {
106			t.Fatalf("bad public key length on line %d: got %d bytes", lineNo, l)
107		}
108
109		var priv [PrivateKeySize]byte
110		copy(priv[:], privBytes)
111		copy(priv[32:], pubKeyBytes)
112
113		sig2 := Sign(&priv, msg)
114		if !bytes.Equal(sig, sig2[:]) {
115			t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
116		}
117
118		var pubKey [PublicKeySize]byte
119		copy(pubKey[:], pubKeyBytes)
120		if !Verify(&pubKey, msg, sig2) {
121			t.Errorf("signature failed to verify on line %d", lineNo)
122		}
123	}
124}
125
126func BenchmarkKeyGeneration(b *testing.B) {
127	var zero zeroReader
128	for i := 0; i < b.N; i++ {
129		if _, _, err := GenerateKey(zero); err != nil {
130			b.Fatal(err)
131		}
132	}
133}
134
135func BenchmarkSigning(b *testing.B) {
136	var zero zeroReader
137	_, priv, err := GenerateKey(zero)
138	if err != nil {
139		b.Fatal(err)
140	}
141	message := []byte("Hello, world!")
142	b.ResetTimer()
143	for i := 0; i < b.N; i++ {
144		Sign(priv, message)
145	}
146}
147
148func BenchmarkVerification(b *testing.B) {
149	var zero zeroReader
150	pub, priv, err := GenerateKey(zero)
151	if err != nil {
152		b.Fatal(err)
153	}
154	message := []byte("Hello, world!")
155	signature := Sign(priv, message)
156	b.ResetTimer()
157	for i := 0; i < b.N; i++ {
158		Verify(pub, message, signature)
159	}
160}
161