/****************************************************************************
 * crypto/cryptosoft.c
 *
 * SPDX-License-Identifier: OAR
 * SPDX-FileCopyrightText: Copyright (c) 2000, 2001 Angelos D. Keromytis
 * SPDX-FileContributor: Angelos D. Keromytis (angelos@cis.upenn.edu)
 *
 * Permission to use, copy, and modify this software with or without fee
 * is hereby granted, provided that this entire notice is included in
 * all source code copies of any software which is or includes a copy or
 * modification of this software.
 *
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
 * PURPOSE.
 *
 * This code was written by Angelos D. Keromytis in Athens, Greece, in
 * February 2000. Network Security Technologies Inc. (NSTI) kindly
 * supported the development of this code.
 *
 ****************************************************************************/

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

#include <assert.h>
#include <errno.h>
#include <endian.h>
#include <fcntl.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mtd/configdata.h>
#include <nuttx/kmalloc.h>
#include <nuttx/lib/math32.h>
#include <crypto/bn.h>
#include <crypto/cryptodev.h>
#include <crypto/cryptosoft.h>
#include <crypto/curve25519.h>
#include <crypto/ecc.h>
#include <crypto/xform.h>
#include <sys/param.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT

#define SWKEY_MAGIC_STRING  "SWKEYMGMT"
#define SWKEY_FILL_NAME(name, keyid, type)       \
  do                                             \
    {                                            \
      snprintf(name, sizeof(name), "%s.%lu.%s",  \
               SWKEY_MAGIC_STRING, keyid, type); \
    }                                            \
  while (0)

/****************************************************************************
 * Private Type Definitions
 ****************************************************************************/

struct swkey_data_s
{
  uint32_t id;
  uint32_t size;
  uint32_t flags;
  uint8_t buf[CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE];
  TAILQ_ENTRY(swkey_data_s) next;
};

struct swkey_context_s
{
  struct file file;
  TAILQ_HEAD(swkey_list, swkey_data_s) head;
};

#endif /* CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT */

/****************************************************************************
 * Private Data
 ****************************************************************************/

#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_CRYPTO

FAR struct swcr_data **swcr_sessions = NULL;
uint32_t swcr_sesnum = 0;
int swcr_id = -1;

#endif /* CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_CRYPTO */

/****************************************************************************
 * Private Functions
 ****************************************************************************/

#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT

/* key data operations in flash */

/****************************************************************************
 * Name: swkey_write
 *
 * Description:
 *   Storing key data into flash and mapping it to the keyid
 *
 ****************************************************************************/

static int swkey_write(FAR struct file *filep, uint32_t keyid,
                       FAR const void *data, uint32_t len, int flags)
{
  struct config_data_s config;
  int ret;

  if (keyid == 0 || data == NULL || len == 0)
    {
      return -EINVAL;
    }

  /* Write the data and flags to the Flash */

  memset(&config, 0, sizeof(config));
  SWKEY_FILL_NAME(config.name, keyid, "data");
  config.len = len;
  config.configdata = (uint8_t *)data;
  ret = file_ioctl(filep, CFGDIOC_SETCONFIG, &config);
  if (ret < 0)
    {
      return ret;
    }

  SWKEY_FILL_NAME(config.name, keyid, "flags");
  config.len = sizeof(uint32_t);
  config.configdata = (uint8_t *)&flags;
  return file_ioctl(filep, CFGDIOC_SETCONFIG, &config);
}

/****************************************************************************
 * Name: swkey_remove
 *
 * Description:
 *   Removing key data from flash
 *
 ****************************************************************************/

static int swkey_remove(FAR struct file *filep, uint32_t keyid)
{
  struct config_data_s config;
  int ret;

  if (keyid == 0)
    {
      return -EINVAL;
    }

  /* Remove the flags and data */

  memset(&config, 0, sizeof(config));
  SWKEY_FILL_NAME(config.name, keyid, "flags");
  ret = file_ioctl(filep, CFGDIOC_DELCONFIG, &config);
  if (ret < 0)
    {
      return ret;
    }

  memset(config.name, 0, sizeof(config.name));
  SWKEY_FILL_NAME(config.name, keyid, "data");
  return file_ioctl(filep, CFGDIOC_DELCONFIG, &config);
}

/****************************************************************************
 * Name: swkey_get_flags
 *
 * Description:
 *   Getting key flags from flash
 *
 ****************************************************************************/

static int swkey_get_flags(FAR struct file *filep, uint32_t keyid,
                           FAR uint32_t *flags)
{
  struct config_data_s config;

  if (keyid == 0 || flags == NULL)
    {
      return -EINVAL;
    }

  memset(&config, 0, sizeof(config));
  SWKEY_FILL_NAME(config.name, keyid, "flags");
  config.len = sizeof(uint32_t);
  config.configdata = (uint8_t *)flags;
  return file_ioctl(filep, CFGDIOC_GETCONFIG, &config);
}

/****************************************************************************
 * Name: swkey_read
 *
 * Description:
 *   Getting key data from flash
 *
 ****************************************************************************/

static int swkey_read(FAR struct file *filep, uint32_t keyid,
                      FAR void *buf, uint32_t buflen)
{
  struct config_data_s config;
  int ret;

  if (keyid == 0 || buf == NULL)
    {
      return -EINVAL;
    }

  memset(&config, 0, sizeof(config));
  SWKEY_FILL_NAME(config.name, keyid, "data");
  config.len = buflen;
  config.configdata = buf;
  ret = file_ioctl(filep, CFGDIOC_GETCONFIG, &config);
  if (ret < 0)
    {
      return ret;
    }

  return config.len;
}

/* key data operations in cache */

/****************************************************************************
 * Name: swkey_get_context
 *
 * Description:
 *   Access key cache list entries
 *
 ****************************************************************************/

static FAR struct swkey_context_s *swkey_get_context(void)
{
  FAR struct swkey_context_s *ctx;
  int swkey_id;

  swkey_id = crypto_find_driverid(CRYPTOCAP_F_KEY_MGMT);
  if (swkey_id < 0)
    {
      return NULL;
    }

  ctx = (FAR struct swkey_context_s *)crypto_driver_get_priv(swkey_id);
  if (ctx == NULL)
    {
      return NULL;
    }

  if (ctx->file.f_inode == NULL)
    {
      if (file_open(&ctx->file,
                    CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_DEVICE,
                    O_RDWR | O_CLOEXEC) < 0)
        {
          return NULL;
        }
    }

  return ctx;
}

/****************************************************************************
 * Name: swkey_get_cache_data
 *
 * Description:
 *   Acquire an available key slot in cache. If the key exists in the cache,
 * utilize that slot immediately; otherwise, locate the last slot.
 *
 ****************************************************************************/

static FAR struct swkey_data_s *
swkey_get_cache_data(FAR struct swkey_context_s *ctx, uint32_t keyid)
{
  FAR struct swkey_data_s *data;

  TAILQ_FOREACH(data, &ctx->head, next)
    {
      if (data->id == keyid)
        {
          break;
        }
    }

  if (data == NULL)
    {
      data = TAILQ_LAST(&ctx->head, swkey_list);
      if (data->id)
        {
          swkey_write(&ctx->file, data->id, data->buf,
                      data->size, data->flags);
        }
    }

  return data;
}

/****************************************************************************
 * Name: swkey_promote_cache_data
 *
 * Description:
 *   Update the key cache linked list.
 *   Move the accessed key cache to the head position to ensure
 *   the most frequently used keys remain cached.
 *
 ****************************************************************************/

static void swkey_promote_cache_data(FAR struct swkey_context_s *ctx,
                                     FAR struct swkey_data_s *data)
{
  TAILQ_REMOVE(&ctx->head, data, next);
  TAILQ_INSERT_HEAD(&ctx->head, data, next);
}

/* key management operations */

/****************************************************************************
 * Name: swkey_clean_cache_data
 *
 * Description:
 *   Clean the cache slot
 *
 ****************************************************************************/

static void swkey_clean_cache_data(FAR struct swkey_data_s *data)
{
  explicit_bzero(data->buf, sizeof(data->buf));
  data->id = 0;
  data->size = 0;
  data->flags = 0;
}

/****************************************************************************
 * Name: swkey_is_valid
 *
 * Description:
 *   Check whether the given keyid is available in the driver
 *
 ****************************************************************************/

static int swkey_is_valid(FAR struct swkey_context_s *ctx, uint32_t keyid)
{
  uint32_t flags;
  int ret;

  if (keyid == 0)
    {
      return -EINVAL;
    }

  ret = swkey_get_flags(&ctx->file, keyid, &flags);
  if (ret == -ENOENT)
    {
      /* No such file means keyid unused and available
       * and occupied this keyid with MAGIC-STRING
       */

      return swkey_write(&ctx->file, keyid, SWKEY_MAGIC_STRING,
                                     sizeof(SWKEY_MAGIC_STRING), 0);
    }
  else if (ret == 0)
    {
      /* success means keyid used and unavailable */

      return -EEXIST;
    }

  return ret;
}

