/*
 * Copyright (C) 2015-2016 Andreas Steffen
 * HSR Hochschule fuer Technik Rapperswil
 *
 * Based on the implementation by the Keccak, Keyak and Ketje Teams, namely,
 * Guido Bertoni, Joan Daemen, Michaël Peeters, Gilles Van Assche and
 * Ronny Van Keer, hereby denoted as "the implementer".
 *
 * To the extent possible under law, the implementer has waived all copyright
 * and related or neighboring rights to the source code in this file.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

#include <string.h>

#include "sha3_keccak.h"

typedef struct private_sha3_keccak_t private_sha3_keccak_t;

#define KECCAK_STATE_SIZE	 200	/* bytes */
#define KECCAK_MAX_RATE		 168	/* bytes */

static const uint64_t round_constants[] = {
    0x0000000000000001ULL,
    0x0000000000008082ULL,
    0x800000000000808aULL,
    0x8000000080008000ULL,
    0x000000000000808bULL,
    0x0000000080000001ULL,
    0x8000000080008081ULL,
    0x8000000000008009ULL,
    0x000000000000008aULL,
    0x0000000000000088ULL,
    0x0000000080008009ULL,
    0x000000008000000aULL,
    0x000000008000808bULL,
    0x800000000000008bULL,
    0x8000000000008089ULL,
    0x8000000000008003ULL,
    0x8000000000008002ULL,
    0x8000000000000080ULL,
    0x000000000000800aULL,
    0x800000008000000aULL,
    0x8000000080008081ULL,
    0x8000000000008080ULL,
    0x0000000080000001ULL,
    0x8000000080008008ULL
};

/**
 * Private data structure with hashing context for SHA-3
 */
struct private_sha3_keccak_t {

	/**
	 * Public interface for this hasher.
	 */
	sha3_keccak_t public;

	/**
	 * Internal state of 1600 bits as defined by FIPS-202
	 */
	uint8_t state[KECCAK_STATE_SIZE];

	/**
	 * Rate in bytes
	 */
	u_int rate;

	/**
	 * Rate input buffer
	 */
	uint8_t rate_buffer[KECCAK_MAX_RATE];

	/**
	 * Index pointing to the current position in the rate buffer
	 */
	u_int rate_index;

	/**
	 * Suffix delimiting the input message
	 */
	uint8_t delimited_suffix;

};

#if BYTE_ORDER != LITTLE_ENDIAN
/**
 * Function to load a 64-bit value using the little-endian (LE) convention.
 * On a LE platform, this could be greatly simplified using a cast.
 */
static uint64_t load64(const uint8_t *x)
{
	int i;
	uint64_t u = 0;

	for (i = 7; i >= 0; --i)
	{
		u <<= 8;
		u |= x[i];
	}
	return u;
}

/**
 * Function to store a 64-bit value using the little-endian (LE) convention.
 * On a LE platform, this could be greatly simplified using a cast.
 */
static void store64(uint8_t *x, uint64_t u)
{
	u_int i;

	for (i = 0; i < 8; ++i)
	{
		x[i] = u;
		u >>= 8;
	}
}

/**
 * Function to XOR into a 64-bit value using the little-endian (LE) convention.
 * On a LE platform, this could be greatly simplified using a cast.
 */
static void xor64(uint8_t *x, uint64_t u)
{
	u_int i;

	for (i = 0; i < 8; ++i)
	{
		x[i] ^= u;
		u >>= 8;
	}
}
#endif

/**
 * Some macros used by the Keccak-f[1600] permutation.
 */
#define ROL64(a, offset) ((((uint64_t)a) << offset) ^ (((uint64_t)a) >> (64-offset)))

#if BYTE_ORDER == LITTLE_ENDIAN
    #define readLane(i)          (((uint64_t*)state)[i])
    #define writeLane(i, lane)   (((uint64_t*)state)[i])  = (lane)
    #define XORLane(i, lane)     (((uint64_t*)state)[i]) ^= (lane)
#elif BYTE_ORDER == BIG_ENDIAN
    #define readLane(i)          load64((uint8_t*)state+sizeof(uint64_t)*i)
    #define writeLane(i, lane)   store64((uint8_t*)state+sizeof(uint64_t)*i, lane)
    #define XORLane(i, lane)     xor64((uint8_t*)state+sizeof(uint64_t)*i, lane)
#endif

/**
 * Function that computes the Keccak-f[1600] permutation on the given state.
 */
