package s3crypto

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"testing"
)

func TestAESCBCEncryptDecrypt(t *testing.T) {
	var testCases = []struct {
		key        string
		iv         string
		plaintext  string
		ciphertext string
		decodeHex  bool
		padder     Padder
	}{
		// Test vectors from RFC 3602: https://tools.ietf.org/html/rfc3602
		{
			"06a9214036b8a15b512e03d534120006",
			"3dafba429d9eb430b422da802c9fac41",
			"Single block msg",
			"e353779c1079aeb82708942dbe77181a",
			false,
			NoPadder,
		},
		{
			"c286696d887c9aa0611bbb3e2025a45a",
			"562e17996d093d28ddb3ba695a2e6f58",
			"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
			"d296cd94c2cccf8a3a863028b5e1dc0a7586602d253cfff91b8266bea6d61ab1",
			true,
			NoPadder,
		},
		{
			"6c3ea0477630ce21a2ce334aa746c2cd",
			"c782dc4c098c66cbd9cd27d825682c81",
			"This is a 48-byte message (exactly 3 AES blocks)",
			"d0a02b3836451753d493665d33f0e8862dea54cdb293abc7506939276772f8d5021c19216bad525c8579695d83ba2684",
			false,
			NoPadder,
		},
		{
			"56e47a38c5598974bc46903dba290349",
			"8ce82eefbea0da3c44699ed7db51b7d9",
			"a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf",
			"c30e32ffedc0774e6aff6af0869f71aa0f3af07a9a31a9c684db207eb0ef8e4e35907aa632c3ffdf868bb7b29d3d46ad83ce9f9a102ee99d49a53e87f4c3da55",
			true,
			NoPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"",
			"B012949BA07D1A6DCE9DEE67274D41AB",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41",
			"8A11ABA68A566132FFE04DB336621D41",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141",
			"97D0896E41DFDB5CEA4A9EB70A938CFD",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141",
			"8464EAD45FA2D8790E8741E32C28083F",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141",
			"1E656D6E2745BA9F154FAF136B2BC73D",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141",
			"0B6031C4B230DAC6BD6D3F195645B287",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141",
			"5D09FEB6462BB489489A7E18FD341D9D",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141",
			"85745E398F2FD1050C2CE8F8614DA369",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141",
			"7BE52933970BA7B0FC6FB3FC37648205",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141",
			"ED3A1E134EF36CCFE60C8123B4272F89",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141",
			"C3B7C9E177E1052FC736F65FC1E74209",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141",
			"C3A8B53F7F57F0B9D346FA99810A3C28",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141",
			"D16B1ECE5BF00AF919E139E99775FF06",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141",
			"B258F4DF57FFCA1EFCF8D76140F05139",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141",
			"3CD2282DE24A2CF9E23326CC3DC9077A",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141",
			"3010232E7C752A3B4C9EE428B4C4FE88",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A22BC4E6D03BFD2418DD412D1ED1B31AF",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A5427BBD4A4D50776989441370E3B5B16",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A7FF985F55567D1B25EA40E23BB4CB1FE",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A0835E548C7370D8F8D9925C0E6B54727",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ADC0CF1436399E67BC1122B31CB596649",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A3D096F0DEAFF91938B82E5D404B0B065",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217AD56ABA897A355CF307CCB74226243192",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A151284F950B1B1DBCAD6D9E7900DF4E6",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217AEF85A612514121C299A1D87116C4A182",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A67F157569BFB4013EA3AD16DB8C69AD6",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217AF8520D191F6ACBD88B2140588B91C697",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ADD8BBAA71745669B96F2683E2F5AEC35",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217AFB2D4282817D7EC6B33EFAD7AA14A3C5",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A459B89E7E0DAF3DA654576B60B2DA7CE",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A65759F23F9789D05B23D5DBAA9E32036",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217A03C78FBD5E2CB08B3B6D181E23FBDE79",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA013D941FBBDE56C106C482CD022F290F",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA0645D313AC3C29B79DB1AA2E00A5B393",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA2ED0FD8048053BF22EBE501D82C4B3F1",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAC57D706C7866A01D6E913F98AE57EE54",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAB7FC1241FAFDFE45C4FF982D5DC1DAEF",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA7063EA296922DE8BDFD3B29D786C5F91",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA3A4603475F4AFDBFADC6E7FA908188B1",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA3365C63C2AF2A6C8FB4D0E9ED3C6FDA3",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA78BCC1874C0B7EB52645FC8F03B9C9CF",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA9B7A31397718EECB89B9E9CCCD729326",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAB15EA8A67E9E9FADB4249710277F3D4F",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA94641D6A076193C660632CEA3F9CB02C",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAB2170A08417BE77F0EAA9110F4790E12",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA4E30F1CD7B2256ABD57DC3DAB05376C9",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA9909B7B93D01BDAAC22D15AF34DF1EEF",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"4141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAD97F5D1206F00E5C7225CAD81CCD4027",
			true,
			AESCBCPadder,
		},
		{
			"11111111111111111111111111111111",
			"22222222222222222222222222222222",
			"414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
			"C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA570CBB001A0C87558906B60C884AB5F41DA97CEF2A9401BC6DD0D22A54DBAD6D",
			true,
			AESCBCPadder,
		},
	}

	for i, testCase := range testCases {
		key, _ := hex.DecodeString(testCase.key)
		iv, _ := hex.DecodeString(testCase.iv)
		cd := CipherData{
			Key: key,
			IV:  iv,
		}

		cbc, err := newAESCBC(cd, testCase.padder)
		if err != nil {
			t.Fatal(fmt.Sprintf("Case %d: Expected no error for cipher creation, but received: %v", i, err.Error()))
		}

		plaintext := []byte(testCase.plaintext)
		if testCase.decodeHex {
			plaintext, _ = hex.DecodeString(testCase.plaintext)
		}

		cipherdata := cbc.Encrypt(bytes.NewReader(plaintext))
		ciphertext := []byte{}
		b := make([]byte, 19)
		err = nil
		n := 0
		for err != io.EOF {
			n, err = cipherdata.Read(b)
			ciphertext = append(ciphertext, b[:n]...)
		}

		if err != io.EOF {
			t.Fatal(fmt.Sprintf("Case %d: Expected no error during io reading, but received: %v", i, err.Error()))
		}

		expectedData, _ := hex.DecodeString(testCase.ciphertext)
		if bytes.Compare(expectedData, ciphertext) != 0 {
			t.Log("\n", ciphertext, "\n", expectedData)
			t.Fatal(fmt.Sprintf("Case %d: AES CBC encryption fails. Data is not the same", i))
		}

		plaindata := cbc.Decrypt(bytes.NewReader(ciphertext))
		plaintextDecrypted := []byte{}
		err = nil
		for err != io.EOF {
			n, err = plaindata.Read(b)
			plaintextDecrypted = append(plaintextDecrypted, b[:n]...)
		}
		if err != io.EOF {
			t.Fatal(fmt.Sprintf("Case %d: Expected no error during io reading, but received: %v", i, err.Error()))
		}

		if bytes.Compare(plaintext, plaintextDecrypted) != 0 {
			t.Log("\n", plaintext, "\n", plaintextDecrypted)
			t.Fatal(fmt.Sprintf("Case %d: AES CBC decryption fails. Data is not the same", i))
		}
	}
}