* 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>
#include <nuttx/percpu.h>
* Public Data
****************************************************************************/
DEFINE_PER_CPU_BSS_BMP(FAR struct cryptocap *, crypto_drivers);
#define crypto_drivers this_cpu_var_bmp(crypto_drivers)
DEFINE_PER_CPU_BSS_BMP(int, crypto_drivers_num);
#define crypto_drivers_num this_cpu_var_bmp(crypto_drivers_num)
* Private Data
****************************************************************************/
static mutex_t g_crypto_lock = NXMUTEX_INITIALIZER;
* Public Functions
****************************************************************************/
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);
* 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];
* referencing it, skip.
*/
if (cpc->cc_newsession == NULL ||
(cpc->cc_flags & CRYPTOCAP_F_CLEANUP))
{
continue;
}
if (cpc->cc_flags & CRYPTOCAP_F_SOFTWARE)
{
if (turn != 2)
{
continue;
}
}
else if (cpc->cc_flags & CRYPTOCAP_F_REMOTE)
{
if (turn != 1)
{
continue;
}
}
for (cr = cri; cr; cr = cr->cri_next)
{
if (cpc->cc_alg[cr->cri_alg] == 0)
{
break;
}
}
* keep searching.
*/
if (cr != NULL)
{
continue;
}
* to this one. Keep "remembering" whichever is
* the best of the two.
*/
if (hid2 != -1)
{
* 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
{
* comparisons.
*/
hid2 = hid;
}
}
* side-effect is that we will always prefer a hardware
* driver over the software one.
*/
if (hid2 != -1)
{
break;
}
turn++;
}
while (turn < 2 || (turn == 2 && !hard));
hid = hid2;
* 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;
}
lid = hid;
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;
}
* driver).
*/
int crypto_freesession(uint64_t sid)
{
int err = 0;
uint32_t hid;
if (crypto_drivers == NULL)
{
return -EINVAL;
}
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--;
}
if (crypto_drivers[hid].cc_freesession)
{
err = crypto_drivers[hid].cc_freesession(sid);
}
* 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;
}
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;
crypto_drivers[i].cc_flags = flags;
nxmutex_unlock(&g_crypto_lock);
return i;
}
}
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;
newdrv[i].cc_flags = flags;
crypto_drivers_num *= 2;
kmm_free(crypto_drivers);
crypto_drivers = newdrv;
nxmutex_unlock(&g_crypto_lock);
return i;
}
nxmutex_unlock(&g_crypto_lock);
return -1;
}
int crypto_find_driverid(uint8_t flags)
{
int i;
for (i = 0; i < crypto_drivers_num; i++)
{
if (crypto_drivers[i].cc_flags & flags)
{
return i;
}
}
return -EINVAL;
}
* 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++)
{
* 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;
}
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++)
{
* 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;
nxmutex_unlock(&g_crypto_lock);
return 0;
}
* 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);
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;
for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
{
if (crypto_drivers[driverid].cc_alg[i] != 0)
{
break;
}
}
}
* (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)
{
crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
crypto_drivers[driverid].cc_sessions = ses;
}
}
nxmutex_unlock(&g_crypto_lock);
return 0;
}
int crypto_kinvoke(FAR struct cryptkop *krp)
{
extern int cryptodevallowsoft;
uint32_t hid;
int error;
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;
}
int crypto_invoke(FAR struct cryptop *crp)
{
FAR struct cryptodesc *crd;
uint64_t nid;
uint32_t hid;
int error;
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)
{
crypto_unregister(hid, CRYPTO_ALGORITHM_MAX + 1);
goto migrate;
}
else
{
crp->crp_etype = error;
}
}
nxmutex_unlock(&g_crypto_lock);
return 0;
migrate:
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
}