static void keccak_f1600_state_permute(void *state)
{
	int round;

	for (round = 0; round < 24; round++)
	{
		{   /* θ step (see [Keccak Reference, Section 2.3.2]) */

			uint64_t C[5], D;

			/* Compute the parity of the columns */
			C[0] = readLane(0) ^ readLane( 5) ^ readLane(10)
							   ^ readLane(15) ^ readLane(20);
			C[1] = readLane(1) ^ readLane( 6) ^ readLane(11)
							   ^ readLane(16) ^ readLane(21);
			C[2] = readLane(2) ^ readLane( 7) ^ readLane(12)
							   ^ readLane(17) ^ readLane(22);
			C[3] = readLane(3) ^ readLane( 8) ^ readLane(13)
							   ^ readLane(18) ^ readLane(23);
			C[4] = readLane(4) ^ readLane( 9) ^ readLane(14)
							   ^ readLane(19) ^ readLane(24);

			/* Compute and add the θ effect to the whole column */
			D = C[4] ^ ROL64(C[1], 1);
			XORLane( 0, D);
			XORLane( 5, D);
			XORLane(10, D);
			XORLane(15, D);
			XORLane(20, D);

			D = C[0] ^ ROL64(C[2], 1);
			XORLane( 1, D);
			XORLane( 6, D);
			XORLane(11, D);
			XORLane(16, D);
			XORLane(21, D);

			D = C[1] ^ ROL64(C[3], 1);
			XORLane( 2, D);
			XORLane( 7, D);
			XORLane(12, D);
			XORLane(17, D);
			XORLane(22, D);

			D = C[2] ^ ROL64(C[4], 1);
			XORLane( 3, D);
			XORLane( 8, D);
			XORLane(13, D);
			XORLane(18, D);
			XORLane(23, D);

			D = C[3] ^ ROL64(C[0], 1);
			XORLane( 4, D);
			XORLane( 9, D);
			XORLane(14, D);
			XORLane(19, D);
			XORLane(24, D);
		}

		{   /* ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) */

			uint64_t t1, t2;

			t1 = readLane( 1);

			t2 = readLane(10);
			writeLane(10, ROL64(t1,  1));

			t1 = readLane( 7);
			writeLane( 7, ROL64(t2,  3));

			t2 = readLane(11);
			writeLane(11, ROL64(t1,  6));

			t1 = readLane(17);
			writeLane(17, ROL64(t2, 10));

			t2 = readLane(18);
			writeLane(18, ROL64(t1, 15));

			t1 = readLane( 3);
			writeLane( 3, ROL64(t2, 21));

			t2 = readLane( 5);
			writeLane( 5, ROL64(t1, 28));

			t1 = readLane(16);
			writeLane(16, ROL64(t2, 36));

			t2 = readLane( 8);
			writeLane( 8, ROL64(t1, 45));

			t1 = readLane(21);
			writeLane(21, ROL64(t2, 55));

			t2 = readLane(24);
			writeLane(24, ROL64(t1,  2));

			t1 = readLane( 4);
			writeLane( 4, ROL64(t2, 14));

			t2 = readLane(15);
			writeLane(15, ROL64(t1, 27));

			t1 = readLane(23);
			writeLane(23, ROL64(t2, 41));

			t2 = readLane(19);
			writeLane(19, ROL64(t1, 56));

			t1 = readLane(13);
			writeLane(13, ROL64(t2,  8));

			t2 = readLane(12);
			writeLane(12, ROL64(t1, 25));

			t1 = readLane( 2);
			writeLane( 2, ROL64(t2, 43));

			t2 = readLane(20);
			writeLane(20, ROL64(t1, 62));

			t1 = readLane(14);
			writeLane(14, ROL64(t2, 18));

			t2 = readLane(22);
			writeLane(22, ROL64(t1, 39));

			t1 = readLane( 9);
			writeLane( 9, ROL64(t2, 61));

			t2 = readLane( 6);
			writeLane( 6, ROL64(t1, 20));

			writeLane( 1, ROL64(t2, 44));
		}

		{   /* χ step (see [Keccak Reference, Section 2.3.1]) */

			uint64_t t[5];

			t[0] = readLane(0);
			t[1] = readLane(1);
			t[2] = readLane(2);
			t[3] = readLane(3);
			t[4] = readLane(4);

			writeLane(0, t[0] ^ ((~t[1]) & t[2]));
			writeLane(1, t[1] ^ ((~t[2]) & t[3]));
			writeLane(2, t[2] ^ ((~t[3]) & t[4]));
			writeLane(3, t[3] ^ ((~t[4]) & t[0]));
			writeLane(4, t[4] ^ ((~t[0]) & t[1]));

			t[0] = readLane(5);
			t[1] = readLane(6);
			t[2] = readLane(7);
			t[3] = readLane(8);
			t[4] = readLane(9);

			writeLane(5, t[0] ^ ((~t[1]) & t[2]));
			writeLane(6, t[1] ^ ((~t[2]) & t[3]));
			writeLane(7, t[2] ^ ((~t[3]) & t[4]));
			writeLane(8, t[3] ^ ((~t[4]) & t[0]));
			writeLane(9, t[4] ^ ((~t[0]) & t[1]));

			t[0] = readLane(10);
			t[1] = readLane(11);
			t[2] = readLane(12);
			t[3] = readLane(13);
			t[4] = readLane(14);

			writeLane(10, t[0] ^ ((~t[1]) & t[2]));
			writeLane(11, t[1] ^ ((~t[2]) & t[3]));
			writeLane(12, t[2] ^ ((~t[3]) & t[4]));
			writeLane(13, t[3] ^ ((~t[4]) & t[0]));
			writeLane(14, t[4] ^ ((~t[0]) & t[1]));

			t[0] = readLane(15);
			t[1] = readLane(16);
			t[2] = readLane(17);
			t[3] = readLane(18);
			t[4] = readLane(19);

			writeLane(15, t[0] ^ ((~t[1]) & t[2]));
			writeLane(16, t[1] ^ ((~t[2]) & t[3]));
			writeLane(17, t[2] ^ ((~t[3]) & t[4]));
			writeLane(18, t[3] ^ ((~t[4]) & t[0]));
			writeLane(19, t[4] ^ ((~t[0]) & t[1]));

			t[0] = readLane(20);
			t[1] = readLane(21);
			t[2] = readLane(22);
			t[3] = readLane(23);
			t[4] = readLane(24);

			writeLane(20, t[0] ^ ((~t[1]) & t[2]));
			writeLane(21, t[1] ^ ((~t[2]) & t[3]));
			writeLane(22, t[2] ^ ((~t[3]) & t[4]));
			writeLane(23, t[3] ^ ((~t[4]) & t[0]));
			writeLane(24, t[4] ^ ((~t[0]) & t[1]));
		}

		{   /* ι step (see [Keccak Reference, Section 2.3.5]) */

			XORLane(0, round_constants[round]);
		}
	}
}

