/*
								+---------------------------------+
								|                                 |
								|  ***  Standard hash funcs  ***  |
								|                                 |
								|  Copyright  © -tHE SWINe- 2006  |
								|                                 |
								|             Hash.h              |
								|                                 |
								+---------------------------------+
*/

#pragma once
#ifndef __HASH_INCLUDED
#define __HASH_INCLUDED

/**
 *	@file Hash.h
 *	@author -tHE SWINe-
 *	@date 2006
 *	@brief implementation of basic message digest algorithms
 *
 *	@date 2007-12-24
 *
 *	improved linux compatibility by adding posix integer types
 *
 *	@date 2008-03-04
 *
 *	now using Integer.h header
 *
 *	@date 2008-07-04
 *
 *	replaced all occurences of unsigned char with uint8_t
 *
 *	changed CStreamHash::Process_Data(const unit8_t*, int) to
 *	CStreamHash::Process_Data(const void*, int) so there's no need
 *	to type cast data anymore (convenience improvement)
 *
 *	@date 2009-05-04
 *
 *	fixed mixed windows / linux line endings
 *
 *	@date 2009-11-03
 *
 *	Removed TTinyMD5.
 *
 *	Removed padding scheme from CStreamHash and added it to TMD5 and TSHA1,
 *	so hash algorithm with different padding scheme may use CStreamHash
 *	as well (design refiniement). This added Process_FinalBlock() function
 *	to both TMD5 and TSHA1.
 *
 *	Renamed TMD5::Process_Data() and TSHA1::Process_Data() to
 *	TMD5::Process_Block() and TSHA1::Process_Block(), respectively,
 *	changed buffer type from const uint8_t* to const void*.
 *
 *	Added hash_Size_Bits, block_Size_Bits and word_Is_BigEndian enums, along
 *	with _WordType data type to both TMD5 and TSHA1.
 *
 *	CStreamHash template has now only a single parameter, type of hash object.
 *	This breaks old code, but it's really easy to fix (just remove additional
 *	parameters).
 *
 *	@date 2010-07-22
 *
 *	Changed type of n_length_bytes parameter in CStreamHash::Process_Data() from
 *	int to size_t. This shouldn't affect any legacy code.
 *
 *	@date 2010-08-10
 *
 *	fixed signed-unsigned mismatch warning, found when compiling with
 *	vs2008 (carried in on 2010-07-22).
 *
 *	@date 2010-11-11
 *
 *	Changed type of n_index parameter in TMD5::operator []() and in
 *	TSHA1::operator []() from int to size_t (fixes some warnings when
 *	compiling for x64).
 *
 *	@date 2011-10-21
 *
 *	Added TTigerHash and TTiger2Hash classes.
 *
 *	@date 2012-06-11
 *
 *	Unified x64 detection macros.
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

#include "Integer.h"
#include <string.h> // memcpy

/**
 *	@def __MD5_UNROLL_LOOPS
 *
 *	@brief enables loop unrolling in TMD5::Process_Block(), enabling faster
 *		data processing on some machines (some optimizations can be made, but
 *		code is significantly larger)
 */
#define __MD5_UNROLL_LOOPS

/**
 *	@def __SHA1_UNROLL_LOOPS
 *
 *	@brief enables loop unrolling in TSHA1::Process_Block(), enabling faster
 *		data processing on some machines (some optimizations can be made, but
 *		code is significantly larger)
 */
#define __SHA1_UNROLL_LOOPS

/**
 *	@brief MD5 class
 *
 *	Class, implementing the Message Digest 5 (MD5) algorithm by Ronald Rivest
 *		("RSA Data Security, Inc. MD5 Message-Digest Algorithm", RFC1321).
 *		Cooperates with <tt>CStreamHash<TMD5></tt>.
 *
 *	@note MD5 has been broken for some time, meaning it's possible to find two
 *		different messages which have the same MD5 hash. Therefore, use of MD5
 *		in security applications should be re-considered. However, it is still
 *		perfectly good hash in areas where the birthday attack isn't anticipated.
 */
struct TMD5 {
public:
	/**
	 *	@brief hash algorithm parameters
	 */
	enum {
		hash_Size_Bits = 128, /**< size of hash, in bits */
		block_Size_Bits = 512, /**< size of input blocks, in bits */
		word_Is_BigEndian = false /**< endiannes */
	};