/****************************************************************************
 * Name: swkey_alloc
 *
 * Description:
 *   Acquire an available key ID from the driver
 *
 ****************************************************************************/

static int swkey_alloc(FAR struct swkey_context_s *ctx,
                       FAR uint32_t *keyid)
{
  int i;

  for (i = 1; i <= CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_NKEYS; i++)
    {
      if (swkey_is_valid(ctx, i) == 0)
        {
          *keyid = i;
          return OK;
        }
    }

  return -ENOMEM;
}

/****************************************************************************
 * Name: swkey_import
 *
 * Description:
 *   Import key data into cache key slot and bind to the keyid
 *
 ****************************************************************************/

static int swkey_import(FAR struct swkey_context_s *ctx,
                        uint32_t keyid, FAR void *buf,
                        uint32_t buflen, uint32_t flags)
{
  FAR struct swkey_data_s *data;

  if (keyid == 0)
    {
      return -EINVAL;
    }

  if (buflen > CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE)
    {
      return swkey_write(&ctx->file, keyid, buf, buflen, flags);
    }

  data = swkey_get_cache_data(ctx, keyid);
  data->id = keyid;
  data->size = buflen;
  memcpy(data->buf, buf, data->size);
  if (flags & CRYPTO_F_NOT_EXPORTABLE)
    {
      data->flags |= CRYPTO_F_NOT_EXPORTABLE;
    }
  else
    {
      data->flags &= ~CRYPTO_F_NOT_EXPORTABLE;
    }

  swkey_promote_cache_data(ctx, data);
  return OK;
}

/****************************************************************************
 * Name: swkey_delete
 *
 * Description:
 *   Remove a specific key by keyid
 *
 ****************************************************************************/

static int swkey_delete(FAR struct swkey_context_s *ctx, uint32_t keyid)
{
  FAR struct swkey_data_s *data;

  if (keyid == 0)
    {
      return -EINVAL;
    }

  data = swkey_get_cache_data(ctx, keyid);
  if (data->id == keyid)
    {
      swkey_clean_cache_data(data);
    }

  return swkey_remove(&ctx->file, keyid);
}

/****************************************************************************
 * Name: swkey_export
 *
 * Description:
 *   Export key data by keyid
 *
 ****************************************************************************/

static int swkey_export(FAR struct swkey_context_s *ctx,
                        uint32_t keyid, FAR void *buf,
                        uint32_t buflen)
{
  FAR struct swkey_data_s *data;
  uint32_t flags;
  int ret;

  if (keyid == 0)
    {
      return -EINVAL;
    }

  data = swkey_get_cache_data(ctx, keyid);
  if (data->id == keyid)
    {
      /* Key in cache, export data and update cache */

      if (data->flags & CRYPTO_F_NOT_EXPORTABLE)
        {
          return -EACCES;
        }

      if (buflen < data->size)
        {
          return -ENOBUFS;
        }

      memcpy(buf, data->buf, data->size);
      swkey_promote_cache_data(ctx, data);
      return data->size;
    }

  /* Key not in cache, get key from flash */

  ret = swkey_get_flags(&ctx->file, keyid, &flags);
  if (ret < 0)
    {
      return ret;
    }

  if (flags & CRYPTO_F_NOT_EXPORTABLE)
    {
      return -EACCES;
    }

  ret = swkey_read(&ctx->file, keyid, buf, buflen);
  if (ret < 0)
    {
      return ret;
    }
  else if (ret > buflen)
    {
      return -ENOBUFS;
    }
  else if (memcmp(buf, SWKEY_MAGIC_STRING, ret) == 0)
    {
      return -ENOENT;
    }

  if (ret < CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE)
    {
      data->id = keyid;
      data->size = ret;
      data->flags = flags;
      memcpy(data->buf, buf, ret);
      swkey_promote_cache_data(ctx, data);
    }

  return ret;
}

/****************************************************************************
 * Name: swkey_gen_secp256r1_key
 *
 * Description:
 *   Generate SECP256R1 keypair and bound with keyid
 *
 ****************************************************************************/

static int swkey_gen_secp256r1_key(FAR struct swkey_context_s *ctx,
                                   uint32_t priv_keyid,
                                   uint32_t pub_keyid)
{
  FAR struct swkey_data_s *data;
  uint8_t priv[secp256r1];
  uint8_t pub[secp256r1 * 2];
  int ret = -EINVAL;

  if (priv_keyid == 0 || pub_keyid == 0)
    {
      return ret;
    }

  if (ecc_make_key_uncomp(pub, pub + secp256r1, priv) == 0)
    {
      return ret;
    }

  /* Private keys cannot be exported */

  ret = swkey_write(&ctx->file, priv_keyid, priv, secp256r1,
                    CRYPTO_F_NOT_EXPORTABLE);
  if (ret < 0)
    {
      return ret;
    }

  ret = swkey_write(&ctx->file, pub_keyid, pub, secp256r1 * 2, 0);
  if (ret < 0)
    {
      swkey_delete(ctx, priv_keyid);
      return ret;
    }

  if (CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE >= secp256r1)
    {
      data = swkey_get_cache_data(ctx, priv_keyid);
      data->id = priv_keyid;
      data->size = secp256r1;
      data->flags = CRYPTO_F_NOT_EXPORTABLE;
      memcpy(data->buf, priv, secp256r1);
      swkey_promote_cache_data(ctx, data);
    }

  if (CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE >= secp256r1 * 2)
    {
      data = swkey_get_cache_data(ctx, pub_keyid);
      data->id = pub_keyid;
      data->size = secp256r1 * 2;
      data->flags = 0;
      memcpy(data->buf, pub, secp256r1 * 2);
      swkey_promote_cache_data(ctx, data);
    }

  return ret;
}

/****************************************************************************
 * Name: swkey_gen_aes_key
 *
 * Description:
 *   Generate AES key and bound with keyid
 *
 ****************************************************************************/

static int swkey_gen_aes_key(FAR struct swkey_context_s *ctx, uint32_t keyid,
                             uint32_t keylen)
{
  FAR struct swkey_data_s *data;
  int ret = -EINVAL;
  char buf[32];

  if (keyid == 0)
    {
      return ret;
    }

  /* Generate a key sufficient for AES-128/192/256 */

  arc4random_buf(buf, keylen);
  ret = swkey_write(&ctx->file, keyid, buf, keylen, 0);
  if (ret < 0)
    {
      return ret;
    }

  if (keylen <= CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE)
    {
      data = swkey_get_cache_data(ctx, keyid);
      data->id = keyid;
      data->size = keylen;
      data->flags = 0;
      memcpy(data->buf, buf, keylen);
      swkey_promote_cache_data(ctx, data);
    }

  return ret;
}

/****************************************************************************
 * Name: swkey_save
 *
 * Description:
 *   Write key from cache to Flash
 *
 ****************************************************************************/

static int swkey_save(FAR struct swkey_context_s *ctx, uint32_t keyid)
{
  FAR struct swkey_data_s *data;
  int ret = -EINVAL;

  if (keyid == 0)
    {
      return ret;
    }

  data = swkey_get_cache_data(ctx, keyid);
  if (data->id == keyid)
    {
      ret = swkey_write(&ctx->file, keyid, data->buf,
                        data->size, data->flags);
      if (ret < 0)
        {
          return ret;
        }

      swkey_promote_cache_data(ctx, data);
    }

  return OK;
}

/****************************************************************************
 * Name: crypto_load_key
 *
 * Description:
 *   Load key data from Flash to cache
 *
 ****************************************************************************/

static int swkey_load(FAR struct swkey_context_s *ctx, uint32_t keyid)
{
  FAR struct swkey_data_s *data;
  char buf[CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE];
  int readlen;

  if (keyid == 0)
    {
      return -EINVAL;
    }

  readlen = swkey_read(&ctx->file, keyid, buf, sizeof(buf));
  if (readlen < 0)
    {
      return readlen;
    }
  else if (readlen > CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_BUFSIZE)
    {
      return -EFBIG;
    }

  data = swkey_get_cache_data(ctx, keyid);
  data->id = keyid;
  data->size = readlen;
  swkey_get_flags(&ctx->file, keyid, &data->flags);
  memcpy(data->buf, buf, data->size);
  swkey_promote_cache_data(ctx, data);
  return OK;
}

/****************************************************************************
 * Name: swkey_unload
 *
 * Description:
 *   Unload key data from cache
 *
 ****************************************************************************/

