Files
nuttx/crypto/crypto.c
T
makejian 5b52a32f5a crypto/crypto.c: Determine the order of obtained crypto drivers
After adding the cross-core crypto driver, there are now three encryption modes:
1. Hardware driver in local core
2. Crypto driver in remote core
3. Software encryption in local core

This prioritizes local hardware driver first, then remote driver (typically hardware),
and finally local software encryption as a fallback.

Signed-off-by: makejian <makejian@xiaomi.com>
2026-01-21 00:25:07 +08:00

708 lines
16 KiB
C

/****************************************************************************
* crypto/crypto.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 <nuttx/config.h>
#include <sys/types.h>
#include <stdbool.h>
#include <string.h>
#include <poll.h>
#include <debug.h>
#include <errno.h>
#include <crypto/cryptodev.h>
#include <nuttx/fs/fs.h>
#include <nuttx/mutex.h>
#include <nuttx/kmalloc.h>
#include <nuttx/crypto/crypto.h>
/****************************************************************************
* Public Data
****************************************************************************/
FAR struct cryptocap *crypto_drivers = NULL;
int crypto_drivers_num = 0;
/****************************************************************************
* Private Data
****************************************************************************/
static mutex_t g_crypto_lock = NXMUTEX_INITIALIZER;
/****************************************************************************
* Public Functions
****************************************************************************/
/* Create a new session. */
int crypto_newsession(FAR uint64_t *sid,
FAR struct cryptoini *cri,
int hard)
{
uint32_t hid;
uint32_t lid;
uint32_t hid2 = -1;
FAR struct cryptocap *cpc;
FAR struct cryptoini *cr;
int turn = 0;
int err;
if (crypto_drivers == NULL)
{
return -EINVAL;
}
nxmutex_lock(&g_crypto_lock);
/* The algorithm we use here is pretty stupid; just use the
* first driver that supports all the algorithms we need. Do
* a double-pass over all the drivers, ignoring software ones
* at first, to deal with cases of drivers that register after
* the software one(s) --- e.g., PCMCIA crypto cards.
*
* XXX We need more smarts here (in real life too, but that's
* XXX another story altogether).
*/
do
{
for (hid = 0; hid < crypto_drivers_num; hid++)
{
cpc = &crypto_drivers[hid];
/* If it's not initialized or has remaining sessions
* referencing it, skip.
*/
if (cpc->cc_newsession == NULL ||
(cpc->cc_flags & CRYPTOCAP_F_CLEANUP))
{
continue;
}
if (cpc->cc_flags & CRYPTOCAP_F_SOFTWARE)
{
/* Thread round of search only for software */
if (turn != 2)
{
continue;
}
}
else if (cpc->cc_flags & CRYPTOCAP_F_REMOTE)
{
/* Second round of search only for remote */
if (turn != 1)
{
continue;
}
}
/* See if all the algorithms are supported. */
for (cr = cri; cr; cr = cr->cri_next)
{
if (cpc->cc_alg[cr->cri_alg] == 0)
{
break;
}
}
/* If even one algorithm is not supported,
* keep searching.
*/
if (cr != NULL)
{
continue;
}
/* If we had a previous match, see how it compares
* to this one. Keep "remembering" whichever is
* the best of the two.
*/
if (hid2 != -1)
{
/* Compare session numbers, pick the one
* with the lowest.
* XXX Need better metrics, this will
* XXX just do un-weighted round-robin.
*/
if (crypto_drivers[hid].cc_sessions <=
crypto_drivers[hid2].cc_sessions)
{
hid2 = hid;
}
}
else
{
/* Remember this one, for future
* comparisons.
*/
hid2 = hid;
}
}
/* If we found something worth remembering, leave. The
* side-effect is that we will always prefer a hardware
* driver over the software one.
*/
if (hid2 != -1)
{
break;
}
turn++;
/* If we only want hardware drivers, don't do second pass. */
}
while (turn < 2 || (turn == 2 && !hard));
hid = hid2;
/* Can't do everything in one session.
* XXX Fix this. We need to inject a "virtual" session
* XXX layer right about here.
*/
if (hid == -1)
{
nxmutex_unlock(&g_crypto_lock);
return -EINVAL;
}
/* Call the driver initialization routine. */
lid = hid; /* Pass the driver ID. */
err = crypto_drivers[hid].cc_newsession(&lid, cri);
if (err == 0)
{
*sid = hid;
*sid <<= 32;
*sid |= (lid & 0xffffffff);
crypto_drivers[hid].cc_sessions++;
}
nxmutex_unlock(&g_crypto_lock);
return err;
}
/* Delete an existing session (or a reserved session on an unregistered
* driver).
*/
int crypto_freesession(uint64_t sid)
{
int err = 0;
uint32_t hid;
if (crypto_drivers == NULL)
{
return -EINVAL;
}
/* Determine two IDs. */
hid = (sid >> 32) & 0xffffffff;
if (hid >= crypto_drivers_num)
{
return -ENOENT;
}
nxmutex_lock(&g_crypto_lock);
if (crypto_drivers[hid].cc_sessions)
{
crypto_drivers[hid].cc_sessions--;
}
/* Call the driver cleanup routine, if available. */
if (crypto_drivers[hid].cc_freesession)
{
err = crypto_drivers[hid].cc_freesession(sid);
}
/* If this was the last session of a driver marked as invalid,
* make the entry available for reuse.
*/
if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
crypto_drivers[hid].cc_sessions == 0)
{
explicit_bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
}
nxmutex_unlock(&g_crypto_lock);
return err;
}
/* Find an empty slot. */
int crypto_get_driverid(uint8_t flags)
{
FAR struct cryptocap *newdrv;
int i;
nxmutex_lock(&g_crypto_lock);
if (crypto_drivers_num == 0)
{
crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
crypto_drivers = kmm_calloc(crypto_drivers_num,
sizeof(struct cryptocap));
if (crypto_drivers == NULL)
{
crypto_drivers_num = 0;
nxmutex_unlock(&g_crypto_lock);
return -1;
}
bzero(crypto_drivers, crypto_drivers_num *
sizeof(struct cryptocap));
}
for (i = 0; i < crypto_drivers_num; i++)
{
if (crypto_drivers[i].cc_process == NULL &&
!(crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) &&
crypto_drivers[i].cc_sessions == 0)
{
crypto_drivers[i].cc_sessions = 1; /* Mark */
crypto_drivers[i].cc_flags = flags;
nxmutex_unlock(&g_crypto_lock);
return i;
}
}
/* Out of entries, allocate some more. */
if (i == crypto_drivers_num)
{
if (crypto_drivers_num >= CRYPTO_DRIVERS_MAX)
{
nxmutex_unlock(&g_crypto_lock);
return -1;
}
newdrv = kmm_calloc(crypto_drivers_num * 2,
sizeof(struct cryptocap));
if (newdrv == NULL)
{
nxmutex_unlock(&g_crypto_lock);
return -1;
}
bcopy(crypto_drivers, newdrv,
crypto_drivers_num * sizeof(struct cryptocap));
bzero(&newdrv[crypto_drivers_num],
crypto_drivers_num * sizeof(struct cryptocap));
newdrv[i].cc_sessions = 1; /* Mark */
newdrv[i].cc_flags = flags;
crypto_drivers_num *= 2;
kmm_free(crypto_drivers);
crypto_drivers = newdrv;
nxmutex_unlock(&g_crypto_lock);
return i;
}
/* Shouldn't really get here... */
nxmutex_unlock(&g_crypto_lock);
return -1;
}
/* Register a crypto driver. It should be called once for each algorithm
* supported by the driver.
*/
int crypto_kregister(uint32_t driverid, FAR int *kalg,
CODE int (*kprocess)(FAR struct cryptkop *))
{
int i;
if (driverid >= crypto_drivers_num || kalg == NULL ||
crypto_drivers == NULL)
{
return -EINVAL;
}
nxmutex_lock(&g_crypto_lock);
for (i = 0; i <= CRK_ALGORITHM_MAX; i++)
{
/* XXX Do some performance testing to determine
* placing. We probably need an auxiliary data
* structure that describes relative performances.
*/
crypto_drivers[driverid].cc_kalg[i] = kalg[i];
}
crypto_drivers[driverid].cc_kprocess = kprocess;
nxmutex_unlock(&g_crypto_lock);
return 0;
}
/* Register a crypto driver. */
int crypto_register(uint32_t driverid, FAR int *alg,
CODE int (*newses)(FAR uint32_t *,
FAR struct cryptoini *),
CODE int (*freeses)(uint64_t),
CODE int (*process)(FAR struct cryptop *))
{
int i;
if (driverid >= crypto_drivers_num || alg == NULL ||
crypto_drivers == NULL)
{
return -EINVAL;
}
nxmutex_lock(&g_crypto_lock);
for (i = 0; i <= CRYPTO_ALGORITHM_MAX; i++)
{
/* XXX Do some performance testing to determine
* placing. We probably need an auxiliary data
* structure that describes relative performances.
*/
crypto_drivers[driverid].cc_alg[i] = alg[i];
}
crypto_drivers[driverid].cc_newsession = newses;
crypto_drivers[driverid].cc_process = process;
crypto_drivers[driverid].cc_freesession = freeses;
crypto_drivers[driverid].cc_sessions = 0; /* Unmark */
nxmutex_unlock(&g_crypto_lock);
return 0;
}
/* Unregister a crypto driver. If there are pending sessions using it,
* leave enough information around so that subsequent calls using those
* sessions will correctly detect the driver being unregistered and reroute
* the request.
*/
int crypto_unregister(uint32_t driverid, int alg)
{
int i = CRYPTO_ALGORITHM_MAX + 1;
uint32_t ses;
nxmutex_lock(&g_crypto_lock);
/* Sanity checks. */
if (driverid >= crypto_drivers_num || crypto_drivers == NULL ||
alg <= 0 || alg > (CRYPTO_ALGORITHM_MAX + 1))
{
nxmutex_unlock(&g_crypto_lock);
return -EINVAL;
}
if (alg != CRYPTO_ALGORITHM_MAX + 1)
{
if (crypto_drivers[driverid].cc_alg[alg] == 0)
{
nxmutex_unlock(&g_crypto_lock);
return -EINVAL;
}
crypto_drivers[driverid].cc_alg[alg] = 0;
/* Was this the last algorithm ? */
for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
{
if (crypto_drivers[driverid].cc_alg[i] != 0)
{
break;
}
}
}
/* If a driver unregistered its last algorithm or all of them
* (alg == CRYPTO_ALGORITHM_MAX + 1), cleanup its entry.
*/
if (i == CRYPTO_ALGORITHM_MAX + 1 || alg == CRYPTO_ALGORITHM_MAX + 1)
{
ses = crypto_drivers[driverid].cc_sessions;
bzero(&crypto_drivers[driverid], sizeof(struct cryptocap));
if (ses != 0)
{
/* If there are pending sessions, just mark as invalid. */
crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
crypto_drivers[driverid].cc_sessions = ses;
}
}
nxmutex_unlock(&g_crypto_lock);
return 0;
}
/* Dispatch an asymmetric crypto request to the appropriate crypto devices. */
int crypto_kinvoke(FAR struct cryptkop *krp)
{
extern int cryptodevallowsoft;
uint32_t hid;
int error;
/* Sanity checks. */
if (krp == NULL)
{
return -EINVAL;
}
nxmutex_lock(&g_crypto_lock);
for (hid = 0; hid < crypto_drivers_num; hid++)
{
if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
cryptodevallowsoft == 0)
{
continue;
}
if (crypto_drivers[hid].cc_kprocess == NULL)
{
continue;
}
if ((crypto_drivers[hid].cc_kalg[krp->krp_op] &
CRYPTO_ALG_FLAG_SUPPORTED) == 0)
{
continue;
}
break;
}
if (hid == crypto_drivers_num)
{
krp->krp_status = -ENODEV;
nxmutex_unlock(&g_crypto_lock);
return 0;
}
krp->krp_hid = hid;
crypto_drivers[hid].cc_koperations++;
error = crypto_drivers[hid].cc_kprocess(krp);
if (error)
{
krp->krp_status = error;
}
nxmutex_unlock(&g_crypto_lock);
return 0;
}
/* Dispatch a crypto request to the appropriate crypto devices. */
int crypto_invoke(FAR struct cryptop *crp)
{
FAR struct cryptodesc *crd;
uint64_t nid;
uint32_t hid;
int error;
/* Sanity checks. */
if (crp == NULL)
{
return -EINVAL;
}
nxmutex_lock(&g_crypto_lock);
if (crp->crp_desc == NULL || crypto_drivers == NULL)
{
crp->crp_etype = -EINVAL;
nxmutex_unlock(&g_crypto_lock);
return 0;
}
hid = (crp->crp_sid >> 32) & 0xffffffff;
if (hid >= crypto_drivers_num)
{
goto migrate;
}
if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP)
{
crypto_freesession(crp->crp_sid);
goto migrate;
}
if (crypto_drivers[hid].cc_process == NULL)
{
goto migrate;
}
crypto_drivers[hid].cc_operations++;
crypto_drivers[hid].cc_bytes += crp->crp_ilen;
error = crypto_drivers[hid].cc_process(crp);
if (error)
{
if (error == -ERESTART)
{
/* Unregister driver and migrate session. */
crypto_unregister(hid, CRYPTO_ALGORITHM_MAX + 1);
goto migrate;
}
else
{
crp->crp_etype = error;
}
}
nxmutex_unlock(&g_crypto_lock);
return 0;
migrate:
/* Migrate session. */
for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
{
crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
}
if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
{
crp->crp_sid = nid;
}
crp->crp_etype = -EAGAIN;
nxmutex_unlock(&g_crypto_lock);
return 0;
}
int crypto_getfeat(FAR int *featp)
{
extern int cryptodevallowsoft;
extern int userasymcrypto;
int hid;
int kalg;
int feat = 0;
if (userasymcrypto == 0)
{
goto out;
}
for (hid = 0; hid < crypto_drivers_num; hid++)
{
if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
cryptodevallowsoft == 0)
{
continue;
}
if (crypto_drivers[hid].cc_kprocess == NULL)
{
continue;
}
for (kalg = 0; kalg <= CRK_ALGORITHM_MAX; kalg++)
{
if ((crypto_drivers[hid].cc_kalg[kalg] &
CRYPTO_ALG_FLAG_SUPPORTED) != 0)
{
feat |= 1 << kalg;
}
}
}
out:
*featp = feat;
return 0;
}
int crypto_driver_set_priv(uint32_t driverid, FAR void *priv)
{
if (driverid >= crypto_drivers_num || crypto_drivers == NULL)
{
return -EINVAL;
}
nxmutex_lock(&g_crypto_lock);
crypto_drivers[driverid].priv = priv;
nxmutex_unlock(&g_crypto_lock);
return 0;
}
FAR void *crypto_driver_get_priv(uint32_t driverid)
{
if (driverid >= crypto_drivers_num || crypto_drivers == NULL)
{
return NULL;
}
return crypto_drivers[driverid].priv;
}
int up_cryptoinitialize(void)
{
#ifdef CONFIG_CRYPTO_ALGTEST
int ret = crypto_test();
if (ret)
{
crypterr("ERROR: crypto test failed\n");
}
else
{
cryptinfo("crypto test OK\n");
}
return ret;
#else
return OK;
#endif
}