|  | // Copyright 2020 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package wycheproof | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "crypto/aes" | 
|  | "crypto/cipher" | 
|  | "fmt" | 
|  | "testing" | 
|  |  | 
|  | "golang.org/x/crypto/chacha20poly1305" | 
|  | ) | 
|  |  | 
|  | func TestAEAD(t *testing.T) { | 
|  | // AeadTestVector | 
|  | type AeadTestVector struct { | 
|  |  | 
|  | // additional authenticated data | 
|  | Aad string `json:"aad,omitempty"` | 
|  |  | 
|  | // A brief description of the test case | 
|  | Comment string `json:"comment,omitempty"` | 
|  |  | 
|  | // the ciphertext (without iv and tag) | 
|  | Ct string `json:"ct,omitempty"` | 
|  |  | 
|  | // A list of flags | 
|  | Flags []string `json:"flags,omitempty"` | 
|  |  | 
|  | // the nonce | 
|  | Iv string `json:"iv,omitempty"` | 
|  |  | 
|  | // the key | 
|  | Key string `json:"key,omitempty"` | 
|  |  | 
|  | // the plaintext | 
|  | Msg string `json:"msg,omitempty"` | 
|  |  | 
|  | // Test result | 
|  | Result string `json:"result,omitempty"` | 
|  |  | 
|  | // the authentication tag | 
|  | Tag string `json:"tag,omitempty"` | 
|  |  | 
|  | // Identifier of the test case | 
|  | TcId int `json:"tcId,omitempty"` | 
|  | } | 
|  |  | 
|  | // Notes a description of the labels used in the test vectors | 
|  | type Notes struct { | 
|  | } | 
|  |  | 
|  | // AeadTestGroup | 
|  | type AeadTestGroup struct { | 
|  |  | 
|  | // the IV size in bits | 
|  | IvSize int `json:"ivSize,omitempty"` | 
|  |  | 
|  | // the keySize in bits | 
|  | KeySize int `json:"keySize,omitempty"` | 
|  |  | 
|  | // the expected size of the tag in bits | 
|  | TagSize int               `json:"tagSize,omitempty"` | 
|  | Tests   []*AeadTestVector `json:"tests,omitempty"` | 
|  | Type    interface{}       `json:"type,omitempty"` | 
|  | } | 
|  |  | 
|  | // Root | 
|  | type Root struct { | 
|  |  | 
|  | // the primitive tested in the test file | 
|  | Algorithm string `json:"algorithm,omitempty"` | 
|  |  | 
|  | // the version of the test vectors. | 
|  | GeneratorVersion string `json:"generatorVersion,omitempty"` | 
|  |  | 
|  | // additional documentation | 
|  | Header []string `json:"header,omitempty"` | 
|  |  | 
|  | // a description of the labels used in the test vectors | 
|  | Notes *Notes `json:"notes,omitempty"` | 
|  |  | 
|  | // the number of test vectors in this test | 
|  | NumberOfTests int              `json:"numberOfTests,omitempty"` | 
|  | Schema        interface{}      `json:"schema,omitempty"` | 
|  | TestGroups    []*AeadTestGroup `json:"testGroups,omitempty"` | 
|  | } | 
|  |  | 
|  | testSealOpen := func(t *testing.T, aead cipher.AEAD, tv *AeadTestVector, recoverBadNonce func()) { | 
|  | defer recoverBadNonce() | 
|  |  | 
|  | iv, tag, ct, msg, aad := decodeHex(tv.Iv), decodeHex(tv.Tag), decodeHex(tv.Ct), decodeHex(tv.Msg), decodeHex(tv.Aad) | 
|  |  | 
|  | genCT := aead.Seal(nil, iv, msg, aad) | 
|  | genMsg, err := aead.Open(nil, iv, genCT, aad) | 
|  | if err != nil { | 
|  | t.Errorf("failed to decrypt generated ciphertext: %s", err) | 
|  | } | 
|  | if !bytes.Equal(genMsg, msg) { | 
|  | t.Errorf("unexpected roundtripped plaintext: got %x, want %x", genMsg, msg) | 
|  | } | 
|  |  | 
|  | ctWithTag := append(ct, tag...) | 
|  | msg2, err := aead.Open(nil, iv, ctWithTag, aad) | 
|  | wantPass := shouldPass(tv.Result, tv.Flags, nil) | 
|  | if !wantPass && err == nil { | 
|  | t.Error("decryption succeeded when it should've failed") | 
|  | } else if wantPass { | 
|  | if err != nil { | 
|  | t.Fatalf("decryption failed: %s", err) | 
|  | } | 
|  | if !bytes.Equal(genCT, ctWithTag) { | 
|  | t.Errorf("generated ciphertext doesn't match expected: got %x, want %x", genCT, ctWithTag) | 
|  | } | 
|  | if !bytes.Equal(msg, msg2) { | 
|  | t.Errorf("decrypted ciphertext doesn't match expected: got %x, want %x", msg2, msg) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | vectors := map[string]func(*testing.T, []byte) cipher.AEAD{ | 
|  | "aes_gcm_test.json": func(t *testing.T, key []byte) cipher.AEAD { | 
|  | aesCipher, err := aes.NewCipher(key) | 
|  | if err != nil { | 
|  | t.Fatalf("failed to construct cipher: %s", err) | 
|  | } | 
|  | aead, err := cipher.NewGCM(aesCipher) | 
|  | if err != nil { | 
|  | t.Fatalf("failed to construct cipher: %s", err) | 
|  | } | 
|  | return aead | 
|  | }, | 
|  | "chacha20_poly1305_test.json": func(t *testing.T, key []byte) cipher.AEAD { | 
|  | aead, err := chacha20poly1305.New(key) | 
|  | if err != nil { | 
|  | t.Fatalf("failed to construct cipher: %s", err) | 
|  | } | 
|  | return aead | 
|  | }, | 
|  | "xchacha20_poly1305_test.json": func(t *testing.T, key []byte) cipher.AEAD { | 
|  | aead, err := chacha20poly1305.NewX(key) | 
|  | if err != nil { | 
|  | t.Fatalf("failed to construct cipher: %s", err) | 
|  | } | 
|  | return aead | 
|  | }, | 
|  | } | 
|  | for file, cipherInit := range vectors { | 
|  | var root Root | 
|  | readTestVector(t, file, &root) | 
|  | for _, tg := range root.TestGroups { | 
|  | for _, tv := range tg.Tests { | 
|  | testName := fmt.Sprintf("%s #%d", file, tv.TcId) | 
|  | if tv.Comment != "" { | 
|  | testName += fmt.Sprintf(" %s", tv.Comment) | 
|  | } | 
|  | t.Run(testName, func(t *testing.T) { | 
|  | aead := cipherInit(t, decodeHex(tv.Key)) | 
|  | testSealOpen(t, aead, tv, func() { | 
|  | // A bad nonce causes a panic in AEAD.Seal and AEAD.Open, | 
|  | // so should be recovered. Fail the test if it broke for | 
|  | // some other reason. | 
|  | if r := recover(); r != nil { | 
|  | if tg.IvSize/8 == aead.NonceSize() { | 
|  | t.Error("unexpected panic") | 
|  | } | 
|  | } | 
|  | }) | 
|  | }) | 
|  | } | 
|  | } | 
|  | } | 
|  | } |