	/**
	 *	@brief word data type
	 */
	typedef uint32_t _WordType;

protected:
	uint32_t m_n_hash[4]; /**< MD5 data */
	uint64_t m_n_total_length_bits; /**< length of processed data, in bits */

public:
	/**
	 *	@brief default constructor
	 *
	 *	Initializes MD5 to the initial value (0x67452301efcdab8998badcfe10325476).
	 *
	 *	@note This is not MD5 value of an empty string, for an empty string, padding
	 *		mechanism is applied (a single 1 bit and 511 zero bits (64-bit length +
	 *		padding) must be processed first), yielding MD5 of an empty string
	 *		(0xd41d8cd98f00b204e9800998ecf8427e).
	 */
	TMD5();

	/**
	 *	@brief converts the hash to a null-terminated string
	 *	@return Returns null-terminated string, containing hexadecimal
	 *		value of this hash (without the "0x" prefix).
	 */
	const char *p_s_ToString() const;

	/**
	 *	@brief copy-operator
	 *
	 *	Copies contents of r_t_md5 to this, and returns reference to this.
	 *
	 *	@param[in] r_t_md5 is source MD5 to be copied
	 *
	 *	@return Returns reference to this (for multiple-assignment).
	 */
	TMD5 &operator =(const TMD5 &r_t_md5);

	/**
	 *	@brief equality operator
	 *
	 *	Compares contents of this to r_t_md5.
	 *
	 *	@param[in] r_t_md5 is reference MD5 to be compared to
	 *
	 *	@return Returns true if this contains the same MD5
	 *		as r_t_md5 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just MD5 value is compared, not amount
	 *		of input data taken to calculate that MD5.
	 */
	bool operator ==(const TMD5 &r_t_md5) const;

	/**
	 *	@brief less-than operator
	 *
	 *	Compares contents of this to r_t_md5.
	 *
	 *	@param[in] r_t_md5 is reference MD5 to be compared to
	 *
	 *	@return Returns true if this contains numerically smaller MD5
	 *		than r_t_md5 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just MD5 value is compared, not amount
	 *		of input data taken to calculate that MD5.
	 */
	bool operator <(const TMD5 &r_t_md5) const;

	/**
	 *	@brief non-equality operator
	 *
	 *	Compares contents of this to r_t_md5.
	 *
	 *	@param[in] r_t_md5 is reference MD5 to be compared to
	 *
	 *	@return Returns false if this contains the same MD5
	 *		as r_t_md5 does, otherwise returns true.
	 *
	 *	@note For the sake of clarity, just MD5 value is compared, not amount
	 *		of input data taken to calculate that MD5.
	 */
	inline bool operator !=(const TMD5 &r_t_md5) const
	{
		return !(*this == r_t_md5);
	}

	/**
	 *	@brief greater-than operator
	 *
	 *	Compares contents of this to r_t_md5.
	 *
	 *	@param[in] r_t_md5 is reference MD5 to be compared to
	 *
	 *	@return Returns true if this contains numerically greater MD5
	 *		than r_t_md5 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just MD5 value is compared, not amount
	 *		of input data taken to calculate that MD5.
	 */
	inline bool operator >(const TMD5 &r_t_md5) const
	{
		return r_t_md5 < *this;
	}

	/**
	 *	@brief greater-than-equal operator
	 *
	 *	Compares contents of this to r_t_md5.
	 *
	 *	@param[in] r_t_md5 is reference MD5 to be compared to
	 *
	 *	@return Returns true if this contains numerically greater or equal MD5
	 *		as r_t_md5 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just MD5 value is compared, not amount
	 *		of input data taken to calculate that MD5.
	 */
	inline bool operator >=(const TMD5 &r_t_md5) const
	{
		return !(*this < r_t_md5);
	}

	/**
	 *	@brief less-than-equal operator
	 *
	 *	Compares contents of this to r_t_md5.
	 *
	 *	@param[in] r_t_md5 is reference MD5 to be compared to
	 *
	 *	@return Returns true if this contains numerically smaller or equal MD5
	 *		as r_t_md5 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just MD5 value is compared, not amount
	 *		of input data taken to calculate that MD5.
	 */
	inline bool operator <=(const TMD5 &r_t_md5) const
	{
		return !(r_t_md5 < *this);
	}

