/*
 * sha1.c
 *
 * (C) Copyright 2007-2009 Andre McCurdy, NXP Semiconductors
 *
 * 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/nxp_sha.h>

#define K1 0x5A827999U
#define K2 0x6ED9EBA1U
#define K3 0x8F1BBCDCU
#define K4 0xCA62C1D6U


void nxp_SHA1_Init (NXP_SHA_CTX *context)
{
	context->bytesHandled = 0;
	context->state[0] = 0x67452301U;
	context->state[1] = 0xEFCDAB89U;
	context->state[2] = 0x98BADCFEU;
	context->state[3] = 0x10325476U;
	context->state[4] = 0xC3D2E1F0U;
}

extern void nxp_SHA1_Transform_asm (unsigned int *state, unsigned int *in, int repeat);

static __inline__ void nxp_SHA1_Transform (unsigned int *state, unsigned int *in, int repeat)
{
	nxp_SHA1_Transform_asm (state, in, repeat);
}

static void nxp_SHA1_Transform_unaligned (unsigned int *state, unsigned char *in, int repeat)
{
	unsigned int buf[16];

	for ( ; repeat > 0; repeat--) {
		memcpy (buf, in, sizeof(buf));
		nxp_SHA1_Transform (state, buf, 1);
		in += sizeof(buf);
	}
}

void nxp_SHA1_Update (NXP_SHA_CTX *context, unsigned char *input, unsigned long input_bytes)
{
	int byteIndex;
	int startLen;
	int remainderLen;
	int finalRemainder;

	byteIndex = (context->bytesHandled & 0x3F);
	context->bytesHandled += input_bytes;
	startLen = (64 - byteIndex);
	remainderLen = (input_bytes - startLen);

	if (remainderLen >= 0) {
		memcpy (&context->buffer[byteIndex], input, startLen);
		nxp_SHA1_Transform (context->state, (unsigned int *) context->buffer, 1);

		if ((((unsigned long) &input[startLen]) & 0x03) != 0)
			nxp_SHA1_Transform_unaligned (context->state, &input[startLen], (remainderLen / 64));
		else
			nxp_SHA1_Transform (context->state, (unsigned int *) &input[startLen], (remainderLen / 64));

		finalRemainder = startLen + (remainderLen & ~0x3F);
		byteIndex = 0;
	}
	else
		finalRemainder = 0;

	memcpy (&context->buffer[byteIndex], &input[finalRemainder], (input_bytes - finalRemainder));
}

static void unaligned_write32_be (unsigned char *dst, unsigned int value)
{
	*dst++ = value >> 24;
	*dst++ = value >> 16;
	*dst++ = value >> 8;
	*dst++ = value >> 0;
}

void nxp_SHA1_Final (unsigned char digest[SHA_DIGEST_LENGTH], NXP_SHA_CTX *context)
{
	unsigned char finalblock[64 + 8];
	unsigned int blockStartOffset;
	unsigned int finalBlockLength;
	int i;

	memset (finalblock, 0, 64);
	finalblock[0] = 0x80;
	blockStartOffset = (context->bytesHandled & 0x3F);
	finalBlockLength = (((blockStartOffset < 56) ? 56 : 120) - blockStartOffset);
	unaligned_write32_be (&finalblock[finalBlockLength + 0], context->bytesHandled >> 29);
	unaligned_write32_be (&finalblock[finalBlockLength + 4], context->bytesHandled << 3);
	nxp_SHA1_Update (context, finalblock, finalBlockLength + 8);
	for (i = 0; i < 5; i++)
		unaligned_write32_be (&digest[i*4], context->state[i]);
}

