/*
 * Copyright (c) 2018 Qualcomm Technologies, Inc.
 * All Rights Reserved.
 * Confidential and Proprietary - Qualcomm Technologies, Inc.
 */

#include <stdint.h>

#include "cdefs.h"
#include "cipher.h"
#include "cipher_handler.h"
#include "qsee_cipher.h"
#include "qsee_log.h"

#define CIPHER_AES128_KEY_LEN 16
#define CIPHER_AES256_KEY_LEN 32

#define CIPHER_MAX_KEY_LEN 32

static const uint8_t default_cipher_key[CIPHER_MAX_KEY_LEN] = "0123456789abcdef0123456789abcdef";

static uint32_t get_qsee_cipher_alg(enum cipher_alg alg)
{
  switch (alg) {
    case CIPHER_ALG_AES_128:
      return QSEE_CIPHER_ALGO_AES_128;

    case CIPHER_ALG_AES_256:
      return QSEE_CIPHER_ALGO_AES_256;

    default:
      return QSEE_CIPHER_ALGO_INVALID;
  }
}

/* Returns the required key length for an encryption algorithm 'alg'. */
static uint32_t cipher_key_length(enum cipher_alg alg)
{
  switch (alg) {
    case CIPHER_ALG_AES_128:
      return CIPHER_AES128_KEY_LEN;
    case CIPHER_ALG_AES_256:
      return CIPHER_AES256_KEY_LEN;
    default:
      return 0;
  }
}

void cipher_handle_cmd(const struct cipher_req *req, struct cipher_rsp *rsp)
{
  int error;

  qsee_cipher_ctx *ctx;

  QSEE_CIPHER_MODE_ET mode;
  QSEE_CIPHER_PAD_ET padding;

  /* Initialize the cipher context. */
  error = qsee_cipher_init(get_qsee_cipher_alg(req->alg), &ctx);

  if (error) {
    qsee_log(QSEE_LOG_MSG_ERROR, "Failed to init cipher context: %d", error);
    rsp->status = CIPHER_FAILURE;
    return;
  }

  /* Set the cipher key parameter. */
  error = qsee_cipher_set_param(
    ctx, QSEE_CIPHER_PARAM_KEY, default_cipher_key, cipher_key_length(req->alg));

  if (error) {
    qsee_log(QSEE_LOG_MSG_ERROR, "Failed to set cipher key: %d", error);
    goto out;
  }

  /* Set the cipher mode parameter. */
  mode = QSEE_CIPHER_MODE_ECB;

  error = qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_MODE, &mode, sizeof(mode));
  if (error) {
    qsee_log(QSEE_LOG_MSG_ERROR, "Failed to set cipher mode: %d", error);
    goto out;
  }

  /* Set the cipher pad parameter. */
  padding = QSEE_CIPHER_PAD_NO_PAD;

  if (req->padded) {
    padding = QSEE_CIPHER_PAD_ISO10126;
  }

  error = qsee_cipher_set_param(ctx, QSEE_CIPHER_PARAM_PAD, &padding, sizeof(padding));
  if (error) {
    qsee_log(QSEE_LOG_MSG_ERROR, "Failed to set padding: %d", error);
    goto out;
  }

  /*
   * Initialize result_size to buffer capacity. result_size is then
   * updated to the actual size by the encrypt or decrypt function.
   */
  rsp->result_size = CIPHER_MAX_RESULT_SIZE;

  /* Run the encryption/decryption. The result is stored in rsp->result. */
  switch (req->mode) {
    case CIPHER_MODE_ENCRYPT:
      error = qsee_cipher_encrypt(ctx, req->msg, req->msg_len, rsp->result, &rsp->result_size);
      if (error) {
        qsee_log(QSEE_LOG_MSG_ERROR, "qsee_cipher_encrypt failed with error: %d", error);
      }
      break;

    case CIPHER_MODE_DECRYPT:
      error = qsee_cipher_decrypt(ctx, req->msg, req->msg_len, rsp->result, &rsp->result_size);
      if (error) {
        qsee_log(QSEE_LOG_MSG_ERROR, "qsee_cipher_decrypt failed with error: %d", error);
      }
      break;

    default:
      error = CIPHER_FAILURE;
      qsee_log(QSEE_LOG_MSG_ERROR, "Invalid cipher encrypt/decrypt mode");
      break;
  }

out:
  if (error) {
    rsp->status = CIPHER_FAILURE;
  } else {
    rsp->status = CIPHER_SUCCESS;
  }

  qsee_cipher_free_ctx(ctx);
}
