gnarl_wire_test.go raw
1 package crypto
2
3 import (
4 "bytes"
5 "testing"
6 )
7
8 // gnarlTestSecret creates a Hamadryad secret from a seed string for testing.
9 func gnarlTestSecret(seed string) Hamadryad {
10 var secret Hamadryad
11 mid := GMid([]byte(seed))
12 copy(secret[:], mid[:])
13 return secret
14 }
15
16 func TestGnarlHeaderLen(t *testing.T) {
17 if GnarlHeaderLen != 64 {
18 t.Fatalf("GnarlHeaderLen = %d, want 64", GnarlHeaderLen)
19 }
20 if GnarlNonceLen != 10 {
21 t.Fatalf("GnarlNonceLen = %d, want 10", GnarlNonceLen)
22 }
23 }
24
25 func TestGnarlSealOpenRoundTrip(t *testing.T) {
26 var secret Hamadryad
27 h := GHash([]byte("test-shared-secret"))
28 copy(secret[:], h[:])
29
30 identity := GMid([]byte("sender-spore-fingerprint"))
31 nonce := GnarlNonceFromCounter(0, 1)
32 plaintext := []byte("hello from the Bethe lattice")
33
34 pkt := GnarlSeal(secret, identity, nonce, plaintext)
35
36 if pkt.Nonce != nonce {
37 t.Fatal("nonce mismatch")
38 }
39 if pkt.Identity != identity {
40 t.Fatal("identity mismatch")
41 }
42 if len(pkt.Ciphertext) != len(plaintext) {
43 t.Fatalf("ciphertext len = %d, want %d", len(pkt.Ciphertext), len(plaintext))
44 }
45 if bytes.Equal(pkt.Ciphertext, plaintext) {
46 t.Fatal("ciphertext == plaintext")
47 }
48
49 got, err := GnarlOpen(secret, pkt)
50 if err != nil {
51 t.Fatalf("GnarlOpen: %v", err)
52 }
53 if !bytes.Equal(got, plaintext) {
54 t.Fatalf("decrypted = %q, want %q", got, plaintext)
55 }
56 }
57
58 func TestGnarlSealOpenEmptyPlaintext(t *testing.T) {
59 secret := gnarlTestSecret("empty-test")
60 identity := GMid([]byte("id"))
61 nonce := GnarlNonceFromCounter(0, 0)
62
63 pkt := GnarlSeal(secret, identity, nonce, []byte{})
64 if len(pkt.Ciphertext) != 0 {
65 t.Fatalf("ciphertext len = %d for empty plaintext", len(pkt.Ciphertext))
66 }
67
68 got, err := GnarlOpen(secret, pkt)
69 if err != nil {
70 t.Fatalf("GnarlOpen empty: %v", err)
71 }
72 if len(got) != 0 {
73 t.Fatalf("decrypted len = %d, want 0", len(got))
74 }
75 }
76
77 func TestGnarlSealOpenLargePayload(t *testing.T) {
78 secret := gnarlTestSecret("large")
79 identity := GMid([]byte("sender"))
80 nonce := GnarlNonceFromCounter(1, 42)
81
82 plaintext := make([]byte, 1400)
83 for i := range plaintext {
84 plaintext[i] = byte(i * 7)
85 }
86
87 pkt := GnarlSeal(secret, identity, nonce, plaintext)
88 got, err := GnarlOpen(secret, pkt)
89 if err != nil {
90 t.Fatal(err)
91 }
92 if !bytes.Equal(got, plaintext) {
93 t.Fatal("large payload round-trip failed")
94 }
95 }
96
97 func TestGnarlMarshalUnmarshalRoundTrip(t *testing.T) {
98 secret := gnarlTestSecret("marshal-test")
99 identity := GMid([]byte("me"))
100 nonce := GnarlNonceFromCounter(0, 99)
101 plaintext := []byte("wire format test")
102
103 pkt := GnarlSeal(secret, identity, nonce, plaintext)
104 wire := MarshalGnarlPacket(pkt)
105
106 if len(wire) != GnarlHeaderLen+len(plaintext) {
107 t.Fatalf("wire len = %d, want %d", len(wire), GnarlHeaderLen+len(plaintext))
108 }
109
110 pkt2, err := UnmarshalGnarlPacket(wire)
111 if err != nil {
112 t.Fatal(err)
113 }
114
115 if pkt2.Nonce != pkt.Nonce {
116 t.Fatal("nonce mismatch after unmarshal")
117 }
118 if pkt2.Identity != pkt.Identity {
119 t.Fatal("identity mismatch after unmarshal")
120 }
121 if pkt2.AuthTag != pkt.AuthTag {
122 t.Fatal("auth tag mismatch after unmarshal")
123 }
124 if !bytes.Equal(pkt2.Ciphertext, pkt.Ciphertext) {
125 t.Fatal("ciphertext mismatch after unmarshal")
126 }
127
128 got, err := GnarlOpen(secret, pkt2)
129 if err != nil {
130 t.Fatal(err)
131 }
132 if !bytes.Equal(got, plaintext) {
133 t.Fatal("decrypt after unmarshal failed")
134 }
135 }
136
137 func TestGnarlOpenWrongSecret(t *testing.T) {
138 secret1 := gnarlTestSecret("key-1")
139 secret2 := gnarlTestSecret("key-2")
140
141 identity := GMid([]byte("id"))
142 nonce := GnarlNonceFromCounter(0, 1)
143
144 pkt := GnarlSeal(secret1, identity, nonce, []byte("secret data"))
145
146 _, err := GnarlOpen(secret2, pkt)
147 if err == nil {
148 t.Fatal("wrong secret accepted")
149 }
150 }
151
152 func TestGnarlOpenTamperedCiphertext(t *testing.T) {
153 secret := gnarlTestSecret("tamper-test")
154 identity := GMid([]byte("id"))
155 nonce := GnarlNonceFromCounter(0, 1)
156
157 pkt := GnarlSeal(secret, identity, nonce, []byte("important data"))
158 pkt.Ciphertext[0] ^= 0x01
159
160 _, err := GnarlOpen(secret, pkt)
161 if err == nil {
162 t.Fatal("tampered ciphertext accepted")
163 }
164 }
165
166 func TestGnarlOpenTamperedIdentity(t *testing.T) {
167 secret := gnarlTestSecret("id-tamper")
168 identity := GMid([]byte("real-sender"))
169 nonce := GnarlNonceFromCounter(0, 1)
170
171 pkt := GnarlSeal(secret, identity, nonce, []byte("data"))
172 pkt.Identity[0] ^= 0xFF
173
174 _, err := GnarlOpen(secret, pkt)
175 if err == nil {
176 t.Fatal("tampered identity accepted")
177 }
178 }
179
180 func TestGnarlOpenTamperedNonce(t *testing.T) {
181 secret := gnarlTestSecret("nonce-tamper")
182 identity := GMid([]byte("sender"))
183 nonce := GnarlNonceFromCounter(0, 1)
184
185 pkt := GnarlSeal(secret, identity, nonce, []byte("data"))
186 pkt.Nonce[0] ^= 0x01
187
188 _, err := GnarlOpen(secret, pkt)
189 if err == nil {
190 t.Fatal("tampered nonce accepted")
191 }
192 }
193
194 func TestGnarlOpenNilPacket(t *testing.T) {
195 var secret Hamadryad
196 _, err := GnarlOpen(secret, nil)
197 if err == nil {
198 t.Fatal("nil packet accepted")
199 }
200 }
201
202 func TestUnmarshalGnarlPacketTooShort(t *testing.T) {
203 _, err := UnmarshalGnarlPacket(make([]byte, GnarlHeaderLen-1))
204 if err == nil {
205 t.Fatal("short packet accepted")
206 }
207 }
208
209 func TestUnmarshalGnarlPacketHeaderOnly(t *testing.T) {
210 pkt, err := UnmarshalGnarlPacket(make([]byte, GnarlHeaderLen))
211 if err != nil {
212 t.Fatalf("header-only unmarshal: %v", err)
213 }
214 if len(pkt.Ciphertext) != 0 {
215 t.Fatalf("ciphertext len = %d for header-only", len(pkt.Ciphertext))
216 }
217 }
218
219 func TestGnarlNonceFromCounter(t *testing.T) {
220 nonce := GnarlNonceFromCounter(0x1234, 0xDEADBEEF)
221 if nonce[0] != 0xEF || nonce[1] != 0xBE || nonce[2] != 0xAD || nonce[3] != 0xDE {
222 t.Fatalf("counter bytes wrong: %x", nonce[:8])
223 }
224 if nonce[8] != 0x34 || nonce[9] != 0x12 {
225 t.Fatalf("epoch bytes wrong: %x", nonce[8:10])
226 }
227 }
228
229 func TestGnarlSealDeterministic(t *testing.T) {
230 secret := gnarlTestSecret("det")
231 identity := GMid([]byte("id"))
232 nonce := GnarlNonceFromCounter(0, 0)
233
234 pkt1 := GnarlSeal(secret, identity, nonce, []byte("hello"))
235 pkt2 := GnarlSeal(secret, identity, nonce, []byte("hello"))
236 if !bytes.Equal(pkt1.Ciphertext, pkt2.Ciphertext) {
237 t.Fatal("Seal not deterministic")
238 }
239 if pkt1.AuthTag != pkt2.AuthTag {
240 t.Fatal("auth tag not deterministic")
241 }
242 }
243
244 func TestGnarlSealDifferentNonces(t *testing.T) {
245 secret := gnarlTestSecret("nonce-diff")
246 identity := GMid([]byte("id"))
247
248 n1 := GnarlNonceFromCounter(0, 1)
249 n2 := GnarlNonceFromCounter(0, 2)
250 plaintext := []byte("same message")
251
252 pkt1 := GnarlSeal(secret, identity, n1, plaintext)
253 pkt2 := GnarlSeal(secret, identity, n2, plaintext)
254 if bytes.Equal(pkt1.Ciphertext, pkt2.Ciphertext) {
255 t.Fatal("different nonces produced same ciphertext")
256 }
257 if pkt1.AuthTag == pkt2.AuthTag {
258 t.Fatal("different nonces produced same auth tag")
259 }
260 }
261