#ifndef _RIJNDAEL_H_
#define _RIJNDAEL_H_
//=============================================================================
//
//   File : Rijndael.h
//   Creation date : Sun Nov 5 2000 15:42:14 CEST by Szymon Stefanek
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net)
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the terms of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your option) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

//
// Another implementation of the Rijndael cipher.
// This is intended to be an easily usable library file.
// Based on the Vincent Rijmen and K.U.Leuven implementation 2.4.
//

//
// Original Copyright notice:
//
//    rijndael-alg-fst.c   v2.4   April '2000
//    rijndael-alg-fst.h
//    rijndael-api-fst.c
//    rijndael-api-fst.h
//
//    Optimised ANSI C code
//
//    authors: v1.0: Antoon Bosselaers
//             v2.0: Vincent Rijmen, K.U.Leuven
//             v2.3: Paulo Barreto
//             v2.4: Vincent Rijmen, K.U.Leuven
//
//    This code is placed in the public domain.
//

//
// This implementation works on 128, 192, 256 bit keys
// and on 128 bit blocks
//

//
// Example of usage:
//
//  // Input data
//	unsigned char key[32];                       // The key
//  initializeYour256BitKey();                   // Obviously initialized with sth
//  const unsigned char * plainText = getYourPlainText(); // Your plain text
//  int plainTextLen = strlen(plainText);        // Plain text length
//
//  // Encrypting
//	Rijndael rin;
//  unsigned char output[plainTextLen + 16];
//
//	rin.init(Rijndael::CBC,Rijndael::Encrypt,key,Rijndael::Key32Bytes);
//  // It is a good idea to check the error code
//	int len = rin.padEncrypt(plainText,len,output);
//  if(len >= 0)useYourEncryptedText();
//  else encryptError(len);
//
//  // Decrypting: we can reuse the same object
//	unsigned char output2[len];
//	rin.init(Rijndael::ECB,Rijndael::Decrypt,keyMaterial,Rijndael::Key32Bytes));
//	len = rin.padDecrypt(output,len,output2);
//  if(len >= 0)useYourDecryptedText();
//  else decryptError(len);
//

#include "kvi_settings.h"

#if defined(COMPILE_CRYPT_SUPPORT) || defined(Q_MOC_RUN)

#define _MAX_KEY_COLUMNS (256 / 32)
#define _MAX_ROUNDS 14
//#define BITSPERBLOCK        128 /* Default number of bits in a cipher block */
#define MAX_IV_SIZE 16
#define STRICT_ALIGN 0

// We assume that unsigned int is 32 bits long....
typedef unsigned char UINT8;
typedef unsigned int UINT32;
typedef unsigned short UINT16;

#define RIJNDAEL_SUCCESS 0
#define RIJNDAEL_UNSUPPORTED_MODE -1
#define RIJNDAEL_UNSUPPORTED_DIRECTION -2
#define RIJNDAEL_UNSUPPORTED_KEY_LENGTH -3
#define RIJNDAEL_BAD_KEY -4
#define RIJNDAEL_NOT_INITIALIZED -5
#define RIJNDAEL_BAD_DIRECTION -6
#define RIJNDAEL_CORRUPTED_DATA -7

class Rijndael
{
public:
	enum Direction
	{
		Encrypt,
		Decrypt
	};
	enum Mode
	{
		ECB,
		CBC,
		CFB1
	};
	enum KeyLength
	{
		Key16Bytes,
		Key24Bytes,
		Key32Bytes
	};

	Rijndael();
	~Rijndael();

protected:
	enum State
	{
		Valid,
		Invalid
	};

	State m_state;
	Mode m_mode;
	Direction m_direction;
	UINT8 m_initVector[MAX_IV_SIZE];
	UINT32 m_uRounds;
	UINT8 m_expandedKey[_MAX_ROUNDS + 1][4][4];

public:
	// Initializes the crypt session
	// Returns RIJNDAEL_SUCCESS or an error code
	int init(Mode mode, Direction dir, const UINT8 * key, KeyLength keyLen, UINT8 * initVector = nullptr);
	// Input len is in BITS!
	// Encrypts inputLen / 128 blocks of input and puts it in outBuffer
	// outBuffer must be at least inputLen / 8 bytes long.
	// Returns the encrypted buffer length in BITS or an error code < 0 in case of error
	int blockEncrypt(const UINT8 * input, int inputLen, UINT8 * outBuffer, UINT8 * initVector = nullptr);
	// Input len is in BYTES!
	// outBuffer must be at least inputLen + 16 bytes long
	// Returns the encrypted buffer length in BYTES or an error code < 0 in case of error
	int padEncrypt(const UINT8 * input, int inputOctets, UINT8 * outBuffer, UINT8 * initVector = nullptr);
	// Input len is in BITS!
	// outBuffer must be at least inputLen / 8 bytes long
	// Returns the decrypted buffer length in BITS and an error code < 0 in case of error
	int blockDecrypt(const UINT8 * input, int inputLen, UINT8 * outBuffer, UINT8 * initVector = nullptr);
	// Input len is in BYTES!
	// outBuffer must be at least inputLen bytes long
	// Returns the decrypted buffer length in BYTES and an error code < 0 in case of error
	int padDecrypt(const UINT8 * input, int inputOctets, UINT8 * outBuffer, UINT8 * initVector = nullptr);

protected:
	void keySched(UINT8 key[_MAX_KEY_COLUMNS][4]);
	void keyEncToDec();
	void encrypt(const UINT8 a[16], UINT8 b[16]);
	void decrypt(const UINT8 a[16], UINT8 b[16]);
	void updateInitVector(UINT8 * initVector = nullptr);
};

#endif // COMPILE_CRYPT_SUPPORT

#endif // _RIJNDAEL_H_
