1package ed25519
2
3import (
4	"bufio"
5	"bytes"
6	"compress/gzip"
7	"crypto"
8	"crypto/rand"
9	"encoding/hex"
10	"os"
11	"strings"
12	"testing"
13
14	"github.com/cretz/bine/torutil/ed25519/internal/edwards25519"
15)
16
17// Taken from https://github.com/golang/crypto/blob/1a580b3eff7814fc9b40602fd35256c63b50f491/ed25519/ed25519_test.go
18
19type zeroReader struct{}
20
21func (zeroReader) Read(buf []byte) (int, error) {
22	for i := range buf {
23		buf[i] = 0
24	}
25	return len(buf), nil
26}
27
28func TestUnmarshalMarshal(t *testing.T) {
29	pair, _ := GenerateKey(rand.Reader)
30
31	var A edwards25519.ExtendedGroupElement
32	var pubBytes [32]byte
33	copy(pubBytes[:], pair.PublicKey())
34	if !A.FromBytes(&pubBytes) {
35		t.Fatalf("ExtendedGroupElement.FromBytes failed")
36	}
37
38	var pub2 [32]byte
39	A.ToBytes(&pub2)
40
41	if pubBytes != pub2 {
42		t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", pubBytes, pub2)
43	}
44}
45
46func TestSignVerify(t *testing.T) {
47	var zero zeroReader
48	pair, _ := GenerateKey(zero)
49
50	message := []byte("test message")
51	sig := Sign(pair, message)
52	if !Verify(pair.PublicKey(), message, sig) {
53		t.Errorf("valid signature rejected")
54	}
55
56	wrongMessage := []byte("wrong message")
57	if Verify(pair.PublicKey(), wrongMessage, sig) {
58		t.Errorf("signature of different message accepted")
59	}
60}
61
62func TestCryptoSigner(t *testing.T) {
63	var zero zeroReader
64	pair, _ := GenerateKey(zero)
65
66	signer := crypto.Signer(pair)
67
68	publicInterface := signer.Public()
69	public2, ok := publicInterface.(PublicKey)
70	if !ok {
71		t.Fatalf("expected PublicKey from Public() but got %T", publicInterface)
72	}
73
74	if !bytes.Equal(pair.PublicKey(), public2) {
75		t.Errorf("public keys do not match: original:%x vs Public():%x", pair.PublicKey(), public2)
76	}
77
78	message := []byte("message")
79	var noHash crypto.Hash
80	signature, err := signer.Sign(zero, message, noHash)
81	if err != nil {
82		t.Fatalf("error from Sign(): %s", err)
83	}
84
85	if !Verify(pair.PublicKey(), message, signature) {
86		t.Errorf("Verify failed on signature from Sign()")
87	}
88}
89
90func TestGolden(t *testing.T) {
91	// sign.input.gz is a selection of test cases from
92	// https://ed25519.cr.yp.to/python/sign.input
93	testDataZ, err := os.Open("testdata/sign.input.gz")
94	if err != nil {
95		t.Fatal(err)
96	}
97	defer testDataZ.Close()
98	testData, err := gzip.NewReader(testDataZ)
99	if err != nil {
100		t.Fatal(err)
101	}
102	defer testData.Close()
103
104	scanner := bufio.NewScanner(testData)
105	lineNo := 0
106
107	for scanner.Scan() {
108		lineNo++
109
110		line := scanner.Text()
111		parts := strings.Split(line, ":")
112		if len(parts) != 5 {
113			t.Fatalf("bad number of parts on line %d", lineNo)
114		}
115
116		privBytes, _ := hex.DecodeString(parts[0])
117		pubKey, _ := hex.DecodeString(parts[1])
118		msg, _ := hex.DecodeString(parts[2])
119		sig, _ := hex.DecodeString(parts[3])
120		// The signatures in the test vectors also include the message
121		// at the end, but we just want R and S.
122		sig = sig[:SignatureSize]
123
124		if l := len(pubKey); l != PublicKeySize {
125			t.Fatalf("bad public key length on line %d: got %d bytes", lineNo, l)
126		}
127
128		var otherPriv [PrivateKeySize]byte
129		copy(otherPriv[:], privBytes)
130		copy(otherPriv[32:], pubKey)
131		priv := FromCryptoPrivateKey(otherPriv[:])
132
133		sig2 := Sign(priv, msg)
134		if !bytes.Equal(sig, sig2[:]) {
135			t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
136		}
137
138		if !Verify(pubKey, msg, sig2) {
139			t.Errorf("signature failed to verify on line %d", lineNo)
140		}
141	}
142
143	if err := scanner.Err(); err != nil {
144		t.Fatalf("error reading test data: %s", err)
145	}
146}
147
148func TestMalleability(t *testing.T) {
149	// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
150	// that s be in [0, order). This prevents someone from adding a multiple of
151	// order to s and obtaining a second valid signature for the same message.
152	msg := []byte{0x54, 0x65, 0x73, 0x74}
153	sig := []byte{
154		0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a,
155		0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b,
156		0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67,
157		0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
158		0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33,
159		0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d,
160	}
161	publicKey := []byte{
162		0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5,
163		0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34,
164		0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
165	}
166
167	if Verify(publicKey, msg, sig) {
168		t.Fatal("non-canonical signature accepted")
169	}
170}
171
172func BenchmarkKeyGeneration(b *testing.B) {
173	var zero zeroReader
174	for i := 0; i < b.N; i++ {
175		if _, err := GenerateKey(zero); err != nil {
176			b.Fatal(err)
177		}
178	}
179}
180
181func BenchmarkSigning(b *testing.B) {
182	var zero zeroReader
183	pair, err := GenerateKey(zero)
184	if err != nil {
185		b.Fatal(err)
186	}
187	message := []byte("Hello, world!")
188	b.ResetTimer()
189	for i := 0; i < b.N; i++ {
190		Sign(pair, message)
191	}
192}
193
194func BenchmarkVerification(b *testing.B) {
195	var zero zeroReader
196	pair, err := GenerateKey(zero)
197	if err != nil {
198		b.Fatal(err)
199	}
200	message := []byte("Hello, world!")
201	signature := Sign(pair, message)
202	b.ResetTimer()
203	for i := 0; i < b.N; i++ {
204		Verify(pair.PublicKey(), message, signature)
205	}
206}
207