/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss or reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include <crypto/hash.h>
#include "crypto.h"

/****************************************************************************/
/*						User API											*/
/****************************************************************************/

/** @addtogroup hash */
/** @{ */

/**
*	Initialise hash calculations.
*
*	The function prepares for a hash calculation. The hash state,
*	passed as a pointer to a BC_HASH structure is prepared to be ready
*	to receive data of which the hash should be calculated.
*	The second argument is a pointer to an opaque structure. That
*	structure defines the hash function to be used. Four such structures
*	are defined by the library and they are <tt>\#define</tt>-d:
*	- BC_HASH_MD5<br>
*		The hash will use the MD5 algorithm, resulting in a
*		16-byte hash value.
*	- BC_HASH_MD5_FAST<br>
*		The has will use the MD5 algorithm, but the hash
*		calculating core is faster (and larger). Apart from
*		the code size and the speed there's no difference
*		between BC_HASH_MD5 and BC_HASH_MD5_FAST.
*	- BC_HASH_SHA1<br>
*		The hash will use the SHA-1 algorithm, resulting in
*		a 20-byte hash value.
*	- BC_HASH_MD5_FAST<br>
*		The has will use the SHA-1 algorithm, but the hash
*		calculating core is somewhat faster (and much larger).
*		Apart from the code size and the speed there's no
*		difference between BC_HASH_SHA1 and BC_HASH_SHA1_FAST.
*
*	@param	hash		Pointer to a BC_HASH structure, the hash state.
*	@param	func		Pointer to an opaque structure that selects the
*						hash function. Use the predefined values described
*						above.
*/

void	BC_HashInit( BC_HASH *hash, const struct bc_hfun_s *func )
{
	hash->f = (struct bc_hfun_s *) func;
	hash->n = 0;
	(*hash->f->startf)( hash->s );
}

/**
*	Feeds data into a hash.
*
*	The function takes an already initialised hash state descriptor,
*	a pointer to some data and the length of the data and upgrades
*	the hash state with the data. The data is not changed.
*	Subsequent calls to this function are equivalent to a single call
*	with the data for the separate calls concatenated, that is,
*	@code
*	BC_HashData( &myhash, "abc", 3 );
*	BC_HashData( &myhash, "def", 3 );
*	@endcode
*	is equivalent to
*	@code
*	BC_HashData( &myhash, "abcdef", 6 );
*	@endcode
*
*	@param	hash		Pointer to a BC_HASH structure, the hash state.
*	@param	data		Pointer to the data to feed to the hash.
*	@param	dlen		The length of the data.
*/

void	BC_HashData( BC_HASH *hash, const void *data, unsigned dlen )
{
unsigned int	num, wrd, off, sft;
unsigned char	*ptr;

	// Work out where are we in the buffer. The hash block has a length
	// field, which counts bits, that is what we'll get. We should also
	// load the latest partial data word from the buffer

	ptr = (unsigned char *) data;
	num = dlen;
	off = (((unsigned) hash->n) >> 3 ) & 0x3f;
	sft = ( off & 3 ) << 3;
	off = off >> 2;
	wrd = hash->d[ off ];

	// Copy the data so that it is in the required endianness.

	if ( hash->f->little ) {

		while ( num-- ) {

			if ( ! sft )

				wrd = *ptr++;
			else
				wrd |= *ptr++ << sft;

			if ( ! ( sft = ( sft + 8 ) & 31 ) ) {

				hash->d[ off ] = wrd;

				if ( ! ( off = ( off + 1 ) & 15 ) ) {

					// Buffer is full, update the hash

					(*hash->f->blockf)( hash->s, hash->d );
				}
			}
		}
	}
	else {

		sft = 24 - sft;

		while ( num-- ) {

			if ( sft == 24 )

				wrd = *ptr++ << 24;
			else
				wrd |= *ptr++ << sft;

			if ( ( sft = ( sft - 8 ) & 31 ) == 24 ) {

				hash->d[ off ] = wrd;

				if ( ! ( off = ( off + 1 ) & 15 ) ) {

					// Buffer is full, update the hash

					(*hash->f->blockf)( hash->s, hash->d );
				}
			}
		}
	}

	hash->d[ off ] = wrd;
	hash->n += dlen << 3;
}

/**
*	Calculate the hash value of the data fed to the hash function so far.
*
*	The function calculates the actual value of an MD5 or SHA1 hash, on the
*	data that has been fed into the hash state so far.
*	The final hash value is stored in the result buffer. For MD5 the buffer
*	must be at least 16 bytes long, for SHA1 it must be at least 20 bytes long.
*	The hash state is not changed, therefore further data can be fed to the
*	hash. This allows the calculation of hash values on portions of the data.
*
*	@param	hash	The hash state, which is <i>not</i> modified.
*	@param	data	Pointer to the result buffer. For MD5 it must be 16,
*					for SHA1 20 bytes long.
*/

void	BC_HashDone( const BC_HASH *hash, void *data )
{
unsigned int	tmp[ 16 ];
unsigned int	var[  5 ];
unsigned int	off, mod;
unsigned char	*ptr;

	// Copy the hash state

	var[ 0 ] = hash->s[ 0 ];
	var[ 1 ] = hash->s[ 1 ];
	var[ 2 ] = hash->s[ 2 ];
	var[ 3 ] = hash->s[ 3 ];
	var[ 4 ] = hash->s[ 4 ];

	// Copy the current state

	for ( off = 16 ; off-- ;  ) tmp[ off ] = hash->d[ off ];

	off = (((unsigned) hash->n) >> 3 ) & 0x3f;
	mod = hash->f->little ? 0 : 3;

	// We must add a 0x80 sentinel after the data

	ptr = (unsigned char *) tmp;
	ptr[ off++ ^ mod ] = 0x80;

	if ( off > 56 ) {

		// We will need an other block, zero the rest and update the state

		while ( off < 64 ) ptr[ off++ ^ mod ] = 0;
		(*hash->f->blockf)( var, tmp );
		off = 0;
	}

	// Zero the block up until the 56th byte

	while ( off < 56 && ( off & 3 ) ) ptr[ off++ ^ mod ] = 0;
	off >>= 2;
	ptr += 56;
	while ( off < 14 ) tmp[ off++ ] = 0;

	// Store the length. The byte order depends on the the endianness of
	// the hash, but does not depend on the endianness of the machine.

	if ( hash->f->little ) {

		off = hash->n >> 32;
		mod = hash->n;
	}
	else {

		mod = hash->n >> 32;
		off = hash->n;
	}

	*ptr++ = mod;
	*ptr++ = mod >> 8;
	*ptr++ = mod >> 16;
	*ptr++ = mod >> 24;
	*ptr++ = off;
	*ptr++ = off >> 8;
	*ptr++ = off >> 16;
	*ptr++ = off >> 24;

	// Calculate the final hash

	(*hash->f->blockf)( var, tmp );

	// Now we have to store the result, in the proper endianness

	ptr = (unsigned char *) data;

	if ( hash->f->little ) {

		for ( off = 0 ; off < (unsigned) hash->f->length / 4; off++ ) {

			mod = var[ off ];
			*ptr++ = mod;
			*ptr++ = mod >> 8;
			*ptr++ = mod >> 16;
			*ptr++ = mod >> 24;
		}
	}
	else {

		for ( off = 0 ; off < (unsigned) hash->f->length / 4; off++ ) {

			mod = var[ off ];
			*ptr++ = mod >> 24;
			*ptr++ = mod >> 16;
			*ptr++ = mod >> 8;
			*ptr++ = mod;
		}
	}
}

/** @} */