static int swkey_unload(FAR struct swkey_context_s *ctx, uint32_t keyid)
{
  FAR struct swkey_data_s *data;
  int ret = -EINVAL;

  if (keyid == 0)
    {
      return ret;
    }

  data = swkey_get_cache_data(ctx, keyid);
  if (data->id == keyid)
    {
      ret = swkey_write(&ctx->file, data->id, data->buf,
                        data->size, data->flags);
      if (ret < 0)
        {
          return ret;
        }

      swkey_clean_cache_data(data);
    }

  return ret;
}

/****************************************************************************
 * Name: swkey_kprocess
 *
 * Description:
 *   Key management process function in crypto driver
 *
 ****************************************************************************/

static int swkey_kprocess(FAR struct cryptkop *krp)
{
  FAR struct swkey_context_s *ctx;
  uint32_t priv_keyid;
  uint32_t pub_keyid;
  uint32_t keylen;
  uint32_t keyid;
  int ret;

  /* Sanity check */

  if (krp == NULL)
    {
      return -EINVAL;
    }

  ctx = swkey_get_context();
  if (ctx == NULL)
    {
      return -EINVAL;
    }

  if (krp->krp_param[0].crp_nbits != sizeof(uint32_t) * 8)
    {
      return -EINVAL;
    }

  keyid = *(uint32_t *)krp->krp_param[0].crp_p;

  /* Go through crypto descriptors, processing as we go */

  switch (krp->krp_op)
    {
      case CRK_ALLOCATE_KEY:
        krp->krp_status = swkey_alloc(ctx, &keyid);
        if (krp->krp_status == 0)
          {
            memcpy(krp->krp_param[0].crp_p, &keyid, sizeof(uint32_t));
          }

        break;
      case CRK_VALIDATE_KEYID:
        krp->krp_status = swkey_is_valid(ctx, keyid);
        break;
      case CRK_IMPORT_KEY:
        krp->krp_status = swkey_import(ctx, keyid,
                                       krp->krp_param[1].crp_p,
                                       krp->krp_param[1].crp_nbits / 8,
                                       krp->krp_flags);
        break;
      case CRK_DELETE_KEY:
        krp->krp_status = swkey_delete(ctx, keyid);
        break;
      case CRK_EXPORT_KEY:
        ret = swkey_export(ctx, keyid,
                           krp->krp_param[1].crp_p,
                           krp->krp_param[1].crp_nbits / 8);
        if (ret < 0)
          {
            krp->krp_status = ret;
          }
        else
          {
            krp->krp_param[1].crp_nbits = ret * 8;
          }

        break;
      case CRK_GENERATE_AES_KEY:
        if (krp->krp_param[1].crp_nbits != sizeof(uint32_t) * 8)
          {
            return -EINVAL;
          }

        keylen = *(uint32_t *)krp->krp_param[1].crp_p;
        if (keylen != 16 && keylen != 24 && keylen != 32)
          {
            return -EINVAL;
          }

        krp->krp_status = swkey_gen_aes_key(ctx, keyid, keylen);
        break;
      case CRK_GENERATE_SECP256R1_KEY:
        priv_keyid = keyid;
        if (krp->krp_param[1].crp_nbits != sizeof(uint32_t) * 8)
          {
            return -EINVAL;
          }

        pub_keyid = *(uint32_t *)krp->krp_param[1].crp_p;
        krp->krp_status = swkey_gen_secp256r1_key(ctx, priv_keyid,
                                                       pub_keyid);
        break;
      case CRK_SAVE_KEY:
        krp->krp_status = swkey_save(ctx, keyid);
        break;
      case CRK_LOAD_KEY:
        krp->krp_status = swkey_load(ctx, keyid);
        break;
      case CRK_UNLOAD_KEY:
        krp->krp_status = swkey_unload(ctx, keyid);
        break;
      default:

        /* Unknown/unsupported operation */

        krp->krp_status = -EINVAL;
        break;
    }

  return OK;
}

/****************************************************************************
 * Name: swkey_context_init
 *
 * Description:
 *   Init software key ctx
 *
 ****************************************************************************/

static int swkey_context_init(FAR struct swkey_context_s *ctx)
{
  FAR struct swkey_data_s *data;
  int i;

  TAILQ_INIT(&ctx->head);
  for (i = 0; i < CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT_NSLOTS; i++)
    {
      data = (FAR struct swkey_data_s *)kmm_zalloc(sizeof(*data));
      if (data == NULL)
        {
          return -ENOMEM;
        }

      TAILQ_INSERT_HEAD(&ctx->head, data, next);
    }

  return OK;
}

/****************************************************************************
 * Name: swkey_context_cleanup
 *
 * Description:
 *   Cleanup software key ctx
 *
 ****************************************************************************/

static void swkey_context_cleanup(FAR struct swkey_context_s *ctx)
{
  FAR struct swkey_data_s *data;

  TAILQ_FOREACH(data, &ctx->head, next)
    {
      memset(data, 0, sizeof(struct swkey_data_s));
      kmm_free(data);
    }
}

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

/* key management operations */

/****************************************************************************
 * Name: swkey_init
 *
 * Description:
 *   Register software key management driver
 *
 ****************************************************************************/

void swkey_init(void)
{
  int swkey_id = crypto_get_driverid(CRYPTOCAP_F_KEY_MGMT);
  FAR struct swkey_context_s *ctx;
  int kalgs[CRK_ALGORITHM_MAX + 1];

  ctx = (FAR struct swkey_context_s *)kmm_zalloc(sizeof(*ctx));
  if (ctx == NULL)
    {
      return;
    }

  if (swkey_context_init(ctx))
    {
      swkey_context_cleanup(ctx);
      kmm_free(ctx);
      return;
    }

  crypto_driver_set_priv(swkey_id, ctx);

  memset(kalgs, 0, sizeof(kalgs));
  kalgs[CRK_ALLOCATE_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_VALIDATE_KEYID] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_IMPORT_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_DELETE_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_EXPORT_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_GENERATE_AES_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_GENERATE_SECP256R1_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_SAVE_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_LOAD_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_UNLOAD_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;

  crypto_kregister(swkey_id, kalgs, swkey_kprocess);
}

#endif /* CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT */

#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_CRYPTO

/* Apply a symmetric encryption/decryption algorithm. */

int swcr_encdec(FAR struct cryptop *crp, FAR struct cryptodesc *crd,
                FAR struct swcr_data *sw, caddr_t buf)
{
  FAR char *output;
  unsigned char blk[EALG_MAX_BLOCK_LEN];
  FAR unsigned char *iv;
  FAR unsigned char *ivp;
  FAR unsigned char *nivp;
  unsigned char iv2[EALG_MAX_BLOCK_LEN];
  FAR const struct enc_xform *exf;
  int i;
  int j;
  int blks;
  int ivlen;

  exf = sw->sw_exf;
  blks = exf->blocksize;
  ivlen = exf->ivsize;

  /* Initialize the IV */

  if (crd->crd_flags & CRD_F_ENCRYPT)
    {
      /* Do we need to write the IV */

      if (!(crd->crd_flags & CRD_F_IV_PRESENT))
        {
          arc4random_buf(crd->crd_iv, ivlen);
          bcopy(crd->crd_iv, buf + crd->crd_inject, ivlen);
        }
    }
  else
    {
      /* Decryption */

      /* IV explicitly provided ? */

      if (!(crd->crd_flags & CRD_F_IV_EXPLICIT))
        {
          /* Get IV off buf */

          bcopy(buf + crd->crd_inject, crd->crd_iv, ivlen);
        }
    }

  iv = crd->crd_iv;
  ivp = iv;

  /* xforms that provide a reinit method perform all IV
   * handling themselves.
   */

  if (exf->reinit && !(crd->crd_flags & CRD_F_UPDATE))
    {
      exf->reinit((caddr_t)sw->sw_kschedule, iv);
    }

  i = crd->crd_len;

  buf = buf + crd->crd_skip;
  output = crp->crp_dst;
  while (i > 0)
    {
      bcopy(buf, blk, exf->blocksize);
      buf += exf->blocksize;
      if (exf->reinit)
        {
          if (crd->crd_flags & CRD_F_ENCRYPT)
            {
              exf->encrypt((caddr_t)sw->sw_kschedule,
                  blk);
            }
          else
            {
              exf->decrypt((caddr_t)sw->sw_kschedule,
                  blk);
            }
        }
      else if (crd->crd_flags & CRD_F_ENCRYPT)
        {
          /* XOR with previous block */

          for (j = 0; j < blks; j++)
            blk[j] ^= ivp[j];

          exf->encrypt((caddr_t)sw->sw_kschedule, blk);

          /* Keep encrypted block for XOR'ng
           * with next block
           */

          bcopy(blk, iv, blks);
          ivp = iv;
        }
      else
        {
          /* decrypt */

          /* Keep encrypted block for XOR'ing
           * with next block
           */

          nivp = (ivp == iv) ? iv2 : iv;
          bcopy(blk, nivp, blks);

          exf->decrypt((caddr_t)sw->sw_kschedule, blk);

          /* XOR with previous block */

          for (j = 0; j < blks; j++)
            {
              blk[j] ^= ivp[j];
            }

          ivp = nivp;
        }

      bcopy(blk, output, exf->blocksize);
      output += exf->blocksize;

      i -= blks;

      /* Could be done... */

      if (i == 0)
        {
          break;
        }
    }

  if (crd->crd_alg == CRYPTO_AES_CBC)
    {
      /* AES-CBC need update IV */

      bcopy(((FAR AES_CTX *)sw->sw_kschedule)->iv, crp->crp_iv, ivlen);
    }

  return 0; /* Done with encryption/decryption */
}

