/*
 * Copyright (c) 2015 Jan Kolarik
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @file rc4.c
 *
 * Implementation of ARC4 symmetric cipher cryptographic algorithm.
 *
 */

#include <errno.h>
#include <mem.h>
#include "crypto.h"

/* Sbox table size. */
#define SBOX_SIZE  256

/** Swap two values in sbox.
 *
 * @param i    First index of value in sbox to be swapped.
 * @param j    Second index of value in sbox to be swapped.
 * @param sbox Sbox to be modified.
 *
 */
static void swap(size_t i, size_t j, uint8_t *sbox)
{
	uint8_t temp = sbox[i];
	sbox[i] = sbox[j];
	sbox[j] = temp;
}

/** Sbox initialization procedure.
 *
 * @param key      Input key.
 * @param key_size Size of key sequence.
 * @param sbox     Place for result sbox.
 *
 */
static void create_sbox(uint8_t *key, size_t key_size, uint8_t *sbox)
{
	for (size_t i = 0; i < SBOX_SIZE; i++)
		sbox[i] = i;
	
	uint8_t j = 0;
	for (size_t i = 0; i < SBOX_SIZE; i++) {
		j = j + sbox[i] + key[i % key_size];
		swap(i, j, sbox);
	}
}

/** ARC4 encryption/decryption algorithm.
 *
 * @param key        Input key.
 * @param key_size   Size of key sequence.
 * @param input      Input data sequence to be processed.
 * @param input_size Size of input data sequence.
 * @param skip       Number of bytes to be skipped from
 *                   the beginning of key stream.
 * @param output     Result data sequence.
 *
 * @return EINVAL when input or key not specified,
 *         ENOMEM when pointer for output is not allocated,
 *         otherwise EOK.
 *
 */
errno_t rc4(uint8_t *key, size_t key_size, uint8_t *input, size_t input_size,
    size_t skip, uint8_t *output)
{
	if ((!key) || (!input))
		return EINVAL;
	
	if (!output)
		return ENOMEM;
	
	/* Initialize sbox. */
	uint8_t sbox[SBOX_SIZE];
	create_sbox(key, key_size, sbox);
	
	/* Skip first x bytes. */
	uint8_t i = 0;
	uint8_t j = 0;
	for (size_t k = 0; k < skip; k++) {
		i = i + 1;
		j = j + sbox[i];
		swap(i, j, sbox);
	}
	
	/* Processing loop. */
	uint8_t val;
	for (size_t k = 0; k < input_size; k++) {
		i = i + 1;
		j = j + sbox[i];
		swap(i, j, sbox);
		val = sbox[sbox[i] + sbox[j]];
		output[k] = val ^ input[k];
	}
	
	return EOK;
}
