nuttx/crypto/cryptosoft.c
makejian bdd68ed1e5 crypto: Add ChaCha20/ChaCha20-Poly1305 to /dev/crypto, fix RFC 8439 nonce.
Expose the ChaCha20 stream cipher and the ChaCha20-Poly1305 AEAD through
the OCF crypto framework (/dev/crypto) so applications such as an SSH
server (chacha20-poly1305) can use them directly, and fix the underlying
ChaCha nonce/counter layout so both match RFC 8439.

RFC 8439 nonce layout fix
-------------------------
chacha_ivsetup() previously used the original DJB layout: a 64-bit block
counter (input[12..13]) followed by a 64-bit nonce (input[14..15]). RFC
8439 defines a 32-bit block counter (input[12]) and a 96-bit / 12-byte
nonce (input[13..15]). With the old layout the existing ChaCha20-Poly1305
AEAD could not reproduce the RFC 8439 test vectors (the last 4 bytes of a
12-byte nonce were consumed as the high half of the counter). This
commit switches chacha_ivsetup() to the RFC 8439 layout and updates the
ChaCha20-Poly1305 one-shot helpers to pass a 12-byte nonce accordingly.

Standalone ChaCha20 on the unified enc path
-------------------------------------------
Instead of introducing a separate multi-buffer stream path (parallel
encrypt_multi/decrypt_multi callbacks), extend the existing enc_xform
encrypt/decrypt callback signature with a length argument:

  void (*encrypt)(caddr_t, FAR uint8_t *, size_t len);
  void (*decrypt)(caddr_t, FAR uint8_t *, size_t len);

With that single change every cipher, block or stream, flows through the
same swcr_encdec path. swcr_encdec already handles a short final block
via buflen = MIN(i, blocksize), so arbitrary-length data works without a
second code path. This is exactly how the existing stream ciphers
(AES-CTR/OFB/CFB) already behave: the cipher keeps its own counter in the
context and swcr_encdec feeds it whole blocks (only the last one may be
shorter). chacha20_crypt likewise relies on the underlying chacha state
block counter (input[12]) to continue the keystream across calls, so no
per-call keystream caching is needed.

  * chacha_private.h: chacha_ivsetup uses a 4-byte counter and a 12-byte
    nonce (RFC 8439).
  * chachapoly.c / chachapoly.h: split reinit into chacha20_reinit (raw,
    counter 0) and chachapoly_reinit (AEAD, counter 1); chacha20_crypt
    takes a length and encrypts it in one pass, mirroring aes_ctr_crypt;
    12-byte nonce for the one-shot AEAD helpers.
  * xform.h / xform.c: add size_t len to encrypt/decrypt; add
    enc_xform_chacha20 (blocksize 64, 12-byte IV).
  * cryptodev.c / cryptosoft.c: register CRYPTO_CHACHA20 as a txform
    cipher, route new sessions to enc_xform_chacha20, feed the AEAD AAD
    through crp_aad/crp_aadlen, and handle the short final block in
    swcr_encdec.
  * cryptodev.h: add CRYPTO_CHACHA20; bump EALG_MAX_BLOCK_LEN to 64.

This keeps all ciphers on one uniform path instead of maintaining two,
and any future stream cipher drops in with just an xform table entry.

Impact: extends an internal kernel callback signature (enc_xform
encrypt/decrypt). All in-tree implementations are updated in the same
commit and the user-facing /dev/crypto ABI is unchanged, so this is
self-contained and not a breaking change for existing configurations.

Testing:
  Build host: Ubuntu Linux x86_64, GCC (host sim toolchain)
  Target: sim:crypto (CONFIG_ARCH=sim)
  Ran the crypto test apps. ChaCha20 uses RFC 8439 2.4.2 vectors
  (including a 375-byte multi-block vector exercising cross-block counter
  continuity); ChaCha20-Poly1305 uses the RFC 8439 2.8.2 AEAD vector.
  A full regression of the other ciphers was run to confirm the extended
  encrypt/decrypt signature does not change their behaviour:

    nsh> chacha20
    chacha20: 2/2 vectors passed
    nsh> chachapoly
    OK test vector 0
    chachapoly: 1/1 vectors passed
    nsh> des3cbc          -> all vectors OK
    nsh> aescbc           -> all vectors OK
    nsh> aesctr           -> all vectors OK
    nsh> aesxts           -> 14 vectors OK (encrypt + decrypt)
    nsh> hmac             -> md5 / sha1 / sha256 all success