/* Compute keyed-hash authenticator. */

int swcr_authcompute(FAR struct cryptop *crp,
                     FAR struct cryptodesc *crd,
                     FAR struct swcr_data *sw,
                     caddr_t buf)
{
  unsigned char aalg[AALG_MAX_RESULT_LEN];
  FAR const struct auth_hash *axf = sw->sw_axf;
  int err;

  if (sw->sw_ictx == 0)
    {
      return -EINVAL;
    }

  if (crd->crd_flags & CRD_F_UPDATE)
    {
      err = axf->update(&sw->sw_ctx, (FAR uint8_t *)buf + crd->crd_skip,
                        crd->crd_len);
      if (err)
        {
          return err;
        }
    }
  else
    {
      axf->final((FAR uint8_t *)crp->crp_mac, &sw->sw_ctx);
      return 0;
    }

  if (crd->crd_flags & CRD_F_ESN)
    {
      axf->update(&sw->sw_ctx, crd->crd_esn, 4);
    }

  axf->final(aalg, &sw->sw_ctx);
  bcopy(sw->sw_octx, &sw->sw_ctx, axf->ctxsize);
  return axf->update(&sw->sw_ctx, aalg, axf->hashsize);
}

int swcr_hash(FAR struct cryptop *crp,
              FAR struct cryptodesc *crd,
              FAR struct swcr_data *sw,
              caddr_t buf)
{
  FAR const struct auth_hash *axf = sw->sw_axf;

  if (crd->crd_flags & CRD_F_UPDATE)
    {
      return axf->update(&sw->sw_ctx, (FAR uint8_t *)buf + crd->crd_skip,
                         crd->crd_len);
    }
  else
    {
      axf->final((FAR uint8_t *)crp->crp_mac, &sw->sw_ctx);
    }

  return 0;
}

/* Apply a combined encryption-authentication transformation */

int swcr_authenc(FAR struct cryptop *crp)
{
  uint32_t blkbuf[div_round_up(EALG_MAX_BLOCK_LEN, sizeof(uint32_t))];
  FAR u_char *blk = (u_char *)blkbuf;
  u_char aalg[AALG_MAX_RESULT_LEN];
  u_char iv[EALG_MAX_BLOCK_LEN];
  union authctx ctx;
  FAR struct cryptodesc *crd;
  FAR struct cryptodesc *crda = NULL;
  FAR struct cryptodesc *crde = NULL;
  FAR struct swcr_data *sw;
  FAR struct swcr_data *swa = NULL;
  FAR struct swcr_data *swe = NULL;
  FAR const struct auth_hash *axf = NULL;
  FAR const struct enc_xform *exf = NULL;
  caddr_t buf = (caddr_t)crp->crp_buf;
  caddr_t aad = (caddr_t)crp->crp_aad;
  FAR uint32_t *blkp;
  int blksz = 0;
  int ivlen = 0;
  int iskip = 0;
  int oskip = 0;
  int aadlen;
  int len;
  int i;

  for (crd = crp->crp_desc; crd; crd = crd->crd_next)
    {
      for (sw = swcr_sessions[crp->crp_sid & 0xffffffff];
           sw && sw->sw_alg != crd->crd_alg;
           sw = sw->sw_next);

      if (sw == NULL)
        {
          return -EINVAL;
        }

      switch (sw->sw_alg)
        {
          case CRYPTO_AES_GCM_16:
          case CRYPTO_AES_GMAC:
          case CRYPTO_AES_CMAC:
          case CRYPTO_CHACHA20_POLY1305:
          swe = sw;
          crde = crd;
          exf = swe->sw_exf;
          ivlen = exf->ivsize;
          break;
          case CRYPTO_AES_128_GMAC:
          case CRYPTO_AES_192_GMAC:
          case CRYPTO_AES_256_GMAC:
          case CRYPTO_AES_128_CMAC:
          case CRYPTO_CHACHA20_POLY1305_MAC:
          swa = sw;
          crda = crd;
          axf = swa->sw_axf;
          bcopy(&swa->sw_ctx, &ctx, axf->ctxsize);
          blksz = axf->blocksize;
          break;
          default:
            return -EINVAL;
        }
    }

  if (crde == NULL || crda == NULL)
    {
      return -EINVAL;
    }

  /* Initialize the IV */

  if (crde->crd_flags & CRD_F_ENCRYPT)
    {
      /* IV explicitly provided ? */

      if (crde->crd_flags & CRD_F_IV_EXPLICIT)
        {
          bcopy(crde->crd_iv, iv, ivlen);
        }
      else
        {
          arc4random_buf(iv, ivlen);
        }

      if (!((crde->crd_flags) & CRD_F_IV_PRESENT))
        {
          bcopy(iv, buf + crde->crd_inject, ivlen);
        }
    }
  else
    {
        /* Decryption */

        /* IV explicitly provided ? */

      if (crde->crd_flags & CRD_F_IV_EXPLICIT)
        {
          bcopy(crde->crd_iv, iv, ivlen);
        }
      else
        {
          /* Get IV off buf */

          bcopy(iv, buf + crde->crd_inject, ivlen);
        }
    }

  /* Supply MAC with IV */

  if (axf->reinit)
    {
      axf->reinit(&ctx, iv, ivlen);
    }

  /* Supply MAC with AAD */

  if (aad)
    {
      aadlen = crda->crd_len;
      /* Section 5 of RFC 4106 specifies that AAD construction consists of
      * {SPI, ESN, SN} whereas the real packet contains only {SPI, SN}.
      * Unfortunately it doesn't follow a good example set in the Section
      * 3.3.2.1 of RFC 4303 where upper part of the ESN, located in the
      * external (to the packet) memory buffer, is processed by the hash
      * function in the end thus allowing to retain simple programming
      * interfaces and avoid kludges like the one below.
      */

      if (crda->crd_flags & CRD_F_ESN)
        {
          aadlen += 4;

          /* SPI */

          bcopy(buf + crda->crd_skip, blk, 4);
          iskip = 4; /* loop below will start with an offset of 4 */

          /* ESN */

          bcopy(crda->crd_esn, blk + 4, 4);
          oskip = iskip + 4; /* offset output buffer blk by 8 */
        }

      for (i = iskip; i < crda->crd_len; i += axf->hashsize)
        {
          len = MIN(crda->crd_len - i, axf->hashsize - oskip);
          bcopy(buf + crda->crd_skip + i, blk + oskip, len);
          bzero(blk + len + oskip, axf->hashsize - len - oskip);
          axf->update(&ctx, blk, axf->hashsize);
          oskip = 0; /* reset initial output offset */
        }
    }

  if (exf->reinit)
    {
      exf->reinit((caddr_t)swe->sw_kschedule, iv);
    }

  /* Do encryption/decryption with MAC */

  if (buf)
    {
      for (i = 0; i < crde->crd_len; i += blksz)
        {
          len = MIN(crde->crd_len - i, blksz);
          if (len < blksz)
            {
              bzero(blk, blksz);
            }

          bcopy(buf + i, blk, len);
          if (crde->crd_flags & CRD_F_ENCRYPT)
            {
              exf->encrypt((caddr_t)swe->sw_kschedule, blk);
              axf->update(&ctx, blk, len);
            }
          else
            {
              axf->update(&ctx, blk, len);
              exf->decrypt((caddr_t)swe->sw_kschedule, blk);
            }

          if (crp->crp_dst)
            {
              bcopy(blk, crp->crp_dst + i, len);
            }
        }

      bcopy(&ctx, &swa->sw_ctx, axf->ctxsize);
    }

  /* Do any required special finalization */

  if (crp->crp_mac)
    {
      switch (crda->crd_alg)
        {
          case CRYPTO_AES_128_GMAC:
          case CRYPTO_AES_192_GMAC:
          case CRYPTO_AES_256_GMAC:

            /* length block */

            bzero(blk, axf->hashsize);
            blkp = (uint32_t *)blk + 1;
            *blkp = htobe32(aadlen * 8);
            blkp = (uint32_t *)blk + 3;
            *blkp = htobe32(crde->crd_len * 8);
            axf->update(&ctx, blk, axf->hashsize);
            break;

          case CRYPTO_CHACHA20_POLY1305_MAC:

            /* length block */

            bzero(blk, axf->hashsize);
            blkp = (uint32_t *)blk;
            *blkp = htole32(aadlen);
            blkp = (uint32_t *)blk + 2;
            *blkp = htole32(crde->crd_len);
            axf->update(&ctx, blk, axf->hashsize);
            break;
        }

      /* Finalize MAC */

      axf->final(aalg, &ctx);

      /* Inject the authentication data */

      bcopy(aalg, crp->crp_mac, axf->authsize);
    }

  return 0;
}