METHOD(sha3_keccak_t, get_rate, u_int,
	private_sha3_keccak_t *this)
{
	return this->rate;
}

METHOD(sha3_keccak_t, reset, void,
	private_sha3_keccak_t *this)
{
    memset(this->state, 0x00, KECCAK_STATE_SIZE);
	this->rate_index = 0;
}


METHOD(sha3_keccak_t, absorb, void,
	private_sha3_keccak_t *this, chunk_t data)
{
	uint64_t *buffer_lanes, *state_lanes;
	size_t len, rate_lanes;
	int i;

	buffer_lanes = (uint64_t*)this->rate_buffer;
	state_lanes  = (uint64_t*)this->state;
	rate_lanes = this->rate / sizeof(uint64_t);

	while (data.len)
	{
		len = min(data.len, this->rate - this->rate_index);
		memcpy(this->rate_buffer + this->rate_index, data.ptr, len);
		this->rate_index += len;
		data.ptr += len;
		data.len -= len;

		if (this->rate_index == this->rate)
		{
			for (i = 0; i < rate_lanes; i++)
			{
				state_lanes[i] ^= buffer_lanes[i];
			}
			this->rate_index = 0;

			keccak_f1600_state_permute(this->state);
		}
	}
}

METHOD(sha3_keccak_t, finalize, void,
	private_sha3_keccak_t *this)
{
	uint64_t *buffer_lanes, *state_lanes;
	size_t rate_lanes, remainder;
	int i;

	/* Add the delimitedSuffix as the first bit of padding */
	this->rate_buffer[this->rate_index++] = this->delimited_suffix;

	buffer_lanes = (uint64_t*)this->rate_buffer;
	state_lanes  = (uint64_t*)this->state;
	rate_lanes = this->rate_index / sizeof(uint64_t);

	remainder = this->rate_index - rate_lanes * sizeof(uint64_t);
	if (remainder)
	{
		memset(this->rate_buffer + this->rate_index, 0x00,
			   sizeof(uint64_t) - remainder);
		rate_lanes++;
	}
	for (i = 0; i < rate_lanes; i++)
	{
		state_lanes[i] ^= buffer_lanes[i];
	}

	/* Add the second bit of padding */
	this->state[this->rate - 1] ^= 0x80;

	/* Switch to the squeezing phase */
	keccak_f1600_state_permute(this->state);
	this->rate_index = 0;
}

METHOD(sha3_keccak_t, squeeze, void,
	private_sha3_keccak_t *this, size_t out_len, uint8_t *out)
{
	size_t index = 0, len;

	while (index < out_len)
	{
		if (this->rate_index == this->rate)
		{
			keccak_f1600_state_permute(this->state);
			this->rate_index = 0;
		}
		len = min(out_len - index, this->rate - this->rate_index);
		memcpy(out, &this->state[this->rate_index], len);
		out += len;
		index += len;
		this->rate_index += len;
	}
}

METHOD(sha3_keccak_t, destroy, void,
	private_sha3_keccak_t *this)
{
	free(this);
}

/*
 * Described in header.
 */
sha3_keccak_t *sha3_keccak_create(u_int capacity, uint8_t delimited_suffix)
{
	private_sha3_keccak_t *this;
	int rate;

	rate = KECCAK_STATE_SIZE - capacity;

	if (rate <= 0 || rate > KECCAK_MAX_RATE)
	{
		return NULL;
	}

	INIT(this,
		.public = {
			.get_rate = _get_rate,
			.reset = _reset,
			.absorb = _absorb,
			.finalize = _finalize,
			.squeeze = _squeeze,
			.destroy = _destroy,
		},
		.rate = rate,
		.delimited_suffix = delimited_suffix,
	);

	return &this->public;
}