Signed-off-by: makejian <makejian@xiaomi.com>
2026-07-11 10:19:39 -03:00

2485 lines
63 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
* 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 ret;
}
/****************************************************************************
* 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);
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;
int buflen;
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 (!(crd->crd_flags & CRD_F_UPDATE) && exf->reinit)
{
exf->reinit((caddr_t)sw->sw_kschedule, iv);
}
i = crd->crd_len;
buf = buf + crd->crd_skip;
output = crp->crp_dst;
while (i > 0)
{
buflen = MIN(i, exf->blocksize);
bcopy(buf, blk, buflen);
buf += buflen;
if (exf->reinit)
{
if (crd->crd_flags & CRD_F_ENCRYPT)
{
exf->encrypt((caddr_t)sw->sw_kschedule,
blk, buflen);
}
else
{
exf->decrypt((caddr_t)sw->sw_kschedule,
blk, buflen);
}
}
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, buflen);
/* 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, buflen);
/* XOR with previous block */
for (j = 0; j < blks; j++)
{
blk[j] ^= ivp[j];
}
ivp = nivp;
}
bcopy(blk, output, buflen);
output += buflen;
i -= buflen;
/* Could be done... */
if (i == 0)
{
break;
}
}
if (crp->crp_iv)
{
bcopy(ivp, 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;
}
err = axf->update(&sw->sw_ctx, (FAR uint8_t *)buf + crd->crd_skip,
crd->crd_len);
if (err)
{
return err;
}
if (crd->crd_flags & CRD_F_ESN)
{
axf->update(&sw->sw_ctx, crd->crd_esn, 4);
}
switch (sw->sw_alg)
{
case CRYPTO_MD5_HMAC:
case CRYPTO_SHA1_HMAC:
case CRYPTO_RIPEMD160_HMAC:
case CRYPTO_SHA2_224_HMAC:
case CRYPTO_SHA2_256_HMAC:
case CRYPTO_SHA2_384_HMAC:
case CRYPTO_SHA2_512_HMAC:
case CRYPTO_PBKDF2_HMAC_SHA1:
case CRYPTO_PBKDF2_HMAC_SHA256:
if (sw->sw_octx == NULL)
{
return -EINVAL;
}
if (crd->crd_flags & CRD_F_UPDATE)
{
break;
}
axf->final(aalg, &sw->sw_ctx);
bcopy(sw->sw_octx, &sw->sw_ctx, axf->ctxsize);
axf->update(&sw->sw_ctx, aalg, axf->hashsize);
axf->final((FAR uint8_t *)crp->crp_mac, &sw->sw_ctx);
bcopy(sw->sw_ictx, &sw->sw_ctx, axf->ctxsize);
break;
}
return 0;
}
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;
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 aadlen = 0;
int blksz = 0;
int ivlen = 0;
int iskip = 0;
int oskip = 0;
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;
if (swa->sw_ictx == 0)
{
return -EINVAL;
}
bcopy(swa->sw_ictx, &ctx, axf->ctxsize);
blksz = axf->blocksize;
break;
default:
return -EINVAL;
}
}
if (crde == NULL || crda == NULL)
{
return -EINVAL;
}
/* Initialize the IV */
if (crp->crp_iv)
{
if (!(crde->crd_flags & CRD_F_IV_EXPLICIT))
{
bcopy(crp->crp_iv, crde->crd_iv, exf->ivsize);
crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT;
crde->crd_skip = 0;
}
}
else
{
crde->crd_flags |= CRD_F_IV_PRESENT;
crde->crd_skip = exf->blocksize;
crde->crd_len -= exf->blocksize;
}
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 (!(crda->crd_flags & CRD_F_UPDATE_AAD) && axf->reinit)
{
axf->reinit(&ctx, iv, ivlen);
}
/* Supply MAC with AAD */
if (aad)
{
aadlen = crp->crp_aadlen;
/* 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(aad + 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 < aadlen; i += axf->hashsize)
{
len = MIN(aadlen - i, axf->hashsize - oskip);
bcopy(aad + 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, len);
axf->update(&ctx, blk, len);
}
else
{
axf->update(&ctx, blk, len);
exf->decrypt((caddr_t)swe->sw_kschedule, blk, len);
}
if (crp->crp_dst)
{
bcopy(blk, crp->crp_dst + i, len);
}
}
}
/* 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;
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:
txf = &enc_xform_chacha20;
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_klen / 8 > txf->maxkey ||
cri->cri_klen / 8 < txf->minkey)
{
swcr_freesession(i);
return -EINVAL;
}
if (txf->setkey((*swd)->sw_kschedule,
(FAR uint8_t *)cri->cri_key,
cri->cri_klen / 8) < 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:
case CRYPTO_PBKDF2_HMAC_SHA1:
axf = &auth_hash_hmac_sha1_96;
goto authcommon;
case CRYPTO_RIPEMD160_HMAC:
axf = &auth_hash_hmac_ripemd_160_96;
goto authcommon;
case CRYPTO_SHA2_224_HMAC:
axf = &auth_hash_hmac_sha2_224_114;
goto authcommon;
case CRYPTO_SHA2_256_HMAC:
case CRYPTO_PBKDF2_HMAC_SHA256:
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 the key is too long, hash it first using ictx */
if (cri->cri_klen / 8 > axf->keysize)
{
axf->init((*swd)->sw_ictx);
axf->update((*swd)->sw_ictx,
(FAR uint8_t *)cri->cri_key,
cri->cri_klen / 8);
axf->final((unsigned char *)cri->cri_key,
(*swd)->sw_ictx);
cri->cri_klen = axf->hashsize * 8;
}
for (k = 0; k < cri->cri_klen / 8; k++)
{
cri->cri_key[k] ^= HMAC_IPAD_VAL;
}
axf->init((*swd)->sw_ictx);
axf->update((*swd)->sw_ictx, (FAR uint8_t *)cri->cri_key,
cri->cri_klen / 8);
axf->update((*swd)->sw_ictx, hmac_ipad_buffer,
axf->blocksize - (cri->cri_klen / 8));
for (k = 0; k < cri->cri_klen / 8; k++)
{
cri->cri_key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
}
axf->init((*swd)->sw_octx);
axf->update((*swd)->sw_octx, (FAR uint8_t *)cri->cri_key,
cri->cri_klen / 8);
axf->update((*swd)->sw_octx, hmac_opad_buffer,
axf->blocksize - (cri->cri_klen / 8));
for (k = 0; k < cri->cri_klen / 8; k++)
{
cri->cri_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_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;
}
axf->init((*swd)->sw_ictx);
axf->setkey((*swd)->sw_ictx, (FAR uint8_t *)cri->cri_key,
cri->cri_klen / 8);
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:
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_224_HMAC:
case CRYPTO_SHA2_256_HMAC:
case CRYPTO_SHA2_384_HMAC:
case CRYPTO_SHA2_512_HMAC:
case CRYPTO_PBKDF2_HMAC_SHA1:
case CRYPTO_PBKDF2_HMAC_SHA256:
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_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:
case CRYPTO_CHACHA20:
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_224_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_PBKDF2_HMAC_SHA1:
case CRYPTO_PBKDF2_HMAC_SHA256:
swcr_pbkdf2(crp, crd, sw, crp->crp_buf);
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_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_pbkdf2(FAR struct cryptop *crp,
FAR struct cryptodesc *crd,
FAR struct swcr_data *swd,
caddr_t buf)
{
uint8_t U[64];
uint8_t T[64];
uint8_t macbuf[64];
uint8_t ictx[256];
struct cryptop crp_dummy;
struct cryptodesc crd_dummy;
size_t generated = 0;
uint32_t blocknum;
uint32_t i;
uint32_t j;
crp_dummy.crp_mac = (caddr_t)macbuf;
for (blocknum = 1; generated < crp->crp_olen; blocknum++)
{
uint8_t saltblk[crp->crp_ilen + 4];
memcpy(saltblk, crp->crp_buf, crp->crp_ilen);
*(FAR uint32_t *)(saltblk + crp->crp_ilen) = htobe32(blocknum);
memcpy(ictx, swd->sw_ictx, swd->sw_axf->ctxsize);
memcpy(&swd->sw_ctx, ictx, swd->sw_axf->ctxsize);
crd_dummy.crd_skip = 0;
crd_dummy.crd_flags = 0;
/* U1 */
crd_dummy.crd_len = crp->crp_ilen + 4;
swcr_authcompute(&crp_dummy, &crd_dummy, swd, (caddr_t)saltblk);
memcpy(U, macbuf, swd->sw_axf->hashsize);
memcpy(T, U, swd->sw_axf->hashsize);
/* U2..Uc */
for (i = 1; i < crp->crp_iter; i++)
{
memcpy(&swd->sw_ctx, ictx, swd->sw_axf->ctxsize);
crd_dummy.crd_len = swd->sw_axf->hashsize;
swcr_authcompute(&crp_dummy, &crd_dummy, swd, (caddr_t)U);
memcpy(U, macbuf, swd->sw_axf->hashsize);
for (j = 0; j < swd->sw_axf->hashsize; j++)
{
T[j] ^= U[j];
}
}
size_t tocopy = MIN(crp->crp_olen - generated,
swd->sw_axf->hashsize);
memcpy(crp->crp_mac + generated, T, tocopy);
generated += tocopy;
}
return 0;
}
int swcr_mod_exp(struct cryptkop *krp)
{
uint8_t *input = (uint8_t *)krp->krp_param[0].crp_p;
uint8_t *exp = (uint8_t *)krp->krp_param[1].crp_p;
uint8_t *modulus = (uint8_t *)krp->krp_param[2].crp_p;
uint8_t *output = (uint8_t *)krp->krp_param[3].crp_p;
int input_len = krp->krp_param[0].crp_nbits / 8;
int exp_len = krp->krp_param[1].crp_nbits / 8;
int modulus_len = krp->krp_param[2].crp_nbits / 8;
int output_len = krp->krp_param[3].crp_nbits / 8;
struct bn a;
struct bn e;
struct bn n;
struct bn r;
bignum_init(&a);
bignum_init(&e);
bignum_init(&n);
bignum_init(&r);
memcpy(e.array, exp, exp_len);
memcpy(n.array, modulus, modulus_len);
memcpy(a.array, input, input_len);
pow_mod_faster(&a, &e, &n, &r);
memcpy(output, r.array, output_len);
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 calculated 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)
{
uint8_t *exp = (uint8_t *)krp->krp_param[0].crp_p;
uint8_t *modulus = (uint8_t *)krp->krp_param[1].crp_p;
uint8_t *sig = (uint8_t *)krp->krp_param[2].crp_p;
uint8_t *hash = (uint8_t *)krp->krp_param[3].crp_p;
uint8_t *padding = (uint8_t *)krp->krp_param[4].crp_p;
int exp_len = krp->krp_param[0].crp_nbits / 8;
int modulus_len = krp->krp_param[1].crp_nbits / 8;
int sig_len = krp->krp_param[2].crp_nbits / 8;
int hash_len = krp->krp_param[3].crp_nbits / 8;
int padding_len = krp->krp_param[4].crp_nbits / 8;
struct bn a;
struct bn e;
struct bn n;
struct bn r;
bignum_init(&a);
bignum_init(&e);
bignum_init(&n);
bignum_init(&r);
memcpy(e.array, exp, exp_len);
memcpy(n.array, modulus, modulus_len);
memcpy(a.array, sig, sig_len);
pow_mod_faster(&a, &e, &n, &r);
return !!memcmp(r.array, hash, hash_len) +
!!memcmp(r.array + hash_len, padding, padding_len);
}
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_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();
}
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_224_HMAC] = 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] = 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_CRC32] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_AES_CMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_AES_128_CMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_PBKDF2_HMAC_SHA1] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_PBKDF2_HMAC_SHA256] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_ESN] = CRYPTO_ALG_FLAG_SUPPORTED;
crypto_register(swcr_id, algs, swcr_newsession,
swcr_freesession, swcr_process);
kalgs[CRK_MOD_EXP] = 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_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 */