	/**
	 *	@brief access to MD5 data
	 *
	 *	Returns reference to MD5 doubleword with zero-based index n_index.
	 *
	 *	@param[in] n_index is zero-based dword index in range 0 to 3
	 *
	 *	@return Returns reference to selected doubleword.
	 */
	inline _WordType &operator [](size_t n_index)
	{
		return m_n_hash[n_index];
	}

	/**
	 *	@brief access to MD5 data
	 *
	 *	Returns value of MD5 doubleword with zero-based index n_index.
	 *
	 *	@param[in] n_index is zero-based dword index in range 0 to 3
	 *
	 *	@return Returns value of selected doubleword.
	 */
	inline _WordType operator [](size_t n_index) const
	{
		return m_n_hash[n_index];
	}

	/**
	 *	@brief calculate MD5 from data
	 *
	 *	Processes block of 512 bits of data (512 bits equals 64 bytes). To calculate
	 *		MD5 of (longer) sequences, Process_Block() is called repeatedly, followed
	 *		by a single call to Process_FinalBlock() which processes final block,
	 *		possibly shorter than 512 bits, and adds padding / length as specified in
	 *		the MD5 specification.
	 *
	 *	@param[in] p_data contains 512 bits (64 bytes) of input data
	 *	@param[in] n_size_bytes is size of input data in bytes, must be 64.
	 *		This parameter is used for validation only.
	 */
	void Process_Block(const void *p_data, int n_size_bytes);

	/**
	 *	@brief calculate MD5 from data
	 *
	 *	Calculates final MD5 from the last block of data (@c less than 512 bits).
	 *		After calling this function, operator []() can be used to read MD5 value.
	 *
	 *	@param[in] p_data contains final block of input data
	 *	@param[in] n_size_bytes is size of input data in bytes, less or equal to 64
	 *
	 *	@note Once Process_FinalBlock() was called, Process_Block() and Process_FinalBlock()
	 *		may not be called again. This is not checked in any way. To calculate MD5 of
	 *		another data, it's necessary to create a new TMD5 object.
	 */
	void Process_FinalBlock(const void *p_data, int n_size_bytes);

private:
	static inline uint32_t n_ByteSwap32(uint32_t x);
#ifndef __MD5_UNROLL_LOOPS
	static inline uint32_t n_LeftRot(uint32_t x, int y);
#endif // __MD5_UNROLL_LOOPS
};

/**
 *	@brief SHA-1 class
 *
 *	This class implements the Secure Hashing Algorithm 1 (SHA-1), as
 *		defined in FIPS PUB 180-1 published April 17, 1995.
 *		Cooperates with <tt>CStreamHash<TSHA1></tt>.
 *
 *	@note SHA-1 has been considered unsafe for some time, however SHA-2
 *		is rather similar, sharing the same weaknesses as SHA-1, and SHA-3
 *		is still being developed at the time (as of 2009-11), so there's
 *		no better alternative yet.
 */
struct TSHA1 {
public:
	/**
	 *	@brief hash algorithm parameters
	 */
	enum {
		hash_Size_Bits = 160, /**< size of hash, in bits */
		block_Size_Bits = 512, /**< size of input blocks, in bits */
		word_Is_BigEndian = true /**< endiannes */
	};

	/**
	 *	@brief word data type
	 */
	typedef uint32_t _WordType;

protected:
	uint32_t m_n_hash[5]; /**< SHA-1 data */
	uint64_t m_n_total_length_bits; /**< length of processed data, in bits */

public:
	/**
	 *	@brief default constructor
	 *
	 *	Initializes SHA-1 to the initial value (0x67452301EFCDAB8998BADCFE10325476C3D2E1F0).
	 *
	 *	@note This is not SHA-1 value of an empty string, for an empty string, padding
	 *		mechanism is applied as described in specification, yielding SHA-1 of an
	 *		empty string (0xda39a3ee5e6b4b0d3255bfef95601890afd80709).
	 */
	TSHA1();

	/**
	 *	@brief converts the hash to a null-terminated string
	 *	@return Returns null-terminated string, containing hexadecimal
	 *		value of this hash (without the "0x" prefix).
	 */
	const char *p_s_ToString() const;

