nip44_test.go raw
1 package nip44
2
3 import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/base64"
7 "encoding/hex"
8 "encoding/json"
9 "os"
10 "strings"
11 "testing"
12
13 "git.smesh.lol/nostrino/pkg/crypto/secp256k1"
14 )
15
16 // The test vectors are the official NIP-44 v2 vectors published at
17 // https://github.com/paulmillr/nip44/blob/main/nip44.vectors.json, whose
18 // SHA-256 is 269ed0f69e4c192512cc779e78c555090cebc7c785b609e338a62afc3ce25040
19 // as stated in the NIP.
20 const vectorsPath = "testdata/nip44.vectors.json"
21
22 type conversationKeyVector struct {
23 Sec1 string `json:"sec1"`
24 Pub2 string `json:"pub2"`
25 ConversationKey string `json:"conversation_key"`
26 }
27
28 type messageKeysVector struct {
29 Nonce string `json:"nonce"`
30 ChaChaKey string `json:"chacha_key"`
31 ChaChaNonce string `json:"chacha_nonce"`
32 HMACKey string `json:"hmac_key"`
33 }
34
35 type encryptDecryptVector struct {
36 Sec1 string `json:"sec1"`
37 Sec2 string `json:"sec2"`
38 ConversationKey string `json:"conversation_key"`
39 Nonce string `json:"nonce"`
40 Plaintext string `json:"plaintext"`
41 Payload string `json:"payload"`
42 }
43
44 type longMessageVector struct {
45 ConversationKey string `json:"conversation_key"`
46 Nonce string `json:"nonce"`
47 Pattern string `json:"pattern"`
48 Repeat int `json:"repeat"`
49 PlaintextSHA256 string `json:"plaintext_sha256"`
50 PayloadSHA256 string `json:"payload_sha256"`
51 }
52
53 type invalidDecryptVector struct {
54 ConversationKey string `json:"conversation_key"`
55 Nonce string `json:"nonce"`
56 Plaintext string `json:"plaintext"`
57 Payload string `json:"payload"`
58 Note string `json:"note"`
59 }
60
61 type invalidConversationKeyVector struct {
62 Sec1 string `json:"sec1"`
63 Pub2 string `json:"pub2"`
64 Note string `json:"note"`
65 }
66
67 type vectors struct {
68 V2 struct {
69 Valid struct {
70 GetConversationKey []conversationKeyVector `json:"get_conversation_key"`
71 GetMessageKeys struct {
72 ConversationKey string `json:"conversation_key"`
73 Keys []messageKeysVector `json:"keys"`
74 } `json:"get_message_keys"`
75 CalcPaddedLen [][2]int `json:"calc_padded_len"`
76 EncryptDecrypt []encryptDecryptVector `json:"encrypt_decrypt"`
77 EncryptDecryptLongMsg []longMessageVector `json:"encrypt_decrypt_long_msg"`
78 } `json:"valid"`
79 Invalid struct {
80 EncryptMsgLengths []int `json:"encrypt_msg_lengths"`
81 GetConversationKey []invalidConversationKeyVector `json:"get_conversation_key"`
82 Decrypt []invalidDecryptVector `json:"decrypt"`
83 } `json:"invalid"`
84 } `json:"v2"`
85 }
86
87 func loadVectors(t *testing.T) vectors {
88 t.Helper()
89 data, err := os.ReadFile(vectorsPath)
90 if err != nil {
91 t.Fatalf("read vectors: %v", err)
92 }
93 sum := sha256.Sum256(data)
94 const wantSum = "269ed0f69e4c192512cc779e78c555090cebc7c785b609e338a62afc3ce25040"
95 if got := hex.EncodeToString(sum[:]); got != wantSum {
96 t.Fatalf("test vector checksum = %s, want %s", got, wantSum)
97 }
98 var v vectors
99 if err := json.Unmarshal(data, &v); err != nil {
100 t.Fatalf("parse vectors: %v", err)
101 }
102 return v
103 }
104
105 func mustHex(t *testing.T, s string, size int) []byte {
106 t.Helper()
107 b, err := hex.DecodeString(s)
108 if err != nil {
109 t.Fatalf("bad hex %q: %v", s, err)
110 }
111 if size >= 0 && len(b) != size {
112 t.Fatalf("hex %q has length %d, want %d", s, len(b), size)
113 }
114 return b
115 }
116
117 func bytes32(t *testing.T, s string) [32]byte {
118 t.Helper()
119 var out [32]byte
120 copy(out[:], mustHex(t, s, 32))
121 return out
122 }
123
124 func TestOfficialVectorChecksum(t *testing.T) {
125 // loadVectors verifies the checksum; this test makes the check appear in
126 // the verbose test list and guards against a stale file.
127 v := loadVectors(t)
128 if len(v.V2.Valid.EncryptDecrypt) == 0 {
129 t.Fatal("no encrypt_decrypt vectors")
130 }
131 if len(v.V2.Valid.EncryptDecrypt) < 10 || len(v.V2.Valid.GetConversationKey) < 35 {
132 t.Fatalf("unexpected vector counts: %d/%d", len(v.V2.Valid.EncryptDecrypt), len(v.V2.Valid.GetConversationKey))
133 }
134 }
135
136 func TestOfficialConversationKeys(t *testing.T) {
137 v := loadVectors(t)
138 for i, vec := range v.V2.Valid.GetConversationKey {
139 sec := bytes32(t, vec.Sec1)
140 pub := bytes32(t, vec.Pub2)
141 got, err := ConversationKey(sec, pub)
142 if err != nil {
143 t.Fatalf("vector %d (%s): unexpected error: %v", i, vec.ConversationKey, err)
144 }
145 want := bytes32(t, vec.ConversationKey)
146 if got != want {
147 t.Fatalf("vector %d: conversation key = %x, want %x", i, got, want)
148 }
149 }
150 }
151
152 func TestOfficialMessageKeys(t *testing.T) {
153 v := loadVectors(t)
154 conv := bytes32(t, v.V2.Valid.GetMessageKeys.ConversationKey)
155 if len(v.V2.Valid.GetMessageKeys.Keys) != 32 {
156 t.Fatalf("got %d message key vectors, want 32", len(v.V2.Valid.GetMessageKeys.Keys))
157 }
158 for i, vec := range v.V2.Valid.GetMessageKeys.Keys {
159 nonce := bytes32(t, vec.Nonce)
160 key, chachaNonce, hmacKey := messageKeys(conv, nonce)
161 if want := mustHex(t, vec.ChaChaKey, 32); !bytes.Equal(key[:], want) {
162 t.Fatalf("vector %d: chacha key = %x, want %x", i, key, want)
163 }
164 if want := mustHex(t, vec.ChaChaNonce, 12); !bytes.Equal(chachaNonce[:], want) {
165 t.Fatalf("vector %d: chacha nonce = %x, want %x", i, chachaNonce, want)
166 }
167 if want := mustHex(t, vec.HMACKey, 32); !bytes.Equal(hmacKey[:], want) {
168 t.Fatalf("vector %d: hmac key = %x, want %x", i, hmacKey, want)
169 }
170 }
171 }
172
173 func TestOfficialCalcPaddedLen(t *testing.T) {
174 v := loadVectors(t)
175 if len(v.V2.Valid.CalcPaddedLen) != 24 {
176 t.Fatalf("got %d padding vectors, want 24", len(v.V2.Valid.CalcPaddedLen))
177 }
178 for _, vec := range v.V2.Valid.CalcPaddedLen {
179 got := calcPaddedLen(uint64(vec[0]))
180 if got != uint64(vec[1]) {
181 t.Fatalf("calcPaddedLen(%d) = %d, want %d", vec[0], got, vec[1])
182 }
183 }
184 }
185
186 func TestOfficialEncryptDecrypt(t *testing.T) {
187 v := loadVectors(t)
188 for i, vec := range v.V2.Valid.EncryptDecrypt {
189 sec1 := bytes32(t, vec.Sec1)
190 sec2 := bytes32(t, vec.Sec2)
191 pub1, ok := secp256k1.PubKey(sec1)
192 if !ok {
193 t.Fatalf("vector %d: bad sec1", i)
194 }
195 pub2, ok := secp256k1.PubKey(sec2)
196 if !ok {
197 t.Fatalf("vector %d: bad sec2", i)
198 }
199 conv := bytes32(t, vec.ConversationKey)
200
201 convA, err := ConversationKey(sec1, pub2)
202 if err != nil {
203 t.Fatalf("vector %d: conv(1, pub2): %v", i, err)
204 }
205 if convA != conv {
206 t.Fatalf("vector %d: conv(1, pub2) = %x, want %x", i, convA, conv)
207 }
208 convB, err := ConversationKey(sec2, pub1)
209 if err != nil {
210 t.Fatalf("vector %d: conv(2, pub1): %v", i, err)
211 }
212 if convB != conv {
213 t.Fatalf("vector %d: conv(2, pub1) = %x, want %x", i, convB, conv)
214 }
215
216 nonce := bytes32(t, vec.Nonce)
217 payload, err := EncryptWithNonce(conv, nonce, []byte(vec.Plaintext))
218 if err != nil {
219 t.Fatalf("vector %d: encrypt: %v", i, err)
220 }
221 if payload != vec.Payload {
222 t.Fatalf("vector %d: payload = %s, want %s", i, payload, vec.Payload)
223 }
224 got, err := Decrypt(conv, vec.Payload)
225 if err != nil {
226 t.Fatalf("vector %d: decrypt: %v", i, err)
227 }
228 if string(got) != vec.Plaintext {
229 t.Fatalf("vector %d: plaintext = %q, want %q", i, got, vec.Plaintext)
230 }
231 // The other direction must decrypt with the same conversation key.
232 got, err = Decrypt(convB, vec.Payload)
233 if err != nil || string(got) != vec.Plaintext {
234 t.Fatalf("vector %d: reverse decrypt: %q, %v", i, got, err)
235 }
236 }
237 }
238
239 func TestOfficialLongMessages(t *testing.T) {
240 v := loadVectors(t)
241 if len(v.V2.Valid.EncryptDecryptLongMsg) != 3 {
242 t.Fatalf("got %d long message vectors, want 3", len(v.V2.Valid.EncryptDecryptLongMsg))
243 }
244 for i, vec := range v.V2.Valid.EncryptDecryptLongMsg {
245 conv := bytes32(t, vec.ConversationKey)
246 nonce := bytes32(t, vec.Nonce)
247 plaintext := []byte(strings.Repeat(vec.Pattern, vec.Repeat))
248 plainSum := sha256.Sum256(plaintext)
249 if got, want := hex.EncodeToString(plainSum[:]), vec.PlaintextSHA256; got != want {
250 t.Fatalf("vector %d: plaintext sha256 = %s, want %s", i, got, want)
251 }
252 payload, err := EncryptWithNonce(conv, nonce, plaintext)
253 if err != nil {
254 t.Fatalf("vector %d: encrypt: %v", i, err)
255 }
256 payloadSum := sha256.Sum256([]byte(payload))
257 if got, want := hex.EncodeToString(payloadSum[:]), vec.PayloadSHA256; got != want {
258 t.Fatalf("vector %d: payload sha256 = %s, want %s", i, got, want)
259 }
260 got, err := Decrypt(conv, payload)
261 if err != nil {
262 t.Fatalf("vector %d: decrypt: %v", i, err)
263 }
264 if !bytes.Equal(got, plaintext) {
265 t.Fatalf("vector %d: plaintext mismatch", i)
266 }
267 }
268 }
269
270 // extendedPrefixVectors are the extended length prefix checks from the NIP
271 // itself (plaintext is "a" repeated plaintextLen times). Payloads are too
272 // large to embed, so only SHA-256 checksums are published; paddedLen excludes
273 // the length prefix.
274 var extendedPrefixVectors = []struct {
275 plaintextLen int
276 paddedLen int
277 plaintextSHA256 string
278 payloadSHA256 string
279 }{
280 {65535, 65536, "6e1bebca6a8229364a162a72ef064826c4cd7457bf54f190ef782bd9deff3e42", "6d8c2810d1e870fbaa1f0a0937126cca837a15f9260e27060c331d70a3c0bc84"},
281 {65536, 65536, "bf718b6f653bebc184e1479f1935b8da974d701b893afcf49e701f3e2f9f9c5a", "b7b4edb36ba92e267d322d56d9aebc22e7fa96ff52e3c12adc07f07a43cbc616"},
282 {65537, 81920, "008ffc88d3c96a9f307524eb361e47c5222a887fc45fa0c1fb8d429c5c23b430", "eeb7c7c5373894ea2c1547cfd3ccb15d5a0b2d619da852e5c79df792dcc9e435"},
283 }
284
285 func TestExtendedPrefixVectors(t *testing.T) {
286 const convHex = "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d"
287 const nonceHex = "0000000000000000000000000000000000000000000000000000000000000001"
288 conv := bytes32(t, convHex)
289 nonce := bytes32(t, nonceHex)
290
291 for _, vec := range extendedPrefixVectors {
292 plaintext := bytes.Repeat([]byte{'a'}, vec.plaintextLen)
293 if got := hex.EncodeToString(sha256Sum(plaintext)); got != vec.plaintextSHA256 {
294 t.Fatalf("len %d: plaintext sha256 = %s, want %s", vec.plaintextLen, got, vec.plaintextSHA256)
295 }
296 if got, want := calcPaddedLen(uint64(vec.plaintextLen)), uint64(vec.paddedLen); got != want {
297 t.Fatalf("len %d: calcPaddedLen = %d, want %d", vec.plaintextLen, got, want)
298 }
299 payload, err := EncryptWithNonce(conv, nonce, plaintext)
300 if err != nil {
301 t.Fatalf("len %d: encrypt: %v", vec.plaintextLen, err)
302 }
303 if got := hex.EncodeToString(sha256Sum([]byte(payload))); got != vec.payloadSHA256 {
304 t.Fatalf("len %d: payload sha256 = %s, want %s", vec.plaintextLen, got, vec.payloadSHA256)
305 }
306 // Check the wire sizes documented in the NIP.
307 raw, err := base64.StdEncoding.DecodeString(payload)
308 if err != nil {
309 t.Fatalf("len %d: decode payload: %v", vec.plaintextLen, err)
310 }
311 prefixLen := 2
312 if vec.plaintextLen >= 65536 {
313 prefixLen = 6
314 }
315 if want := 1 + 32 + prefixLen + vec.paddedLen + 32; len(raw) != want {
316 t.Fatalf("len %d: raw payload = %d bytes, want %d", vec.plaintextLen, len(raw), want)
317 }
318 got, err := Decrypt(conv, payload)
319 if err != nil {
320 t.Fatalf("len %d: decrypt: %v", vec.plaintextLen, err)
321 }
322 if !bytes.Equal(got, plaintext) {
323 t.Fatalf("len %d: plaintext mismatch", vec.plaintextLen)
324 }
325 }
326 }
327
328 func TestOfficialInvalidConversationKeys(t *testing.T) {
329 v := loadVectors(t)
330 if len(v.V2.Invalid.GetConversationKey) != 8 {
331 t.Fatalf("got %d invalid conversation key vectors, want 8", len(v.V2.Invalid.GetConversationKey))
332 }
333 for i, vec := range v.V2.Invalid.GetConversationKey {
334 var sec, pub [32]byte
335 copy(sec[:], mustHex(t, vec.Sec1, 32))
336 copy(pub[:], mustHex(t, vec.Pub2, 32))
337 if _, err := ConversationKey(sec, pub); err == nil {
338 t.Fatalf("vector %d (%s): expected an error", i, vec.Note)
339 }
340 }
341 }
342
343 func TestOfficialInvalidDecrypts(t *testing.T) {
344 v := loadVectors(t)
345 if len(v.V2.Invalid.Decrypt) != 12 {
346 t.Fatalf("got %d invalid decrypt vectors, want 12", len(v.V2.Invalid.Decrypt))
347 }
348 for i, vec := range v.V2.Invalid.Decrypt {
349 conv := bytes32(t, vec.ConversationKey)
350 if _, err := Decrypt(conv, vec.Payload); err == nil {
351 t.Fatalf("vector %d (%s): expected an error", i, vec.Note)
352 }
353 }
354 }
355
356 // TestOfficialInvalidEncryptLengths covers invalid.encrypt_msg_lengths. The
357 // published vector file predates the 6-byte extended length prefix and still
358 // lists 65536, 100000 and 10000000, so only zero (an invalid empty plaintext)
359 // is checked here; extended lengths are covered by the NIP's own checksums in
360 // TestExtendedPrefixVectors.
361 func TestOfficialInvalidEncryptLengths(t *testing.T) {
362 v := loadVectors(t)
363 if len(v.V2.Invalid.EncryptMsgLengths) == 0 {
364 t.Fatal("no invalid encrypt length vectors")
365 }
366 var conv [32]byte
367 conv[0] = 1
368 for _, n := range v.V2.Invalid.EncryptMsgLengths {
369 if n != 0 {
370 continue
371 }
372 if _, err := EncryptWithNonce(conv, [32]byte{}, make([]byte, n)); err == nil {
373 t.Fatalf("encrypting %d bytes: expected an error", n)
374 }
375 }
376 }
377
378 func sha256Sum(b []byte) []byte {
379 s := sha256.Sum256(b)
380 return s[:]
381 }
382
383 func TestConversationKeySymmetry(t *testing.T) {
384 pairs := [][2]string{
385 {"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000002"},
386 {"5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a", "4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d"},
387 {"7a1c0b0f9b3f4e2d1c5a6b7c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7f80912", "112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"},
388 }
389 for _, pair := range pairs {
390 secA := bytes32(t, pair[0])
391 secB := bytes32(t, pair[1])
392 pubA, ok := secp256k1.PubKey(secA)
393 if !ok {
394 t.Fatalf("bad secret a %s", pair[0])
395 }
396 pubB, ok := secp256k1.PubKey(secB)
397 if !ok {
398 t.Fatalf("bad secret b %s", pair[1])
399 }
400 k1, err := ConversationKey(secA, pubB)
401 if err != nil {
402 t.Fatal(err)
403 }
404 k2, err := ConversationKey(secB, pubA)
405 if err != nil {
406 t.Fatal(err)
407 }
408 if k1 != k2 {
409 t.Fatalf("conversation keys differ: %x %x", k1, k2)
410 }
411 }
412 }
413
414 func TestConversationKeyRejectsBadInput(t *testing.T) {
415 var zero, one [32]byte
416 one[31] = 1
417 if _, err := ConversationKey(zero, one); err == nil {
418 t.Fatal("accepted a zero secret key")
419 }
420 pubOne, ok := secp256k1.PubKey(one)
421 if !ok {
422 t.Fatal("pubkey")
423 }
424 offCurve := [32]byte{0xff}
425 if _, err := ConversationKey(one, offCurve); err == nil {
426 t.Fatal("accepted an off-curve public key")
427 }
428 if _, err := ConversationKey(one, pubOne); err != nil {
429 t.Fatalf("self ECDH: %v", err)
430 }
431 }
432
433 func TestRoundTrip(t *testing.T) {
434 conv := bytes32(t, "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d")
435 lengths := []int{1, 31, 32, 33, 100, 65535, 65536, 70000}
436 for _, n := range lengths {
437 plaintext := bytes.Repeat([]byte{'z'}, n)
438 for i := range plaintext {
439 plaintext[i] = byte(i * 31)
440 }
441
442 // Deterministic nonce.
443 var nonce [32]byte
444 nonce[0] = byte(n)
445 nonce[1] = byte(n >> 8)
446 nonce[31] = 0x5a
447 payload, err := EncryptWithNonce(conv, nonce, plaintext)
448 if err != nil {
449 t.Fatalf("len %d: encrypt: %v", n, err)
450 }
451 got, err := Decrypt(conv, payload)
452 if err != nil {
453 t.Fatalf("len %d: decrypt: %v", n, err)
454 }
455 if !bytes.Equal(got, plaintext) {
456 t.Fatalf("len %d: round trip mismatch", n)
457 }
458
459 // Random nonce through Encrypt.
460 payload, err = Encrypt(conv, plaintext)
461 if err != nil {
462 t.Fatalf("len %d: Encrypt: %v", n, err)
463 }
464 got, err = Decrypt(conv, payload)
465 if err != nil {
466 t.Fatalf("len %d: Decrypt: %v", n, err)
467 }
468 if !bytes.Equal(got, plaintext) {
469 t.Fatalf("len %d: random nonce round trip mismatch", n)
470 }
471 }
472 }
473
474 func TestEncryptRejectsEmptyPlaintext(t *testing.T) {
475 var conv [32]byte
476 if _, err := Encrypt(conv, nil); err == nil {
477 t.Fatal("accepted nil plaintext")
478 }
479 if _, err := Encrypt(conv, []byte{}); err == nil {
480 t.Fatal("accepted empty plaintext")
481 }
482 }
483
484 // tamperDecode decodes a payload, applies mutate, and re-encodes it.
485 func tamperDecode(t *testing.T, payload string, mutate func([]byte) []byte) string {
486 t.Helper()
487 raw, err := base64.StdEncoding.DecodeString(payload)
488 if err != nil {
489 t.Fatal(err)
490 }
491 return base64.StdEncoding.EncodeToString(mutate(raw))
492 }
493
494 func TestDecryptTamper(t *testing.T) {
495 conv := bytes32(t, "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d")
496 var nonce [32]byte
497 nonce[0] = 7
498 plaintext := bytes.Repeat([]byte("tamper me "), 32) // 320 bytes
499 payload, err := EncryptWithNonce(conv, nonce, plaintext)
500 if err != nil {
501 t.Fatal(err)
502 }
503 if got, err := Decrypt(conv, payload); err != nil || !bytes.Equal(got, plaintext) {
504 t.Fatalf("baseline decrypt failed: %v", err)
505 }
506
507 tests := []struct {
508 name string
509 payload string
510 }{
511 {"modified MAC", tamperDecode(t, payload, func(raw []byte) []byte {
512 raw[len(raw)-1] ^= 0x01
513 return raw
514 })},
515 {"modified ciphertext", tamperDecode(t, payload, func(raw []byte) []byte {
516 raw[50] ^= 0x80
517 return raw
518 })},
519 {"truncated payload", payload[:len(payload)-4]},
520 {"short payload", payload[:100]},
521 {"truncated base64 decode", payload[:len(payload)-20]},
522 {"hash prefix", "#" + payload[1:]},
523 {"empty", ""},
524 {"just hash", "#"},
525 {"invalid base64", payload[:len(payload)-1] + "!"},
526 {"bad version 0", tamperDecode(t, payload, func(raw []byte) []byte {
527 raw[0] = 0
528 return raw
529 })},
530 {"bad version 3", tamperDecode(t, payload, func(raw []byte) []byte {
531 raw[0] = 3
532 return raw
533 })},
534 {"bad version 1 with del", tamperDecode(t, payload, func(raw []byte) []byte {
535 raw[0] = 1
536 raw = append(raw[:1], raw[2:]...)
537 return raw
538 })},
539 {"wrong conversation key", ""},
540 }
541
542 for _, tc := range tests {
543 if tc.name == "wrong conversation key" {
544 wrong := conv
545 wrong[0] ^= 0xff
546 if _, err := Decrypt(wrong, payload); err == nil {
547 t.Fatal("wrong conversation key: expected an error")
548 }
549 continue
550 }
551 if _, err := Decrypt(conv, tc.payload); err == nil {
552 t.Fatalf("%s: expected an error", tc.name)
553 }
554 }
555 }
556
557 func TestUnpad(t *testing.T) {
558 // Valid small prefix.
559 small := append([]byte{0, 5}, []byte("hello")...)
560 small = append(small, make([]byte, 32-5)...)
561 got, err := unpad(small)
562 if err != nil || string(got) != "hello" {
563 t.Fatalf("unpad small = %q, %v", got, err)
564 }
565
566 // Valid extended prefix: 65536 bytes at exactly the padded length.
567 large := make([]byte, 6+65536)
568 large[3] = 1 // 0x00010000 big-endian in bytes 2..6
569 for i := 6; i < len(large); i++ {
570 large[i] = 0x41
571 }
572 got, err = unpad(large)
573 if err != nil || len(got) != 65536 || got[0] != 0x41 || got[65535] != 0x41 {
574 t.Fatalf("unpad large: len=%d err=%v", len(got), err)
575 }
576
577 bad := []struct {
578 name string
579 in []byte
580 }{
581 {"empty", nil},
582 {"one byte", []byte{0}},
583 {"zero length", []byte{0, 0, 0, 0, 0, 0}},
584 {"short extended", []byte{0, 0, 0, 0}},
585 {"extended below threshold", []byte{0, 0, 0, 0, 1, 0, 'a'}},
586 {"length too small for buffer", []byte{0, 5, 'h', 'e', 'l', 'l', 'o'}},
587 {"padded length wrong", append(append([]byte{0, 5}, []byte("hello")...), make([]byte, 10)...)},
588 {"nonzero padding", append(append([]byte{0, 5}, []byte("hello")...), append([]byte{1}, make([]byte, 26)...)...)},
589 {"u16 zero not followed by u32", []byte{0, 0}},
590 }
591 for _, tc := range bad {
592 if _, err := unpad(tc.in); err == nil {
593 t.Fatalf("%s: expected an error", tc.name)
594 }
595 }
596 }
597