/* Apply a compression/decompression algorithm */

int swcr_compdec(FAR struct cryptodesc *crd, FAR struct swcr_data *sw,
                 caddr_t buf, int outtype)
{
  FAR uint8_t *data;
  FAR uint8_t *out;
  FAR const struct comp_algo *cxf;
  uint32_t result;

  cxf = sw->sw_cxf;

  /* We must handle the whole buffer of data in one time
   * then if there is not all the data in the mbuf, we must
   * copy in a buffer.
   */

  data = kmm_malloc(crd->crd_len);
  if (data == NULL)
    {
      return -EINVAL;
    }

  bcopy(buf + crd->crd_skip, data, crd->crd_len);

  if (crd->crd_flags & CRD_F_COMP)
    {
      result = cxf->compress(data, crd->crd_len, &out);
    }
  else
    {
      result = cxf->decompress(data, crd->crd_len, &out);
    }

  kmm_free(data);
  if (result == 0)
    {
      return -EINVAL;
    }

  sw->sw_size = result;

  /* Check the compressed size when doing compression */

  if (crd->crd_flags & CRD_F_COMP)
    {
      if (result > crd->crd_len)
        {
          /* Compression was useless, we lost time */

          kmm_free(out);
          return 0;
        }
    }

  bcopy(out, buf + crd->crd_skip, result);
  kmm_free(out);
  return 0;
}

/* Generate a new software session. */

int swcr_newsession(FAR uint32_t *sid, FAR struct cryptoini *cri)
{
  FAR struct swcr_data **swd;
  FAR const struct auth_hash *axf;
  FAR const struct enc_xform *txf;
  FAR uint8_t *key = (FAR uint8_t *)cri->cri_key;
  int keylen = cri->cri_klen / 8;
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
  FAR struct swkey_context_s *ctx = swkey_get_context();
  uint8_t keybuf[128];
#endif
  uint32_t i;
  int k;

  if (sid == NULL || cri == NULL)
    {
      return -EINVAL;
    }

  if (swcr_sessions)
    {
      for (i = 1; i < swcr_sesnum; i++)
        {
          if (swcr_sessions[i] == NULL)
            {
              break;
            }
        }
    }

  if (swcr_sessions == NULL || i == swcr_sesnum)
    {
      if (swcr_sessions == NULL)
        {
          i = 1; /* We leave swcr_sessions[0] empty */
          swcr_sesnum = CRYPTO_SW_SESSIONS;
        }
      else
        {
          swcr_sesnum *= 2;
        }

      swd = kmm_calloc(swcr_sesnum, sizeof(struct swcr_data *));
      if (swd == NULL)
        {
          /* Reset session number */

          if (swcr_sesnum == CRYPTO_SW_SESSIONS)
            {
              swcr_sesnum = 0;
            }
          else
            {
              swcr_sesnum /= 2;
            }

          return -ENOBUFS;
        }

      /* Copy existing sessions */

      if (swcr_sessions)
        {
          bcopy(swcr_sessions, swd,
              (swcr_sesnum / 2) * sizeof(struct swcr_data *));
          kmm_free(swcr_sessions);
        }

      swcr_sessions = swd;
    }

  swd = &swcr_sessions[i];
  *sid = i;

  while (cri)
    {
      *swd = kmm_zalloc(sizeof(struct swcr_data));
      if (*swd == NULL)
        {
          swcr_freesession(i);
          return -ENOBUFS;
        }

      switch (cri->cri_alg)
        {
          case CRYPTO_3DES_CBC:
            txf = &enc_xform_3des;
            goto enccommon;
          case CRYPTO_BLF_CBC:
            txf = &enc_xform_blf;
            goto enccommon;
          case CRYPTO_CAST_CBC:
            txf = &enc_xform_cast5;
            goto enccommon;
          case CRYPTO_AES_CBC:
            txf = &enc_xform_aes;
            goto enccommon;
          case CRYPTO_AES_CTR:
            txf = &enc_xform_aes_ctr;
            goto enccommon;
          case CRYPTO_AES_XTS:
            txf = &enc_xform_aes_xts;
            goto enccommon;
          case CRYPTO_AES_GCM_16:
            txf = &enc_xform_aes_gcm;
            goto enccommon;
          case CRYPTO_AES_GMAC:
            txf = &enc_xform_aes_gmac;
            (*swd)->sw_exf = txf;
            break;
          case CRYPTO_AES_CMAC:
            txf = &enc_xform_aes_cmac;
            (*swd)->sw_exf = txf;
            break;
          case CRYPTO_AES_OFB:
            txf = &enc_xform_aes_ofb;
            goto enccommon;
          case CRYPTO_AES_CFB_8:
            txf = &enc_xform_aes_cfb_8;
            goto enccommon;
          case CRYPTO_AES_CFB_128:
            txf = &enc_xform_aes_cfb_128;
            goto enccommon;
          case CRYPTO_CHACHA20_POLY1305:
            txf = &enc_xform_chacha20_poly1305;
            goto enccommon;
          case CRYPTO_NULL:
            txf = &enc_xform_null;
            goto enccommon;
          enccommon:
            if (txf->ctxsize > 0)
              {
                (*swd)->sw_kschedule = kmm_zalloc(txf->ctxsize);
                if ((*swd)->sw_kschedule == NULL)
                  {
                    swcr_freesession(i);
                    return -EINVAL;
                  }
              }

            if (cri->cri_flags & CRD_F_KEYID)
              {
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
                keylen = swkey_read(&ctx->file, *(uint32_t *)cri->cri_key,
                                    keybuf, sizeof(keybuf));
                if (keylen < 0)
                  {
                    swcr_freesession(i);
                    return keylen;
                  }

                key = keybuf;
#endif
              }

            if (keylen > txf->maxkey ||
                keylen < txf->minkey)
              {
                swcr_freesession(i);
                return -EINVAL;
              }

            if (txf->setkey((*swd)->sw_kschedule, key, keylen) < 0)
              {
                swcr_freesession(i);
                return -EINVAL;
              }

            (*swd)->sw_exf = txf;
            break;

          case CRYPTO_MD5_HMAC:
            axf = &auth_hash_hmac_md5_96;
            goto authcommon;
          case CRYPTO_SHA1_HMAC:
            axf = &auth_hash_hmac_sha1_96;
            goto authcommon;
          case CRYPTO_RIPEMD160_HMAC:
            axf = &auth_hash_hmac_ripemd_160_96;
            goto authcommon;
          case CRYPTO_SHA2_256_HMAC:
            axf = &auth_hash_hmac_sha2_256_128;
            goto authcommon;
          case CRYPTO_SHA2_384_HMAC:
            axf = &auth_hash_hmac_sha2_384_192;
            goto authcommon;
          case CRYPTO_SHA2_512_HMAC:
            axf = &auth_hash_hmac_sha2_512_256;
          authcommon:
            (*swd)->sw_ictx = kmm_malloc(axf->ctxsize);
            if ((*swd)->sw_ictx == NULL)
              {
                swcr_freesession(i);
                return -ENOBUFS;
              }

            (*swd)->sw_octx = kmm_malloc(axf->ctxsize);
            if ((*swd)->sw_octx == NULL)
              {
                swcr_freesession(i);
                return -ENOBUFS;
              }

            if (cri->cri_flags & CRD_F_KEYID)
              {
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
                keylen = swkey_read(&ctx->file, *(uint32_t *)cri->cri_key,
                                    keybuf, sizeof(keybuf));
                if (keylen < 0)
                  {
                    swcr_freesession(i);
                    return keylen;
                  }

                key = keybuf;
#endif
              }

            if (keylen / 8 > axf->keysize)
              {
                swcr_freesession(i);
                return -EINVAL;
              }

            for (k = 0; k < keylen; k++)
              {
                key[k] ^= HMAC_IPAD_VAL;
              }

            axf->init((*swd)->sw_ictx);
            axf->update((*swd)->sw_ictx, key, keylen);
            axf->update((*swd)->sw_ictx, hmac_ipad_buffer,
                        axf->blocksize - keylen);

            for (k = 0; k < keylen; k++)
              {
                key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
              }

            axf->init((*swd)->sw_octx);
            axf->update((*swd)->sw_octx, key, keylen);
            axf->update((*swd)->sw_octx, hmac_opad_buffer,
                        axf->blocksize - keylen);

            for (k = 0; k < keylen; k++)
              {
                key[k] ^= HMAC_OPAD_VAL;
              }

            (*swd)->sw_axf = axf;
            bcopy((*swd)->sw_ictx, &(*swd)->sw_ctx, axf->ctxsize);
            break;

          case CRYPTO_MD5:
            axf = &auth_hash_md5;
            goto auth3common;
          case CRYPTO_RIPEMD160:
            axf = &auth_hash_ripemd_160;
            goto auth3common;
          case CRYPTO_SHA1:
            axf = &auth_hash_sha1;
            goto auth3common;
          case CRYPTO_SHA2_224:
            axf = &auth_hash_sha2_224;
            goto auth3common;
          case CRYPTO_SHA2_256:
            axf = &auth_hash_sha2_256;
            goto auth3common;
          case CRYPTO_SHA2_384:
            axf = &auth_hash_sha2_384;
            goto auth3common;
          case CRYPTO_SHA2_512:
            axf = &auth_hash_sha2_512;

          auth3common:
            (*swd)->sw_ictx = kmm_zalloc(axf->ctxsize);
            if ((*swd)->sw_ictx == NULL)
              {
                swcr_freesession(i);
                return -ENOBUFS;
              }

            axf->init((*swd)->sw_ictx);
            (*swd)->sw_axf = axf;
            bcopy((*swd)->sw_ictx, &(*swd)->sw_ctx, axf->ctxsize);

            if (cri->cri_sid != -1)
              {
                if (swcr_sessions[cri->cri_sid] == NULL)
                  {
                    swcr_freesession(i);
                    return -EINVAL;
                  }

                bcopy(&swcr_sessions[cri->cri_sid]->sw_ctx, &(*swd)->sw_ctx,
                      axf->ctxsize);
              }
            break;

          case CRYPTO_AES_128_GMAC:
            axf = &auth_hash_gmac_aes_128;
            goto auth4common;

          case CRYPTO_AES_192_GMAC:
            axf = &auth_hash_gmac_aes_192;
            goto auth4common;

          case CRYPTO_AES_256_GMAC:
            axf = &auth_hash_gmac_aes_256;
            goto auth4common;

          case CRYPTO_AES_128_CMAC:
            axf = &auth_hash_cmac_aes_128;
            goto auth4common;

          case CRYPTO_POLY1305:
            axf = &auth_hash_poly1305;
            goto auth4common;

          case CRYPTO_CRC8:
            axf = &auth_hash_crc8;
            goto auth4common;

          case CRYPTO_CRC16:
            axf = &auth_hash_crc16;
            goto auth4common;

          case CRYPTO_CRC32:
            axf = &auth_hash_crc32;
            goto auth4common;

          case CRYPTO_CHACHA20_POLY1305_MAC:
            axf = &auth_hash_chacha20_poly1305;

          auth4common:
            (*swd)->sw_ictx = kmm_malloc(axf->ctxsize);
            if ((*swd)->sw_ictx == NULL)
              {
                swcr_freesession(i);
                return -ENOBUFS;
              }

            if (cri->cri_flags & CRD_F_KEYID)
              {
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
                keylen = swkey_read(&ctx->file, *(uint32_t *)cri->cri_key,
                                    keybuf, sizeof(keybuf));
                if (keylen < 0)
                  {
                    swcr_freesession(i);
                    return keylen;
                  }

                key = keybuf;
#endif
              }

            axf->init((*swd)->sw_ictx);
            axf->setkey((*swd)->sw_ictx, key, keylen);
            bcopy((*swd)->sw_ictx, &(*swd)->sw_ctx, axf->ctxsize);
            (*swd)->sw_axf = axf;
            break;

          case CRYPTO_ESN:

            /* nothing to do */

            break;
          default:
            swcr_freesession(i);
            return -EINVAL;
        }

      (*swd)->sw_alg = cri->cri_alg;
      cri = cri->cri_next;
      swd = &((*swd)->sw_next);
    }

  return 0;
}