	/**
	 *	@brief copy-operator
	 *
	 *	Copies contents of r_t_sha1 to this, and returns reference to this.
	 *
	 *	@param[in] r_t_sha1 is source SHA-1 to be copied
	 *
	 *	@return Returns reference to this (for multiple-assignment).
	 */
	TSHA1 &operator =(const TSHA1 &r_t_sha1);

	/**
	 *	@brief equality operator
	 *
	 *	Compares contents of this to r_t_sha1.
	 *
	 *	@param[in] r_t_sha1 is reference SHA-1 to be compared to
	 *
	 *	@return Returns true if this contains the same SHA-1
	 *		as r_t_sha1 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just SHA-1 value is compared, not amount
	 *		of input data taken to calculate that SHA-1.
	 */
	bool operator ==(const TSHA1 &r_t_sha1) const;

	/**
	 *	@brief less-than operator
	 *
	 *	Compares contents of this to r_t_sha1.
	 *
	 *	@param[in] r_t_sha1 is reference SHA-1 to be compared to
	 *
	 *	@return Returns true if this contains numerically smaller SHA-1
	 *		than r_t_sha1 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just SHA-1 value is compared, not amount
	 *		of input data taken to calculate that SHA-1.
	 */
	bool operator <(const TSHA1 &r_t_sha1) const;

	/**
	 *	@brief non-equality operator
	 *
	 *	Compares contents of this to r_t_sha1.
	 *
	 *	@param[in] r_t_sha1 is reference SHA-1 to be compared to
	 *
	 *	@return Returns false if this contains the same SHA-1
	 *		as r_t_sha1 does, otherwise returns true.
	 *
	 *	@note For the sake of clarity, just SHA-1 value is compared, not amount
	 *		of input data taken to calculate that SHA-1.
	 */
	inline bool operator !=(const TSHA1 &r_t_sha1) const
	{
		return !(*this == r_t_sha1);
	}

	/**
	 *	@brief greater-than operator
	 *
	 *	Compares contents of this to r_t_sha1.
	 *
	 *	@param[in] r_t_sha1 is reference SHA-1 to be compared to
	 *
	 *	@return Returns true if this contains numerically greater SHA-1
	 *		than r_t_sha1 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just SHA-1 value is compared, not amount
	 *		of input data taken to calculate that SHA-1.
	 */
	inline bool operator >(const TSHA1 &r_t_sha1) const
	{
		return r_t_sha1 < *this;
	}

	/**
	 *	@brief greater-than-equal operator
	 *
	 *	Compares contents of this to r_t_sha1.
	 *
	 *	@param[in] r_t_sha1 is reference SHA-1 to be compared to
	 *
	 *	@return Returns true if this contains numerically greater or equal SHA-1
	 *		as r_t_sha1 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just SHA-1 value is compared, not amount
	 *		of input data taken to calculate that SHA-1.
	 */
	inline bool operator >=(const TSHA1 &r_t_sha1) const
	{
		return !(*this < r_t_sha1);
	}

	/**
	 *	@brief less-than-equal operator
	 *
	 *	Compares contents of this to r_t_sha1.
	 *
	 *	@param[in] r_t_sha1 is reference SHA-1 to be compared to
	 *
	 *	@return Returns true if this contains numerically smaller or equal SHA-1
	 *		as r_t_sha1 does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just SHA-1 value is compared, not amount
	 *		of input data taken to calculate that SHA-1.
	 */
	inline bool operator <=(const TSHA1 &r_t_sha1) const
	{
		return !(r_t_sha1 < *this);
	}

	/**
	 *	@brief access to SHA-1 data
	 *
	 *	Returns reference to SHA-1 doubleword with zero-based index n_index.
	 *
	 *	@param[in] n_index is zero-based dword index in range 0 to 4
	 *
	 *	@return Returns reference to selected doubleword.
	 */
	inline uint32_t &operator [](size_t n_index)
	{
		return m_n_hash[n_index];
	}

	/**
	 *	@brief access to SHA-1 data
	 *
	 *	Returns value of SHA-1 doubleword with zero-based index n_index.
	 *
	 *	@param[in] n_index is zero-based dword index in range 0 to 4
	 *
	 *	@return Returns value of selected doubleword.
	 */
	inline uint32_t operator [](size_t n_index) const
	{
		return m_n_hash[n_index];
	}

