/****************************************************************************
 * apps/crypto/mbedtls/source/ecdsa_alt.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <dev_alt.h>
#include <mbedtls/bignum.h>
#include <mbedtls/ecdsa.h>
#include <mbedtls/error.h>

#define ECDSA_SECP256R1_KEYLEN 32

/****************************************************************************
 * Public Functions
 ****************************************************************************/

int mbedtls_ecdsa_sign(FAR mbedtls_ecp_group *grp, FAR mbedtls_mpi *r,
                       FAR mbedtls_mpi *s, FAR const mbedtls_mpi *d,
                       FAR const unsigned char *buf, size_t blen,
                       FAR int (*f_rng)(void *, unsigned char *, size_t),
                       FAR void *p_rng)
{
  cryptodev_context_t ctx;
  int ret;

  ret = cryptodev_init(&ctx);
  if (ret != 0)
    {
      return MBEDTLS_ERR_ECP_ALLOC_FAILED;
    }

  switch (grp->id)
    {
      case MBEDTLS_ECP_DP_SECP256R1:
        ctx.cryptk.crk_op = CRK_ECDSA_SECP256R1_SIGN;
        mbedtls_mpi_grow(r, ECDSA_SECP256R1_KEYLEN /
                            sizeof(mbedtls_mpi_uint));
        mbedtls_mpi_grow(s, ECDSA_SECP256R1_KEYLEN /
                            sizeof(mbedtls_mpi_uint));
        break;
      default:
        ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE;
        goto cleanup;
    }

  ctx.cryptk.crk_iparams = 2;
  ctx.cryptk.crk_oparams = 2;
  ctx.cryptk.crk_param[0].crp_p = (caddr_t)d->p;
  ctx.cryptk.crk_param[0].crp_nbits = d->n * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[1].crp_p = (caddr_t)buf;
  ctx.cryptk.crk_param[1].crp_nbits = blen * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[2].crp_p = (caddr_t)r->p;
  ctx.cryptk.crk_param[2].crp_nbits = r->n * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[3].crp_p = (caddr_t)s->p;
  ctx.cryptk.crk_param[3].crp_nbits = s->n * sizeof(mbedtls_mpi_uint) * 8;

  ret = cryptodev_cryptk(&ctx);
  if (ret < 0)
    {
      ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
      goto cleanup;
    }

  ret = ctx.cryptk.crk_status;

cleanup:
  cryptodev_free(&ctx);
  return ret;
}

int mbedtls_ecdsa_verify(FAR mbedtls_ecp_group *grp,
                         FAR const unsigned char *buf, size_t blen,
                         FAR const mbedtls_ecp_point *Q,
                         FAR const mbedtls_mpi *r,
                         FAR const mbedtls_mpi *s)
{
  cryptodev_context_t ctx;
  int ret;

  ret = cryptodev_init(&ctx);
  if (ret != 0)
    {
      return MBEDTLS_ERR_ECP_ALLOC_FAILED;
    }

  switch (grp->id)
    {
      case MBEDTLS_ECP_DP_SECP256R1:
        ctx.cryptk.crk_op = CRK_ECDSA_SECP256R1_VERIFY;
        mbedtls_mpi_shrink(r, ECDSA_SECP256R1_KEYLEN /
                              sizeof(mbedtls_mpi_uint));
        mbedtls_mpi_shrink(s, ECDSA_SECP256R1_KEYLEN /
                              sizeof(mbedtls_mpi_uint));
        break;
      default:
        ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE;
        goto cleanup;
    }

  ctx.cryptk.crk_iparams = 6;
  ctx.cryptk.crk_oparams = 0;
  ctx.cryptk.crk_param[0].crp_p = (caddr_t)Q->X.p;
  ctx.cryptk.crk_param[0].crp_nbits = Q->X.n * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[1].crp_p = (caddr_t)Q->Y.p;
  ctx.cryptk.crk_param[1].crp_nbits = Q->Y.n * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[2].crp_p = (caddr_t)Q->Z.p;
  ctx.cryptk.crk_param[2].crp_nbits = Q->Z.n * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[3].crp_p = (caddr_t)r->p;
  ctx.cryptk.crk_param[3].crp_nbits = r->n * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[4].crp_p = (caddr_t)s->p;
  ctx.cryptk.crk_param[4].crp_nbits = s->n * sizeof(mbedtls_mpi_uint) * 8;
  ctx.cryptk.crk_param[5].crp_p = (caddr_t)buf;
  ctx.cryptk.crk_param[5].crp_nbits = blen * sizeof(mbedtls_mpi_uint) * 8;

  ret = cryptodev_cryptk(&ctx);
  if (ret < 0)
    {
      ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
      goto cleanup;
    }

  ret = ctx.cryptk.crk_status;

cleanup:
  cryptodev_free(&ctx);
  return ret;
}

int mbedtls_ecdsa_genkey(FAR mbedtls_ecdsa_context *ctx,
                         mbedtls_ecp_group_id gid,
                         FAR int (*f_rng)(void *, unsigned char *, size_t),
                         FAR void *p_rng)
{
  cryptodev_context_t dev;
  int ret;

  ret = mbedtls_ecp_group_load(&ctx->grp, gid);
  if (ret != 0)
    {
      return ret;
    }

  ret = cryptodev_init(&dev);
  if (ret != 0)
    {
      return MBEDTLS_ERR_ECP_ALLOC_FAILED;
    }

  switch (gid)
    {
      case MBEDTLS_ECP_DP_SECP256R1:
        dev.cryptk.crk_op = CRK_ECDSA_SECP256R1_GENKEY;
        mbedtls_mpi_grow(&ctx->d, ECDSA_SECP256R1_KEYLEN /
                                  sizeof(mbedtls_mpi_uint));
        mbedtls_mpi_grow(&ctx->Q.X, ECDSA_SECP256R1_KEYLEN /
                                    sizeof(mbedtls_mpi_uint));
        mbedtls_mpi_grow(&ctx->Q.Y, ECDSA_SECP256R1_KEYLEN /
                                    sizeof(mbedtls_mpi_uint));
        break;
      default:
        ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE;
        goto cleanup;
    }

  dev.cryptk.crk_iparams = 0;
  dev.cryptk.crk_oparams = 4;
  dev.cryptk.crk_param[0].crp_p = (caddr_t)ctx->d.p;
  dev.cryptk.crk_param[0].crp_nbits = ctx->d.n *
                                      sizeof(mbedtls_mpi_uint) * 8;
  dev.cryptk.crk_param[1].crp_p = (caddr_t)ctx->Q.X.p;
  dev.cryptk.crk_param[1].crp_nbits = ctx->Q.X.n *
                                      sizeof(mbedtls_mpi_uint) * 8;
  dev.cryptk.crk_param[2].crp_p = (caddr_t)ctx->Q.Y.p;
  dev.cryptk.crk_param[2].crp_nbits = ctx->Q.Y.n *
                                      sizeof(mbedtls_mpi_uint) * 8;
  dev.cryptk.crk_param[3].crp_p = (caddr_t)ctx->Q.Z.p;
  dev.cryptk.crk_param[3].crp_nbits = ctx->Q.Z.n *
                                      sizeof(mbedtls_mpi_uint) * 8;

  ret = cryptodev_cryptk(&dev);
  if (ret < 0)
    {
      ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
      goto cleanup;
    }

  ret = dev.cryptk.crk_status;

cleanup:
  cryptodev_free(&dev);
  return ret;
}