/* Free a session. */

int swcr_freesession(uint64_t tid)
{
  FAR struct swcr_data *swd;
  FAR const struct enc_xform *txf;
  FAR const struct auth_hash *axf;
  uint32_t sid = ((uint32_t) tid) & 0xffffffff;

  if (sid > swcr_sesnum || swcr_sessions == NULL ||
      swcr_sessions[sid] == NULL)
    {
      return -EINVAL;
    }

  /* Silently accept and return */

  if (sid == 0)
    {
      return 0;
    }

  while ((swd = swcr_sessions[sid]) != NULL)
    {
      swcr_sessions[sid] = swd->sw_next;

      switch (swd->sw_alg)
        {
          case CRYPTO_3DES_CBC:
          case CRYPTO_BLF_CBC:
          case CRYPTO_CAST_CBC:
          case CRYPTO_RIJNDAEL128_CBC:
          case CRYPTO_AES_CTR:
          case CRYPTO_AES_XTS:
          case CRYPTO_AES_GCM_16:
          case CRYPTO_AES_GMAC:
          case CRYPTO_AES_CMAC:
          case CRYPTO_AES_OFB:
          case CRYPTO_AES_CFB_8:
          case CRYPTO_AES_CFB_128:
          case CRYPTO_CHACHA20_POLY1305:
          case CRYPTO_NULL:
            txf = swd->sw_exf;

            if (swd->sw_kschedule)
            {
              explicit_bzero(swd->sw_kschedule, txf->ctxsize);
              kmm_free(swd->sw_kschedule);
            }

            break;

          case CRYPTO_MD5_HMAC:
          case CRYPTO_SHA1_HMAC:
          case CRYPTO_RIPEMD160_HMAC:
          case CRYPTO_SHA2_256_HMAC:
          case CRYPTO_SHA2_384_HMAC:
          case CRYPTO_SHA2_512_HMAC:
            axf = swd->sw_axf;

            if (swd->sw_ictx)
              {
                explicit_bzero(swd->sw_ictx, axf->ctxsize);
                kmm_free(swd->sw_ictx);
              }

            if (swd->sw_octx)
              {
                explicit_bzero(swd->sw_octx, axf->ctxsize);
                kmm_free(swd->sw_octx);
              }

            break;

          case CRYPTO_AES_128_GMAC:
          case CRYPTO_AES_192_GMAC:
          case CRYPTO_AES_256_GMAC:
          case CRYPTO_AES_128_CMAC:
          case CRYPTO_CHACHA20_POLY1305_MAC:
          case CRYPTO_MD5:
          case CRYPTO_POLY1305:
          case CRYPTO_RIPEMD160:
          case CRYPTO_SHA1:
          case CRYPTO_SHA2_224:
          case CRYPTO_SHA2_256:
          case CRYPTO_SHA2_384:
          case CRYPTO_SHA2_512:
          case CRYPTO_CRC8:
          case CRYPTO_CRC16:
          case CRYPTO_CRC32:
            axf = swd->sw_axf;

            if (swd->sw_ictx)
              {
                explicit_bzero(swd->sw_ictx, axf->ctxsize);
                kmm_free(swd->sw_ictx);
              }

            break;
          }

      kmm_free(swd);
    }

  return 0;
}

/* Process a software request. */

