/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   sl_sha3_5.c                                        :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: bchanot <bchanot@student.42.fr>            +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2018/10/03 19:33:51 by bchanot           #+#    #+#             */
/*   Updated: 2018/10/08 22:35:12 by bchanot          ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "ft_ssl.h"
#include "sl_sha3_5.h"

static void	sl_sha3_5_get_state(t_sha3_5 *ctx, t_uint64 m[64])
{
	t_process_sha3_5	t;
	size_t				i;

	sl_sha3_5_process_init_add(ctx, &t, true);
	i = -1;
	while (++i < 80)
		sl_sha3_5_get_registers(m, &t, i);
	sl_sha3_5_process_init_add(ctx, &t, false);
}

static void	sl_sha3_5_process(t_sha3_5 *ctx, const t_uint8 *data)
{
	t_uint64	m[80];
	size_t		i;
	size_t		j;

	i = -1;
	while (++i < 80)
	{
		if (i < 16)
		{
			m[i] = data[i * 8] & 0xff;
			j = 0;
			while (++j < 8)
			{
				m[i] <<= 8;
				m[i] |= data[i * 8 + j] & 0xff;
			}
		}
		else
			m[i] = SIG1_SHA3_5(m[i - 2]) + m[i - 7] +
				SIG0_SHA3_5(m[i - 15]) + m[i - 16];
	}
	sl_sha3_5_get_state(ctx, m);
}

void		sl_sha3_5_update(t_sha3_5 *ctx, const t_uint8 *data, size_t len)
{
	size_t	index;
	size_t	i;

	index = (size_t)((ctx->count[0] >> 3) & 0x7f);
	if ((ctx->count[0] += (len << 3)) < (len << 3))
	{
		if ((ctx->count[1] += 1) < 1 && (ctx->count[2] += 1) < 1)
			ctx->count[3]++;
		ctx->count[1] += (len >> 29);
	}
	i = 0;
	if (len >= (sizeof(ctx->buff) - index))
	{
		ft_memcpy(&ctx->buff[index], data, (sizeof(ctx->buff) - index));
		sl_sha3_5_process(ctx, ctx->buff);
		i = (sizeof(ctx->buff) - index);
		while (i + (sizeof(ctx->buff) - 1) < len)
		{
			sl_sha3_5_process(ctx, &data[i]);
			i += sizeof(ctx->buff);
		}
		index = 0;
	}
	ft_memcpy(&ctx->buff[index], &data[i], len - i);
}

void		sl_sha3_5_get_hash(t_sha3_5 *ctx, t_uint8 hash[32])
{
	size_t	state;
	size_t	i;
	size_t	j;
	int		k;

	i = -1;
	j = 0;
	while (++i < 8)
	{
		state = ctx->state[i];
		k = 8;
		while (--k >= 0)
		{
			hash[j + k] = state & 0xff;
			state >>= 8;
		}
		j += 8;
	}
}

void		sl_sha3_5_final(t_sha3_5 *ctx)
{
	static const t_uint8	padding[128] = {0x80, };
	t_uint8					bits[16];
	size_t					pad_len;
	size_t					t;
	int						i;

	t = ctx->count[0];
	i = 16;
	pad_len = 0;
	while (--i >= 0)
	{
		bits[i] = t;
		t = !(i % 4) ? ctx->count[++pad_len] : (t >> 8);
	}
	t = (ctx->count[0] >> 3) & 0x7f;
	pad_len = (t < 112) ? (112 - t) : ((sizeof(ctx->buff) + 112) - t);
	sl_sha3_5_update(ctx, padding, pad_len);
	sl_sha3_5_update(ctx, bits, sizeof(bits));
}