	/**
	 *	@brief calculate SHA-1 from data
	 *
	 *	Processes block of 512 bits of data (512 bits equals 64 bytes). To calculate
	 *		SHA-1 of (longer) sequences, Process_Block() is called repeatedly, followed
	 *		by a single call to Process_FinalBlock() which processes final block,
	 *		possibly shorter than 512 bits, and adds padding / length as specified in
	 *		the SHA-1 specification.
	 *
	 *	@param[in] p_data contains 512 bits (64 bytes) of input data
	 *	@param[in] n_size_bytes is size of input data in bytes, must be 64.
	 *		This parameter is used for validation only.
	 */
	void Process_Block(const void *p_data, int n_size_bytes);

	/**
	 *	@brief calculate SHA-1 from data
	 *
	 *	Calculates final SHA-1 from the last block of data (@c less than 512 bits).
	 *		After calling this function, operator []() can be used to read SHA-1 value.
	 *
	 *	@param[in] p_data contains final block of input data
	 *	@param[in] n_size_bytes is size of input data in bytes, less or equal to 64
	 *
	 *	@note Once Process_FinalBlock() was called, Process_Block() and Process_FinalBlock()
	 *		may not be called again. This is not checked in any way. To calculate SHA-1 of
	 *		another data, it's necessary to create a new TSHA1 object.
	 */
	void Process_FinalBlock(const void *p_data, int n_size_bytes);

private:
	static inline uint32_t n_EndianConversion(uint32_t x);
};

/**
 *	@brief Tiger hash class
 *
 *	This class implements the Tiger, as defined in Ross Anderson and Eli Biham,
 *	"Tiger: A Fast New Hash Function", proceedings of Fast Software Encryption 3,
 *	Cambridge, 1995.
 *
 *	Cooperates with <tt>CStreamHash<TTigerHash></tt>.
 */
struct TTigerBase {
	/**
	 *	@brief hash algorithm parameters
	 */
	enum {
		hash_Size_Bits = 192, /**< size of hash, in bits */
		block_Size_Bits = 512, /**< size of input blocks, in bits */
		word_Is_BigEndian = false, /**< endiannes */

		tiger_Pass_Num = 3 /**< @brief the number of passes of the hash function @note Three passes are recommended, use four passes when you need extra security. */
	};

	/**
	 *	@brief word data type
	 */
	typedef uint64_t _WordType;

protected:
	uint64_t m_n_hash[3]; /**< Tiger hash data */
	uint64_t m_n_total_length_bits; /**< length of processed data, in bits */

public:
	/**
	 *	@brief default constructor
	 *
	 *	Initializes Tiger to the initial value (0x0123456789abcdeffedcba9876543210f096a5b4c3b2e187).
	 *
	 *	@note This is not Tiger value of an empty string, for an empty string, padding
	 *		mechanism is applied as described in specification, yielding Tiger of an
	 *		empty string (0x24f0130c63ac933216166e76b1bb925ff373de2d49584e7a).
	 */
	TTigerBase();

	/**
	 *	@brief copy-operator
	 *
	 *	Copies contents of r_t_tiger to this, and returns reference to this.
	 *
	 *	@param[in] r_t_tiger is source Tiger to be copied
	 *
	 *	@return Returns reference to this (for multiple-assignment).
	 */
	TTigerBase &operator =(const TTigerBase &r_t_tiger);

	/**
	 *	@brief converts the hash to a null-terminated string
	 *	@return Returns null-terminated string, containing hexadecimal
	 *		value of this hash (without the "0x" prefix).
	 */
	const char *p_s_ToString() const;

	/**
	 *	@brief equality operator
	 *
	 *	Compares contents of this to r_t_tiger.
	 *
	 *	@param[in] r_t_tiger is reference Tiger to be compared to
	 *
	 *	@return Returns true if this contains the same Tiger
	 *		as r_t_tiger does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just Tiger value is compared, not amount
	 *		of input data taken to calculate that Tiger.
	 */
	bool operator ==(const TTigerBase &r_t_tiger) const;