int swcr_process(struct cryptop *crp)
{
  FAR const struct enc_xform *txf;
  FAR struct cryptodesc *crd;
  FAR struct swcr_data *sw;
  uint32_t lid;

  /* Sanity check */

  if (crp == NULL)
    {
      return -EINVAL;
    }

  if (crp->crp_desc == NULL)
    {
      crp->crp_etype = -EINVAL;
      goto done;
    }

  lid = crp->crp_sid & 0xffffffff;
  if (lid >= swcr_sesnum || lid == 0 || swcr_sessions[lid] == NULL)
    {
      crp->crp_etype = -ENOENT;
      goto done;
    }

  /* Go through crypto descriptors, processing as we go */

  for (crd = crp->crp_desc; crd; crd = crd->crd_next)
    {
      /* Find the crypto context.
       * XXX Note that the logic here prevents us from having
       * XXX the same algorithm multiple times in a session
       * XXX (or rather, we can but it won't give us the right
       * XXX results). To do that, we'd need some way of differentiating
       * XXX between the various instances of an algorithm (so we can
       * XXX locate the correct crypto context).
       */

      for (sw = swcr_sessions[lid];
           sw && sw->sw_alg != crd->crd_alg;
           sw = sw->sw_next);

      /* No such context ? */

      if (sw == NULL)
        {
          crp->crp_etype = -EINVAL;
          goto done;
        }

      switch (sw->sw_alg)
        {
          case CRYPTO_NULL:
            {
              break;
            }

          case CRYPTO_3DES_CBC:
          case CRYPTO_BLF_CBC:
          case CRYPTO_CAST_CBC:
          case CRYPTO_RIJNDAEL128_CBC:
          case CRYPTO_AES_CTR:
          case CRYPTO_AES_XTS:
          case CRYPTO_AES_OFB:
          case CRYPTO_AES_CFB_8:
          case CRYPTO_AES_CFB_128:
            txf = sw->sw_exf;

            if (crp->crp_iv)
              {
                if (!(crd->crd_flags & CRD_F_IV_EXPLICIT))
                  {
                    bcopy(crp->crp_iv, crd->crd_iv, txf->ivsize);
                    crd->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT;
                    crd->crd_skip = 0;
                  }
              }
            else
              {
                crd->crd_flags |= CRD_F_IV_PRESENT;
                crd->crd_skip = txf->blocksize;
                crd->crd_len -= txf->blocksize;
              }

            if ((crp->crp_etype = swcr_encdec(crp, crd, sw,
                crp->crp_buf)) != 0)
              {
                goto done;
              }

            break;
          case CRYPTO_MD5_HMAC:
          case CRYPTO_SHA1_HMAC:
          case CRYPTO_RIPEMD160_HMAC:
          case CRYPTO_SHA2_256_HMAC:
          case CRYPTO_SHA2_384_HMAC:
          case CRYPTO_SHA2_512_HMAC:
            if ((crp->crp_etype = swcr_authcompute(crp, crd, sw,
                crp->crp_buf)) != 0)
              {
                goto done;
              }

            break;

          case CRYPTO_MD5:
          case CRYPTO_POLY1305:
          case CRYPTO_RIPEMD160:
          case CRYPTO_SHA1:
          case CRYPTO_SHA2_224:
          case CRYPTO_SHA2_256:
          case CRYPTO_SHA2_384:
          case CRYPTO_SHA2_512:
          case CRYPTO_CRC8:
          case CRYPTO_CRC16:
          case CRYPTO_CRC32:
            if ((crp->crp_etype = swcr_hash(crp, crd, sw,
                crp->crp_buf)) != 0)
              {
                goto done;
              }

            break;

          case CRYPTO_AES_GCM_16:
          case CRYPTO_AES_GMAC:
          case CRYPTO_AES_128_GMAC:
          case CRYPTO_AES_192_GMAC:
          case CRYPTO_AES_256_GMAC:
          case CRYPTO_AES_128_CMAC:
          case CRYPTO_CHACHA20_POLY1305:
          case CRYPTO_CHACHA20_POLY1305_MAC:
            crp->crp_etype = swcr_authenc(crp);
            goto done;
            break;

          default:

            /* Unknown/unsupported algorithm */

            crp->crp_etype = -EINVAL;
            goto done;
        }
    }

done:
  return 0;
}

int swcr_mod_exp(FAR struct cryptkop *krp)
{
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
  FAR struct swkey_context_s *ctx = swkey_get_context();
#endif
  FAR struct crparam *src;
  FAR struct crparam *dst;
  FAR struct crparam *e;
  FAR struct crparam *n;
  struct bn bna;
  struct bn bne;
  struct bn bnn;
  struct bn bnr;

  bignum_init(&bna);
  bignum_init(&bne);
  bignum_init(&bnn);
  bignum_init(&bnr);

  src = &krp->krp_param[0];
  dst = &krp->krp_param[krp->krp_iparams +
                        krp->krp_oparams - 1];
  n = &krp->krp_param[1];
  e = &krp->krp_param[2];
  if (krp->krp_flags & CRD_F_KEYID)
    {
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
      bnn.length = swkey_read(&ctx->file, *(uint32_t *)n->crp_p,
                              bnn.array, sizeof(bnn.array));
      if (bnn.length < 0)
        {
          return bnn.length;
        }

      bne.length = swkey_read(&ctx->file, *(uint32_t *)e->crp_p,
                              bne.array, sizeof(bne.array));
      if (bne.length < 0)
        {
          return bne.length;
        }
#endif
    }
  else
    {
      bnn.length = n->crp_nbits / 8;
      bne.length = e->crp_nbits / 8;
      memcpy(bne.array, e->crp_p, bne.length);
      memcpy(bnn.array, n->crp_p, bnn.length);
      bignum_update_length(&bne);
      bignum_update_length(&bnn);
    }

  bna.length = src->crp_nbits / 8;
  memcpy(bna.array, src->crp_p, bna.length);
  bignum_update_length(&bna);

  pow_mod_faster(&bna, &bne, &bnn, &bnr);
  memcpy(dst->crp_p, bnr.array, dst->crp_nbits / 8);
  return 0;
}

int swcr_mod_exp_crt(FAR struct cryptkop *krp)
{
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
  FAR struct swkey_context_s *ctx = swkey_get_context();
#endif
  FAR struct crparam *src;
  FAR struct crparam *dst;
  FAR struct crparam *p;
  FAR struct crparam *q;
  FAR struct crparam *dp;
  FAR struct crparam *dq;
  FAR struct crparam *qp;
  struct bn bna;
  struct bn bnp;
  struct bn bnq;
  struct bn bndp;
  struct bn bndq;
  struct bn bnqp;
  struct bn bnr;

  bignum_init(&bna);
  bignum_init(&bnp);
  bignum_init(&bnq);
  bignum_init(&bndp);
  bignum_init(&bndq);
  bignum_init(&bnqp);
  bignum_init(&bnr);

  src = &krp->krp_param[0];
  dst = &krp->krp_param[krp->krp_iparams +
                        krp->krp_oparams - 1];
  p = &krp->krp_param[4];
  q = &krp->krp_param[5];
  dp = &krp->krp_param[6];
  dq = &krp->krp_param[7];
  qp = &krp->krp_param[8];
  if (krp->krp_flags & CRD_F_KEYID)
    {
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
      bnp.length = swkey_read(&ctx->file, *(uint32_t *)p->crp_p,
                              bnp.array, sizeof(bnp.array));
      if (bnp.length < 0)
        {
          return bnp.length;
        }

      bnq.length = swkey_read(&ctx->file, *(uint32_t *)q->crp_p,
                              bnq.array, sizeof(bnq.array));
      if (bnq.length < 0)
        {
          return bnq.length;
        }

      bndp.length = swkey_read(&ctx->file, *(uint32_t *)dp->crp_p,
                                bndp.array, sizeof(bndp.array));
      if (bndp.length < 0)
        {
          return bndp.length;
        }

      bndq.length = swkey_read(&ctx->file, *(uint32_t *)dq->crp_p,
                                bndq.array, sizeof(bndq.array));
      if (bndq.length < 0)
        {
          return bndq.length;
        }

      bnqp.length = swkey_read(&ctx->file, *(uint32_t *)qp->crp_p,
                                bnqp.array, sizeof(bnqp.array));
      if (bnqp.length < 0)
        {
          return bnqp.length;
        }
#endif
    }
  else
    {
      bnp.length = p->crp_nbits / 8;
      bnq.length = q->crp_nbits / 8;
      bndp.length = dp->crp_nbits / 8;
      bndq.length = dq->crp_nbits / 8;
      bnqp.length = qp->crp_nbits / 8;
      memcpy(bnp.array, p->crp_p, bnp.length);
      memcpy(bnq.array, q->crp_p, bnq.length);
      memcpy(bndp.array, dp->crp_p, bndp.length);
      memcpy(bndq.array, dq->crp_p, bndq.length);
      memcpy(bnqp.array, qp->crp_p, bnqp.length);
      bignum_update_length(&bnp);
      bignum_update_length(&bnq);
      bignum_update_length(&bndp);
      bignum_update_length(&bndq);
      bignum_update_length(&bnqp);
    }

  bna.length = src->crp_nbits / 8;
  memcpy(bna.array, src->crp_p, bna.length);
  bignum_update_length(&bna);

  pow_mod_crt(&bna, &bnp, &bnq, &bndp, &bndq, &bnqp, &bnr);
  memcpy(dst->crp_p, bnr.array, dst->crp_nbits / 8);
  return 0;
}

