/*
 * 본 프로그램에 대한 저작권을 포함한 지적재산권은 삼성SDS(주)에 있으며,
 * 삼성SDS(주)가 명시적으로 허용하지 않은 사용, 복사, 변경, 제3자에의 공개,
 * 배포는 엄격히 금지되며, 삼성SDS(주)의 지적재산권 침해에 해당됩니다.
 * 
 * Copyright (c) 2016 Samsung SDS Co., Ltd. All Rights Reserved. Confidential.
 * 
 * All information including the intellectual and technical concepts contained
 * herein is, and remains the property of Samsung SDS Co. Ltd. Unauthorized use,
 * dissemination, or reproduction of this material is strictly forbidden unless
 * prior written permission is obtained from Samsung SDS Co. Ltd. 
 */

/**
 * @file aes128-swbc-layer1-ctr.c
 * @brief This file provides the cryptographic API for Samsung WBC algorithm
 * configured with AES-128 as the base permutation algorithm, SWBC as the 
 * base cipher in mode of operation, 1 layer of WBC tables, and counter mode of operation
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes128-swbc-layer1-ctr.h"
#include "swbc-aes.h"
#include "swbc-error.h"
#include "swbc-utils.h"

/**
 * Loads SWBC table resource
 *
 * @param ctx         SWBC context
 * @param swbc_table  SWBC table resource
 * @param tag         tag for SWBC table resource. Tag is used to check whether the table resource is an appropriate one or not
 *
 * @return SWBC_SUCCESS on success, other values on failure
 */
int SWBCEXPORT aes128_swbc_layer1_ctr_load(aes128_swbc_layer1_ctr_ctx_t *ctx, const unsigned char **swbc_table, const unsigned char *tag)
{
  int result = 0;
  
	if(!ctx) return SWBC_ERROR_PARAM_CTX_NULL;
	memory_set(ctx, 0, sizeof(aes128_swbc_layer1_ctr_ctx_t));

  if(swbc_table != 0 && tag != 0){
    result = table_check(swbc_table, tag);
    if(result != SWBC_SUCCESS){
      return SWBC_ERROR_RESOURCE_UNLOADED;
    }
    ctx->swbc_table = swbc_table;
    return SWBC_SUCCESS;
  }
  else{
    return SWBC_ERROR_RESOURCE_UNLOADED;
  }
}

/**
 * Initializes SWBC encryption context with specified key
 *
 * @param ctx         SWBC context
 * @param mc          SWBC initialization constant
 *
 * @return SWBC_SUCCESS on success, other values on failure
 */
int SWBCEXPORT aes128_swbc_layer1_ctr_init(aes128_swbc_layer1_ctr_ctx_t *ctx, const unsigned char *mc)
{
  int result = 0;
  const unsigned char tmp_mc[16] = {0};

	if(!ctx) return SWBC_ERROR_PARAM_CTX_NULL;
	if(!mc) mc = tmp_mc;

	if(ctx->swbc_table){
		result = SWBC_AES_set_key_encrypt(ctx->rc, mc);
		if(result < 10) return SWBC_FAILURE;
		return SWBC_SUCCESS;
	}
	else{
		return SWBC_ERROR_RESOURCE_UNLOADED;
	}
}

/**
 * Increments the 16 byte counter value. When it reaches the max value it wraps around and goes on.
 *
 * @param counter counter to increment
 */
static void ctr128_inc(unsigned char *counter) {
	unsigned int n=16;
	unsigned char c;

	do {
		--n;
		c = counter[n];
		++c;
		counter[n] = c;
		if (c) return;
	} while (n);
}

/**
 * Encrypts data of arbitrary given length using SWBC encryption algorithm.
 * It uses ctr mode of operation.
 *
 * @param ctx         SWBC context
 * @param iv		      initialization vector for encryption
 * @param in          plaintext
 * @param out         storage for ciphertext(should be at least as large as given length)
 * @param len         plaintext length
 *
 * @return SWBC_SUCCESS on success, other values on failure
 */
int SWBCEXPORT aes128_swbc_layer1_ctr_encrypt(aes128_swbc_layer1_ctr_ctx_t *ctx, const unsigned char *iv, const unsigned char *in, unsigned char *out, int len)
{
	unsigned int i = 0;

	unsigned char ctr_buf[16]; 
	unsigned char inc_iv[16];

	if(!ctx) return SWBC_ERROR_PARAM_CTX_NULL;
	if(!iv) return SWBC_ERROR_PARAM_IV_NULL;
	if(!in) return SWBC_ERROR_PARAM_IN_NULL;
	if(!out) return SWBC_ERROR_PARAM_OUT_NULL;
	if(len <= 0) return SWBC_ERROR_PARAM_IN_LENGTH_INVALID;

	memory_copy(inc_iv, iv, sizeof(char)*16);

	while (len--) {
		if (i == 0) {
			SWBC_AES_encrypt((const unsigned char (*)[256])ctx->swbc_table, ctx->rc, inc_iv, ctr_buf);
			ctr128_inc(inc_iv);
		}
		*(out++) = *(in++) ^ ctr_buf[i];
		i = (i + 1) % 16;
	}
	return SWBC_SUCCESS;
}

/**
 * Decrypts data of arbitrary given length using SWBC decryption algorithm.
 *
 * @param ctx         SWBC context
 * @param iv          initialization vector for decryption
 * @param in          ciphertext
 * @param out         storage for plaintext(should be at least as large as given length)
 * @param len         ciphertext length
 *
 * @return SWBC_SUCCESS on success, other values on failure
 */
int SWBCEXPORT aes128_swbc_layer1_ctr_decrypt(aes128_swbc_layer1_ctr_ctx_t *ctx, const unsigned char *iv, const unsigned char *in, unsigned char *out, int len)
{
	return aes128_swbc_layer1_ctr_encrypt(ctx, iv, in, out, len);
}