	/**
	 *	@brief less-than operator
	 *
	 *	Compares contents of this to r_t_tiger.
	 *
	 *	@param[in] r_t_tiger is reference Tiger to be compared to
	 *
	 *	@return Returns true if this contains numerically smaller Tiger
	 *		than r_t_tiger does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just Tiger value is compared, not amount
	 *		of input data taken to calculate that Tiger.
	 */
	bool operator <(const TTigerBase &r_t_tiger) const;

	/**
	 *	@brief non-equality operator
	 *
	 *	Compares contents of this to r_t_tiger.
	 *
	 *	@param[in] r_t_tiger is reference Tiger to be compared to
	 *
	 *	@return Returns false if this contains the same Tiger
	 *		as r_t_tiger does, otherwise returns true.
	 *
	 *	@note For the sake of clarity, just Tiger value is compared, not amount
	 *		of input data taken to calculate that Tiger.
	 */
	inline bool operator !=(const TTigerBase &r_t_tiger) const
	{
		return !(*this == r_t_tiger);
	}

	/**
	 *	@brief greater-than operator
	 *
	 *	Compares contents of this to r_t_tiger.
	 *
	 *	@param[in] r_t_tiger is reference Tiger to be compared to
	 *
	 *	@return Returns true if this contains numerically greater Tiger
	 *		than r_t_tiger does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just Tiger value is compared, not amount
	 *		of input data taken to calculate that Tiger.
	 */
	inline bool operator >(const TTigerBase &r_t_tiger) const
	{
		return r_t_tiger < *this;
	}

	/**
	 *	@brief greater-than-equal operator
	 *
	 *	Compares contents of this to r_t_tiger.
	 *
	 *	@param[in] r_t_tiger is reference Tiger to be compared to
	 *
	 *	@return Returns true if this contains numerically greater or equal Tiger
	 *		as r_t_tiger does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just Tiger value is compared, not amount
	 *		of input data taken to calculate that Tiger.
	 */
	inline bool operator >=(const TTigerBase &r_t_tiger) const
	{
		return !(*this < r_t_tiger);
	}

	/**
	 *	@brief less-than-equal operator
	 *
	 *	Compares contents of this to r_t_tiger.
	 *
	 *	@param[in] r_t_tiger is reference Tiger to be compared to
	 *
	 *	@return Returns true if this contains numerically smaller or equal Tiger
	 *		as r_t_tiger does, otherwise returns false.
	 *
	 *	@note For the sake of clarity, just Tiger value is compared, not amount
	 *		of input data taken to calculate that Tiger.
	 */
	inline bool operator <=(const TTigerBase &r_t_tiger) const
	{
		return !(r_t_tiger < *this);
	}

	/**
	 *	@brief access to Tiger data
	 *
	 *	Returns reference to Tiger doubleword with zero-based index n_index.
	 *
	 *	@param[in] n_index is zero-based dword index in range 0 to 4
	 *
	 *	@return Returns reference to selected doubleword.
	 */
	inline uint64_t &operator [](size_t n_index)
	{
		return m_n_hash[n_index];
	}

	/**
	 *	@brief access to Tiger data
	 *
	 *	Returns value of Tiger doubleword with zero-based index n_index.
	 *
	 *	@param[in] n_index is zero-based dword index in range 0 to 4
	 *
	 *	@return Returns value of selected doubleword.
	 */
	inline uint64_t operator [](size_t n_index) const
	{
		return m_n_hash[n_index];
	}

	/**
	 *	@brief calculate Tiger from data
	 *
	 *	Processes block of 512 bits of data (512 bits equals 64 bytes). To calculate
	 *		Tiger of (longer) sequences, Process_Block() is called repeatedly, followed
	 *		by a single call to Process_FinalBlock() which processes final block,
	 *		possibly shorter than 512 bits, and adds padding / length as specified in
	 *		the Tiger specification.
	 *
	 *	@param[in] p_data contains 512 bits (64 bytes) of input data
	 *	@param[in] n_size_bytes is size of input data in bytes, must be 64.
	 *		This parameter is used for validation only.
	 */
	void Process_Block(const void *p_data, int n_size_bytes);

protected:
	static inline uint64_t n_ByteSwap64(uint64_t n_x);
};