static int swcr_dh_make_public(FAR struct cryptkop *krp)
{
  /* Curve25519 is used for testing. In fact,
   * the four parameters of this interface are p, g, x, gx;
   * p: used to determine the conic curve;
   * g: the base point of the curve;
   * x: the private key produced by random;
   * gx: the public key generated by the private key,
   *  which could be caculated by gx = g ^ x mod p;
   * In curve25519, p and g are fixed.
   */

  uint8_t *secret = (uint8_t *)krp->krp_param[2].crp_p;
  uint8_t *public = (uint8_t *)krp->krp_param[3].crp_p;

  curve25519_generate_secret(secret);
  return curve25519_generate_public(public, secret);
}

static int swcr_dh_make_common(FAR struct cryptkop *krp)
{
  /* Curve25519 is used for testing. In fact,
   * the four parameters of this interface are:
   * public key / private key / p (the conic curve) / shared key
   */

  uint8_t *public = (uint8_t *)krp->krp_param[0].crp_p;
  uint8_t *secret = (uint8_t *)krp->krp_param[1].crp_p;
  uint8_t *shared = (uint8_t *)krp->krp_param[3].crp_p;
  return curve25519(shared, secret, public);
}

int swcr_rsa_verify(struct cryptkop *krp)
{
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
  FAR struct swkey_context_s *ctx = swkey_get_context();
#endif
  FAR struct crparam *src;
  FAR struct crparam *dst;
  FAR struct crparam *e;
  FAR struct crparam *n;
  struct bn bna;
  struct bn bne;
  struct bn bnn;
  struct bn bnr;

  bignum_init(&bna);
  bignum_init(&bne);
  bignum_init(&bnn);
  bignum_init(&bnr);

  src = &krp->krp_param[0];
  dst = &krp->krp_param[krp->krp_iparams +
                        krp->krp_oparams - 1];
  n = &krp->krp_param[1];
  e = &krp->krp_param[2];
  if (krp->krp_flags & CRD_F_KEYID)
    {
#ifdef CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_KEYMGMT
      bnn.length = swkey_read(&ctx->file, *(uint32_t *)n->crp_p,
                              bnn.array, sizeof(bnn.array));
      if (bnn.length < 0)
        {
          return bnn.length;
        }

      bne.length = swkey_read(&ctx->file, *(uint32_t *)e->crp_p,
                              bne.array, sizeof(bne.array));
      if (bne.length < 0)
        {
          return bne.length;
        }
#endif
    }
  else
    {
      bne.length = e->crp_nbits / 8;
      bnn.length = n->crp_nbits / 8;
      memcpy(bne.array, e->crp_p, bne.length);
      memcpy(bnn.array, n->crp_p, bnn.length);
      bignum_update_length(&bne);
      bignum_update_length(&bnn);
    }

  bna.length = src->crp_nbits / 8;
  memcpy(bna.array, src->crp_p, bna.length);
  bignum_update_length(&bna);

  pow_mod_faster(&bna, &bne, &bnn, &bnr);
  return memcmp(bnr.array, dst->crp_p, dst->crp_nbits / 8);
}

static int swcr_ecc256_genkey(FAR struct cryptkop *krp)
{
  uint8_t d[secp256r1];
  uint8_t x[secp256r1];
  uint8_t y[secp256r1];

  if (ecc_make_key_uncomp(x, y, d) == 0)
    {
      return -EINVAL;
    }

  memcpy(krp->krp_param[0].crp_p, d, secp256r1);
  memcpy(krp->krp_param[1].crp_p, x, secp256r1);
  memcpy(krp->krp_param[2].crp_p, y, secp256r1);
  return OK;
}

static int swcr_ecc256_sign(struct cryptkop *krp)
{
  uint8_t *d = (uint8_t *)krp->krp_param[0].crp_p;
  uint8_t *hash = (uint8_t *)krp->krp_param[1].crp_p;
  uint8_t sig[secp256r1 * 2];

  if (ecdsa_sign(d, hash, sig) == 0)
    {
      return -EINVAL;
    }

  memcpy(krp->krp_param[2].crp_p, sig, secp256r1);
  memcpy(krp->krp_param[3].crp_p, sig + secp256r1, secp256r1);
  return OK;
}

static int swcr_ecc256_verify(struct cryptkop *krp)
{
  uint8_t *x = (uint8_t *)krp->krp_param[0].crp_p;
  uint8_t *y = (uint8_t *)krp->krp_param[1].crp_p;
  uint8_t *r = (uint8_t *)krp->krp_param[3].crp_p;
  uint8_t *s = (uint8_t *)krp->krp_param[4].crp_p;
  uint8_t *hash = (uint8_t *)krp->krp_param[5].crp_p;
  uint8_t publickey[secp256r1 + 1];
  uint8_t signature[secp256r1 * 2];

  memcpy(publickey + 1, x, secp256r1);
  publickey[0] = 2 + (y[secp256r1 - 1] & 0x01);
  memcpy(signature, r, secp256r1);
  memcpy(signature + secp256r1, s, secp256r1);
  return ecdsa_verify(publickey, hash, signature) == 0;
}

int swcr_kprocess(struct cryptkop *krp)
{
  /* Sanity check */

  if (krp == NULL)
    {
      return -EINVAL;
    }

  /* Go through crypto descriptors, processing as we go */

  switch (krp->krp_op)
    {
      case CRK_MOD_EXP:
        if ((krp->krp_status = swcr_mod_exp(krp)) != 0)
          {
            goto done;
          }

        break;
      case CRK_MOD_EXP_CRT:
      case CRK_RSA_PKCS15_SIGN:
        if ((krp->krp_status = swcr_mod_exp_crt(krp)) != 0)
          {
            goto done;
          }

        break;
      case CRK_DH_MAKE_PUBLIC:
        if ((krp->krp_status = swcr_dh_make_public(krp) != 0))
          {
            goto done;
          }

        break;
      case CRK_DH_COMPUTE_KEY:
        if ((krp->krp_status = swcr_dh_make_common(krp)) != 0)
          {
            goto done;
          }

        break;
      case CRK_RSA_PKCS15_VERIFY:
        if ((krp->krp_status = swcr_rsa_verify(krp)) != 0)
          {
            goto done;
          }

        break;
      case CRK_ECDSA_SECP256R1_SIGN:
        if ((krp->krp_status = swcr_ecc256_sign(krp)) != 0)
          {
            goto done;
          }

        break;
      case CRK_ECDSA_SECP256R1_VERIFY:
        if ((krp->krp_status = swcr_ecc256_verify(krp)) != 0)
          {
            goto done;
          }

        break;
      case CRK_ECDSA_SECP256R1_GENKEY:
        if ((krp->krp_status = swcr_ecc256_genkey(krp)) != 0)
          {
            goto done;
          }

        break;
      default:

        /* Unknown/unsupported algorithm */

        krp->krp_status = -EINVAL;
        goto done;
    }

done:
  return 0;
}

/* Initialize the driver, called from the kernel main(). */

void swcr_init(void)
{
  int algs[CRYPTO_ALGORITHM_MAX + 1];
  int kalgs[CRK_ALGORITHM_MAX + 1];
  int flags = CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_ENCRYPT_MAC |
              CRYPTOCAP_F_MAC_ENCRYPT;

  swcr_id = crypto_get_driverid(flags);
  if (swcr_id < 0)
    {
      /* This should never happen */

      PANIC();
    }

  memset(algs, 0, sizeof(algs));
  algs[CRYPTO_3DES_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_BLF_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_CAST_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_MD5_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA1_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_RIPEMD160_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_RIJNDAEL128_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_CTR] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_XTS] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_GCM_16] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_NULL] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA2_256_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA2_384_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA2_512_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_128_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_192_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_256_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_OFB] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_CFB_8] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_CFB_128] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_CHACHA20_POLY1305] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_CHACHA20_POLY1305_MAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_MD5] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_POLY1305] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_RIPEMD160] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA1] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA2_224] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA2_256] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA2_384] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_SHA2_512] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_CRC8] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_CRC16] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_CRC32] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_CMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_AES_128_CMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
  algs[CRYPTO_ESN] = CRYPTO_ALG_FLAG_SUPPORTED;

  crypto_register(swcr_id, algs, swcr_newsession,
                  swcr_freesession, swcr_process);

  memset(kalgs, 0, sizeof(kalgs));
  kalgs[CRK_MOD_EXP] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_MOD_EXP_CRT] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_DH_MAKE_PUBLIC] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_DH_COMPUTE_KEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_RSA_PKCS15_SIGN] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_RSA_PKCS15_VERIFY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_ECDSA_SECP256R1_SIGN] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_ECDSA_SECP256R1_VERIFY] = CRYPTO_ALG_FLAG_SUPPORTED;
  kalgs[CRK_ECDSA_SECP256R1_GENKEY] = CRYPTO_ALG_FLAG_SUPPORTED;
  crypto_kregister(swcr_id, kalgs, swcr_kprocess);
}

#endif /* CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_CRYPTO */