internal/wycheproof: add test for CBC decryption with PKCS#5 padding

Change-Id: Ie60bdc10065018e193271b4f90f50298f1272396
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/218323
Run-TryBot: Katie Hockman <katie@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
diff --git a/internal/wycheproof/aes_cbc_test.go b/internal/wycheproof/aes_cbc_test.go
new file mode 100644
index 0000000..0a60fc3
--- /dev/null
+++ b/internal/wycheproof/aes_cbc_test.go
@@ -0,0 +1,127 @@
+// Copyright 2019 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 (
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/hex"
+	"fmt"
+	"testing"
+)
+
+func TestAesCbc(t *testing.T) {
+	// IndCpaTestVector
+	type IndCpaTestVector struct {
+
+		// A brief description of the test case
+		Comment string `json:"comment,omitempty"`
+
+		// the raw ciphertext (without IV)
+		Ct string `json:"ct,omitempty"`
+
+		// A list of flags
+		Flags []string `json:"flags,omitempty"`
+
+		// the initialization vector
+		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"`
+
+		// Identifier of the test case
+		TcId int `json:"tcId,omitempty"`
+	}
+
+	// Notes a description of the labels used in the test vectors
+	type Notes struct {
+	}
+
+	// IndCpaTestGroup
+	type IndCpaTestGroup 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   []*IndCpaTestVector `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    []*IndCpaTestGroup `json:"testGroups,omitempty"`
+	}
+
+	var root Root
+	readTestVector(t, "aes_cbc_pkcs5_test.json", &root)
+	for _, tg := range root.TestGroups {
+	tests:
+		for _, tv := range tg.Tests {
+			block, err := aes.NewCipher(decodeHex(tv.Key))
+			if err != nil {
+				t.Fatalf("#%d: %v", tv.TcId, err)
+			}
+			mode := cipher.NewCBCDecrypter(block, decodeHex(tv.Iv))
+			ct := decodeHex(tv.Ct)
+			if len(ct)%aes.BlockSize != 0 {
+				panic(fmt.Sprintf("#%d: ciphertext is not a multiple of the block size", tv.TcId))
+			}
+			mode.CryptBlocks(ct, ct) // decrypt the block in place
+
+			// Skip the tests that are broken due to bad padding. Panic if there are any
+			// tests left that are invalid for some other reason in the future, to
+			// evaluate what to do with those tests.
+			for _, flag := range tv.Flags {
+				if flag == "BadPadding" {
+					continue tests
+				}
+			}
+			if !shouldPass(tv.Result, tv.Flags, nil) {
+				panic(fmt.Sprintf("#%d: found an invalid test that is broken for some reason other than bad padding", tv.TcId))
+			}
+
+			// Remove the PKCS#5 padding from the given ciphertext to validate it
+			padding := ct[len(ct)-1]
+			paddingNum := int(padding)
+			for i := paddingNum; i > 0; i-- {
+				if ct[len(ct)-i] != padding { // panic if the padding is unexpectedly bad
+					panic(fmt.Sprintf("#%d: bad padding at index=%d of %v", tv.TcId, i, ct))
+				}
+			}
+			ct = ct[:len(ct)-paddingNum]
+
+			if got, want := hex.EncodeToString(ct), tv.Msg; got != want {
+				t.Errorf("#%d, type: %s, comment: %q, decoded ciphertext not equal: %s, want %s", tv.TcId, tv.Result, tv.Comment, got, want)
+			}
+		}
+	}
+}