template <const int n_padding_byte = 0x01>
struct TTigerHashTemplate : public TTigerBase {
public:
	enum {
		tiger_PaddingByte = n_padding_byte /**< @brief Tiger padding byte (0x01 for tiger or 0x80 for tiger2) */
	};

public:
	/**
	 *	@brief calculate Tiger from data
	 *
	 *	Calculates final Tiger from the last block of data (@c less than 512 bits).
	 *		After calling this function, operator []() can be used to read Tiger value.
	 *
	 *	@param[in] p_data contains final block of input data
	 *	@param[in] n_size_bytes is size of input data in bytes, less or equal to 64
	 *
	 *	@note Once Process_FinalBlock() was called, Process_Block() and Process_FinalBlock()
	 *		may not be called again. This is not checked in any way. To calculate Tiger of
	 *		another data, it's necessary to create a new TSHA1 object.
	 */
	void Process_FinalBlock(const void *p_data, int n_size_bytes)
	{
		_ASSERTE(n_size_bytes < 64);
		_ASSERTE(m_n_total_length_bits <= UINT64_MAX - n_size_bytes * 8);

		m_n_total_length_bits += n_size_bytes * 8;

		uint64_t l = m_n_total_length_bits;
		// convert length to big endian

		uint8_t p_temp_buffer[64]; // 64 x 8b = 512b
		memcpy(p_temp_buffer, p_data, n_size_bytes);
		if(n_size_bytes >= (512 - 64) / 8) {
			p_temp_buffer[n_size_bytes] = tiger_PaddingByte; // add a single 1 bit
			memset(p_temp_buffer + n_size_bytes + 1, 0x00, 63 - n_size_bytes); // follow by zeros
			Process_Block(p_temp_buffer, sizeof(p_temp_buffer) / sizeof(p_temp_buffer[0]));
			// data 1 0*

			m_n_total_length_bits -= 512; // do not count padding into the length

			memset(p_temp_buffer, 0x00, 64 - sizeof(uint64_t)); // begin with zeros
			memcpy(p_temp_buffer + 64 - sizeof(uint64_t), &l, sizeof(uint64_t)); // append length
			Process_Block(p_temp_buffer, sizeof(p_temp_buffer) / sizeof(p_temp_buffer[0]));
			// 0* length
		} else {
			p_temp_buffer[n_size_bytes] = tiger_PaddingByte; // add a single 1 bit
			memset(p_temp_buffer + n_size_bytes + 1, 0x00, 63 - n_size_bytes); // follow by zeros
			memcpy(p_temp_buffer + 64 - sizeof(uint64_t), &l, sizeof(uint64_t)); // append length
			Process_Block(p_temp_buffer, sizeof(p_temp_buffer) / sizeof(p_temp_buffer[0]));
			// data 1 0* length
		}
		// padd with one and zeros, append length
	}
};

/**
 *	@brief Tiger hash class
 *
 *	This class implements the Tiger, as defined in Ross Anderson and Eli Biham,
 *	"Tiger: A Fast New Hash Function", proceedings of Fast Software Encryption 3,
 *	Cambridge, 1995.
 *
 *	Cooperates with <tt>CStreamHash<TTigerHash></tt>.
 */
typedef TTigerHashTemplate<0x01> TTigerHash;

/**
 *	@brief Tiger2 hash class
 *
 *	This class implements the Tiger, as defined in Ross Anderson and Eli Biham,
 *	"Tiger: A Fast New Hash Function", proceedings of Fast Software Encryption 3,
 *	Cambridge, 1995.
 *
 *	Cooperates with <tt>CStreamHash<TTiger2Hash></tt>.
 */
typedef TTigerHashTemplate<0x80> TTiger2Hash;

/**
 *	@brief template for calculating digests of arbitrary length messages
 *
 *	The above message digest classes can only process data with size of multiple
 *		of 512 bits, and require specific padding scheme to be applied. This simple
 *		template provides convenient way of calculating hashes of arbitrary length
 *		messages.
 *
 *	@param THash is class with public member functions:
 *		<tt>void Process_Block(const uint32_t *p_data, int n_size_bytes)</tt> and
 *		<tt>void Process_FinalBlock(const uint32_t *p_data, int n_size_bytes)</tt>
 *		and with public enum <tt>block_Size_Bits</tt>, containing block size in
 *		bits (such as TMD5 or TSHA1)
 */
template <class THash>
class CStreamHash {
public:
	/**
	 *	@brief type of hash (template parameter)
	 */
	typedef THash _TyHash;

private:
	enum {
		n_blocksize_bits = THash::block_Size_Bits,
		n_blocksize_bytes = n_blocksize_bits / 8
	};

	uint8_t m_p_prev_data[n_blocksize_bytes];
	size_t m_n_prev_length; // % n_blocksize_bytes

	THash m_t_hash;

	bool m_b_processing;

public:
	/**
	 *	@brief default constructor
	 *
	 *	Initializes hash and enables data processing.
	 */
	CStreamHash()
		:m_n_prev_length(0), m_t_hash(), m_b_processing(true)
	{
		_ASSERTE(!(n_blocksize_bits % 8) && n_blocksize_bits >= 8);
	}

	/**
	 *	@brief begins calculation of a new hash
	 *
	 *	Resets hash and reenables data processing.
	 */
	void Reset()
	{
		m_t_hash = THash();
		m_n_prev_length = 0;
		m_b_processing = true;
	}

	/**
	 *	@brief main processing function
	 *
	 *	Processes data p_data with length n_length_bytes bytes. This may be called repeatedly
	 *		with arbitrary amounts of data. After all data were processed, call t_Result()
	 *		to get hash.
	 *
	 *	@param[in] p_data is pointer to input data
	 *	@param[in] n_length_bytes is size of input data, in bytes
	 *
	 *	@return Returns true if processing is enabled (i.e. t_Result() hasn't still
	 *		called yet or Reset() has been called already), otherwise false.
	 *
	 *	@note After calling t_Result(), this fails with false until Reset() is called.
	 */
	bool Process_Data(const void *p_data, size_t n_length_bytes)
	{
		if(!m_b_processing)
			return false;

		const uint8_t *_p_data = (const uint8_t*)p_data;

		if(m_n_prev_length) {
			_ASSERTE(n_blocksize_bytes > m_n_prev_length);
			size_t n_use = n_blocksize_bytes - m_n_prev_length;
			if(n_use <= n_length_bytes) {
				memcpy(m_p_prev_data + m_n_prev_length, _p_data, n_use * sizeof(uint8_t));
				// there is enough data to form 512 bit chunk

				m_t_hash.Process_Block(m_p_prev_data, n_blocksize_bytes);
				// process it

				_p_data += n_use;
				n_length_bytes -= n_use;
				// skip processed data

				m_n_prev_length = 0;
				// we've processed all
			} else {
				memcpy(m_p_prev_data + m_n_prev_length, _p_data, n_length_bytes * sizeof(uint8_t));
				m_n_prev_length += n_length_bytes;
				// just add what we got

				return true;
			}
		}
		// if there wasnt enough data last time to form 512 bit chunk, padd it

		for(const uint8_t *p_end = _p_data + n_length_bytes - n_length_bytes % n_blocksize_bytes;
		   _p_data != p_end; _p_data += n_blocksize_bytes)
			m_t_hash.Process_Block(_p_data, n_blocksize_bytes);
		// main processing loop

		if(m_n_prev_length = n_length_bytes % n_blocksize_bytes)
			memcpy(m_p_prev_data, _p_data, m_n_prev_length * sizeof(uint8_t));
		// store any data we didn't use

		return true;
	}

	/**
	 *	@brief gets hash value
	 *
	 *	Sends last piece of data to processing using THash::Process_FinalBlock() and
	 *		disables data processing - after calling this, Process_Data() can't be called
	 *		(fails with false) until Reset() is called.
	 *
	 *	@return Returns value of hash.
	 *
	 *	@note After calling this, Process_Data() fails with false until Reset() is called.
	 */
	const THash &t_Result()
	{
		if(m_b_processing) {
			_ASSERTE(m_n_prev_length <= INT_MAX);
			m_t_hash.Process_FinalBlock(m_p_prev_data, int(m_n_prev_length));
			// process the final block, padd data, etc.

			m_b_processing = false;
			// we're no longer processing
		}
		// process padding

		return m_t_hash;
	}
};

#endif // !__HASH_INCLUDED
