Files
wolfssl/bsdkm/wolfkmod.c
T
Sameeh Jubran 7ca42d1e55 bsdkm: return 0 from wolfkdriv_process() to comply with opencrypto(9)
cryptodev_process must return 0 once crypto_done() has fired; errors are reported via crp_etype, otherwise the framework may re-dispatch an already-completed request.

Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
2026-04-30 10:56:54 +03:00

1076 lines
30 KiB
C

/* wolfkmod.c -- wolfssl FreeBSD kernel module.
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifdef WOLFSSL_BSDKM
/* freebsd system includes */
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#if defined(BSDKM_CRYPTO_REGISTER)
#include <opencrypto/cryptodev.h>
#include <sys/bus.h>
#include "cryptodev_if.h"
#endif
/* wolf includes */
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#ifdef WOLFCRYPT_ONLY
#include <wolfssl/version.h>
#else
#include <wolfssl/ssl.h>
#endif
#ifdef HAVE_FIPS
#ifdef USE_CONTESTMUTEX
#error USE_CONTESTMUTEX is incompatible with WOLFSSL_BSDKM
#endif
#include <wolfssl/wolfcrypt/fips_test.h>
#endif /* HAVE_FIPS */
#if !defined(NO_CRYPT_TEST)
#include <wolfcrypt/test/test.h>
#endif
#if defined(WOLFSSL_KERNEL_BENCHMARKS)
#include <wolfcrypt/benchmark/benchmark.h>
#endif
#include <wolfssl/wolfcrypt/random.h>
MALLOC_DEFINE(M_WOLFSSL, "libwolfssl", "wolfSSL kernel memory");
#if defined(BSDKM_CRYPTO_REGISTER)
#include "bsdkm/wolfkmod_aes.c"
#endif
/* common functions. */
static int wolfkmod_init(void);
static int wolfkmod_cleanup(void);
#if !defined(BSDKM_CRYPTO_REGISTER)
/* functions specific to a pure kernel module library build. */
static int wolfkmod_load(void);
static int wolfkmod_unload(void);
#else
/* functions specific to a kernel crypto driver module build. */
static void wolfkdriv_identify(driver_t * driver, device_t parent);
static int wolfkdriv_probe(device_t dev);
static int wolfkdriv_attach(device_t dev);
static int wolfkdriv_detach(device_t dev);
static int wolfkdriv_probesession(device_t dev,
const struct crypto_session_params *csp);
static int wolfkdriv_newsession(device_t dev, crypto_session_t cses,
const struct crypto_session_params *csp);
static void wolfkdriv_freesession(device_t dev, crypto_session_t cses);
static int wolfkdriv_process(device_t dev, struct cryptop *crp, int hint);
#endif /* !BSDKM_CRYPTO_REGISTER */
#if defined(WOLFSSL_AESNI) || defined(WOLFSSL_KERNEL_BENCHMARKS)
#include "bsdkm/x86_vecreg.c"
#endif /* WOLFSSL_AESNI || WOLFSSL_KERNEL_BENCHMARKS*/
#ifdef HAVE_FIPS
#define WOLFKMOD_FIPS_ERR_MSG(hash) ({ \
printf("In-core integrity hash check failure.\n"); \
if ((hash)) \
printf("Rebuild with \"WOLFCRYPT_FIPS_CORE_HASH_VALUE=%s\".\n", \
hash); \
else \
printf("error: could not compute new hash. " \
"Contact customer support.\n"); \
})
static void wolfkmod_fips_cb(int ok, int err, const char * hash)
{
if ((!ok) || (err != 0)) {
printf("error: libwolfssl FIPS error: %s\n",
wc_GetErrorString(err));
}
if (err == WC_NO_ERR_TRACE(IN_CORE_FIPS_E)) {
WOLFKMOD_FIPS_ERR_MSG(hash);
}
}
#endif /* HAVE_FIPS */
static int wolfkmod_init(void)
{
int error = 0;
#if defined(WOLFSSL_AESNI) || defined(WOLFSSL_KERNEL_BENCHMARKS)
error = wolfkmod_vecreg_init();
if (error != 0) {
printf("error: wolfkmod_vecreg_init: %d\n", error);
return (ECANCELED);
}
#endif /* WOLFSSL_AESNI || WOLFSSL_KERNEL_BENCHMARKS*/
#ifdef HAVE_FIPS
error = wolfCrypt_SetCb_fips(wolfkmod_fips_cb);
if (error != 0) {
printf("error: wolfCrypt_SetCb_fips failed: %s\n",
wc_GetErrorString(error));
return (ECANCELED);
}
fipsEntry();
error = wolfCrypt_GetStatus_fips();
if (error != 0) {
printf("error: wolfCrypt_GetStatus_fips failed: %d: %s\n",
error, wc_GetErrorString(error));
if (error == WC_NO_ERR_TRACE(IN_CORE_FIPS_E)) {
const char *newhash = wolfCrypt_GetCoreHash_fips();
WOLFKMOD_FIPS_ERR_MSG(newhash);
}
return (ECANCELED);
}
#endif /* HAVE_FIPS */
#ifdef WC_RNG_SEED_CB
error = wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT);
if (error < 0) {
printf("error: wc_SetSeed_Cb failed: %d\n", error);
return (ECANCELED);
}
#endif /* WC_RNG_SEED_CB */
#ifdef WOLFCRYPT_ONLY
error = wolfCrypt_Init();
if (error != 0) {
printf("error: wolfCrypt_Init failed: %s\n", wc_GetErrorString(error));
return (ECANCELED);
}
#else
error = wolfSSL_Init();
if (error != WOLFSSL_SUCCESS) {
printf("error: wolfSSL_Init failed: %s\n", wc_GetErrorString(error));
return (ECANCELED);
}
#endif /* WOLFCRYPT_ONLY */
#ifdef HAVE_FIPS
error = wc_RunAllCast_fips();
if (error != 0) {
printf("error: wc_RunAllCast_fips failed with "
"return value %d\n", error);
return (ECANCELED);
}
else {
printf("info: FIPS 140-3 wolfCrypt-fips v%d.%d.%d%s%s startup "
"self-test succeeded.\n",
#ifdef HAVE_FIPS_VERSION_MAJOR
HAVE_FIPS_VERSION_MAJOR,
#else
HAVE_FIPS_VERSION,
#endif
#ifdef HAVE_FIPS_VERSION_MINOR
HAVE_FIPS_VERSION_MINOR,
#else
0,
#endif
#ifdef HAVE_FIPS_VERSION_PATCH
HAVE_FIPS_VERSION_PATCH,
#else
0,
#endif
#ifdef HAVE_FIPS_VERSION_PORT
"-",
HAVE_FIPS_VERSION_PORT
#else
"",
""
#endif
);
}
#endif /* HAVE_FIPS */
return (0);
}
static int wolfkmod_cleanup(void)
{
int error = 0;
#ifdef WOLFCRYPT_ONLY
error = wolfCrypt_Cleanup();
if (error != 0) {
printf("error: wolfCrypt_Cleanup failed: %s\n",
wc_GetErrorString(error));
error = ECANCELED;
goto wolfkmod_cleanup_out;
}
#else
error = wolfSSL_Cleanup();
if (error != WOLFSSL_SUCCESS) {
printf("error: wolfSSL_Cleanup failed: %s\n",
wc_GetErrorString(error));
error = ECANCELED;
goto wolfkmod_cleanup_out;
}
#endif /* WOLFCRYPT_ONLY */
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
printf("info: libwolfssl " LIBWOLFSSL_VERSION_STRING
" cleanup complete.\n");
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
error = 0;
wolfkmod_cleanup_out:
#if defined(WOLFSSL_AESNI) || defined(WOLFSSL_KERNEL_BENCHMARKS)
wolfkmod_vecreg_exit();
#endif /* WOLFSSL_AESNI || WOLFSSL_KERNEL_BENCHMARKS*/
return (error);
}
#if !defined(BSDKM_CRYPTO_REGISTER)
static int wolfkmod_load(void)
{
int error = 0;
error = wolfkmod_init();
if (error != 0) {
return (ECANCELED);
}
#ifndef NO_CRYPT_TEST
error = wolfcrypt_test(NULL);
if (error != 0) {
printf("error: wolfcrypt test failed: %d\n", error);
(void)wolfkmod_cleanup();
return (ECANCELED);
}
printf("info: wolfCrypt self-test passed.\n");
#endif /* NO_CRYPT_TEST */
#ifdef WOLFSSL_KERNEL_BENCHMARKS
error = benchmark_test(NULL);
if (error != 0) {
printf("error: wolfcrypt benchmark failed: %d\n", error);
(void)wolfkmod_cleanup();
return (ECANCELED);
}
printf("info: wolfCrypt benchmark passed.\n");
#endif /* WOLFSSL_KERNEL_BENCHMARKS */
printf("info: libwolfssl loaded\n");
return (0);
}
static int wolfkmod_unload(void)
{
int error = 0;
#ifdef HAVE_FIPS
error = wc_RunAllCast_fips();
if (error != 0) {
printf("error: wc_RunAllCast_fips failed at shutdown with "
"return value %d\n", error);
}
else
printf("info: wolfCrypt FIPS re-self-test succeeded at unload: "
"all algorithms re-verified.\n");
#endif
error = wolfkmod_cleanup();
if (error == 0) {
printf("info: libwolfssl unloaded\n");
}
return (error);
}
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
static const char * wolfkmod_event_to_str(modeventtype_t what)
{
switch (what) {
case MOD_LOAD:
return "MOD_LOAD";
case MOD_UNLOAD:
return "MOD_UNLOAD";
case MOD_SHUTDOWN:
return "MOD_SHUTDOWN";
case MOD_QUIESCE:
return "MOD_QUIESCE";
}
}
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
/* see /usr/include/sys/module.h for more info. */
static int
wolfkmod_event(struct module * m, int what, void * arg)
{
int error = 0;
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
printf("info: wolfkmod_event: %s\n", wolfkmod_event_to_str(what));
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
switch (what) {
case MOD_LOAD:
error = wolfkmod_load();
break;
case MOD_UNLOAD:
error = wolfkmod_unload();
break;
case MOD_SHUTDOWN:
case MOD_QUIESCE:
default:
error = EOPNOTSUPP;
}
(void)m;
(void)arg;
return (error);
}
#endif /* !BSDKM_CRYPTO_REGISTER */
#if defined(BSDKM_CRYPTO_REGISTER)
/* wolfkdriv device driver software context. */
struct wolfkdriv_softc {
int32_t crid;
device_t dev;
};
struct km_aes_ctx {
Aes aes_encrypt;
Aes aes_decrypt;
};
typedef struct km_aes_ctx km_aes_ctx;
struct wolfkdriv_session {
km_aes_ctx aes_ctx;
int32_t crid;
int type;
int ivlen;
int klen;
};
typedef struct wolfkdriv_session wolfkdriv_session_t;
static void km_AesFree(Aes * aes) {
if (aes == NULL) {
return;
}
wc_AesFree(aes);
#if defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0)
ForceZero(aes, sizeof(*aes));
#endif
}
static void wolfkdriv_aes_ctx_clear(km_aes_ctx * ctx)
{
if (ctx != NULL) {
km_AesFree(&ctx->aes_encrypt);
km_AesFree(&ctx->aes_decrypt);
}
#ifdef WOLFKM_DEBUG_AES
printf("info: exiting km_AesExitCommon\n");
#endif /* WOLFKM_DEBUG_AES */
}
static void wolfkdriv_identify(driver_t * driver, device_t parent)
{
(void)driver;
/* don't double add wolfkdriv child. */
if (device_find_child(parent, "libwolf", -1) != NULL) {
return;
}
BUS_ADD_CHILD(parent, 10, "libwolf", -1);
}
static int wolfkdriv_probe(device_t dev)
{
device_set_desc(dev, "wolfSSL crypto");
return (BUS_PROBE_DEFAULT);
}
/*
* unregister libwolfssl crypto driver
*/
static void wolfkdriv_unregister(struct wolfkdriv_softc * softc)
{
if (softc && softc->crid >= 0) {
crypto_unregister_all(softc->crid);
device_printf(softc->dev, "info: crid unregistered: %d\n", softc->crid);
softc->crid = -1;
}
return;
}
static int wolfkdriv_attach(device_t dev)
{
struct wolfkdriv_softc * softc = NULL;
int flags = CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC |
CRYPTOCAP_F_ACCEL_SOFTWARE | CRYPTOCAP_F_HARDWARE;
int ret = 0;
int crid = 0;
int error = 0;
ret = wolfkmod_init();
if (ret != 0) {
return (ECANCELED);
}
/**
* register wolfcrypt algs here with crypto_get_driverid.
*
* The crid is the literal index into the kernel crypto_drivers array:
* - crid >= 0 is valid.
* - crid < 0 is error.
* */
softc = device_get_softc(dev);
softc->dev = dev;
softc->crid = crypto_get_driverid(dev, sizeof(wolfkdriv_session_t), flags);
if (softc->crid < 0) {
device_printf(dev, "error: crypto_get_driverid failed: %d\n",
softc->crid);
return (ENXIO);
}
/*
* various sanity checks
*/
/* 1. we should find ourself by name */
crid = crypto_find_driver("libwolf");
if (crid != softc->crid) {
device_printf(dev, "error: attach: got crid %d, expected %d\n", crid,
softc->crid);
error = ENXIO;
goto attach_out;
}
/* 2. test various algs */
error = wolfkdriv_test_aes(dev, crid);
if (error) {
device_printf(dev, "error: attach: test_aes: %d\n", error);
error = ENXIO;
goto attach_out;
}
device_printf(dev, "info: driver loaded: %d\n", crid);
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: exiting attach\n");
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
attach_out:
if (error) {
wolfkdriv_unregister(softc);
error = ENXIO;
}
return (error);
}
static int wolfkdriv_detach(device_t dev)
{
struct wolfkdriv_softc * softc = NULL;
int ret = 0;
ret = wolfkmod_cleanup();
if (ret == 0) {
/* unregister wolfcrypt algs */
softc = device_get_softc(dev);
wolfkdriv_unregister(softc);
}
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: exiting detach\n");
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
return (0);
}
static int wolfkdriv_probesession(device_t dev,
const struct crypto_session_params *csp)
{
struct wolfkdriv_softc * softc = NULL;
int error = CRYPTODEV_PROBE_ACCEL_SOFTWARE;
softc = device_get_softc(dev);
switch (csp->csp_mode) {
case CSP_MODE_CIPHER:
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_CBC:
break;
default:
error = EINVAL;
break;
}
break;
case CSP_MODE_AEAD:
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_NIST_GCM_16:
break;
default:
error = EINVAL;
break;
}
break;
case CSP_MODE_DIGEST:
case CSP_MODE_ETA:
default:
error = EINVAL;
break;
}
(void)softc;
(void)csp;
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: probesession: mode=%d, cipher_alg=%d, error=%d\n",
csp->csp_mode, csp->csp_cipher_alg, error);
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
return (error);
}
static int wolfkdriv_newsession_aes(device_t dev,
wolfkdriv_session_t * session,
const struct crypto_session_params *csp)
{
int error = 0;
int klen = csp->csp_cipher_klen; /* key len in bytes */
switch (csp->csp_cipher_alg) {
case CRYPTO_AES_NIST_GCM_16:
session->type = CRYPTO_AES_NIST_GCM_16;
break;
case CRYPTO_AES_CBC:
session->type = CRYPTO_AES_CBC;
break;
default:
return (EOPNOTSUPP);
}
if (klen != 16 && klen != 24 && klen != 32) {
device_printf(dev, "info: newsession_cipher: invalid klen: %d\n", klen);
return (EINVAL);
}
session->klen = klen;
session->ivlen = csp->csp_ivlen;
/* encrypt */
error = wc_AesInit(&session->aes_ctx.aes_encrypt, NULL, INVALID_DEVID);
if (error) {
device_printf(dev, "error: newsession_cipher: aes init: %d\n", error);
goto newsession_cipher_out;
}
if (session->type == CRYPTO_AES_CBC) {
/* Need a separate decrypt structure for aes-cbc. */
error = wc_AesInit(&session->aes_ctx.aes_decrypt, NULL, INVALID_DEVID);
if (error) {
device_printf(dev, "error: newsession_cipher: aes init: %d\n",
error);
goto newsession_cipher_out;
}
}
newsession_cipher_out:
if (error != 0) {
wolfkdriv_aes_ctx_clear(&session->aes_ctx);
return (EINVAL);
}
return (error);
}
static int wolfkdriv_newsession(device_t dev, crypto_session_t cses,
const struct crypto_session_params *csp)
{
wolfkdriv_session_t * session = NULL;
int error = 0;
/* get the wolfkdriv_session_t context */
session = crypto_get_driver_session(cses);
switch (csp->csp_mode) {
case CSP_MODE_DIGEST:
case CSP_MODE_ETA:
device_printf(dev, "info: not supported: %d\n", csp->csp_mode);
error = EOPNOTSUPP;
break;
case CSP_MODE_CIPHER:
case CSP_MODE_AEAD:
error = wolfkdriv_newsession_aes(dev, session, csp);
break;
default:
__assert_unreachable();
}
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: newsession: mode=%d, cipher_alg=%d, error=%d\n",
csp->csp_mode, csp->csp_cipher_alg, error);
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
return (error);
}
static void
wolfkdriv_freesession(device_t dev, crypto_session_t cses)
{
wolfkdriv_session_t * session = NULL;
(void)dev;
/* get the wolfkdriv_session_t context */
session = crypto_get_driver_session(cses);
/* clean it up */
wolfkdriv_aes_ctx_clear(&session->aes_ctx);
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: exiting freesession\n");
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
return;
}
static int wolfkdriv_cbc_work(device_t dev, wolfkdriv_session_t * session,
struct cryptop * crp,
const struct crypto_session_params * csp)
{
struct crypto_buffer_cursor cc_in;
struct crypto_buffer_cursor cc_out;
const unsigned char * in_block = NULL;
const unsigned char * in_seg = NULL;
unsigned char * out_block = NULL;
unsigned char * out_seg = NULL;
Aes aes;
uint8_t iv[WC_AES_BLOCK_SIZE];
uint8_t block[EALG_MAX_BLOCK_LEN];
size_t data_len = 0;
size_t seg_len = 0;
size_t in_len = 0;
size_t out_len = 0;
int error = 0;
int is_encrypt = 0;
int type = AES_ENCRYPTION;
if (csp->csp_cipher_alg != CRYPTO_AES_CBC) {
error = EINVAL;
goto cbc_work_out;
}
data_len = crp->crp_payload_length;
if (CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
is_encrypt = 1;
type = AES_ENCRYPTION;
memcpy(&aes, &session->aes_ctx.aes_encrypt, sizeof(aes));
}
else {
is_encrypt = 0;
type = AES_DECRYPTION;
memcpy(&aes, &session->aes_ctx.aes_decrypt, sizeof(aes));
}
/* must be multiple of block size */
if (data_len % WC_AES_BLOCK_SIZE) {
error = EINVAL;
goto cbc_work_out;
}
crypto_read_iv(crp, iv);
error = wc_AesSetKey(&aes, csp->csp_cipher_key,
csp->csp_cipher_klen, iv, type);
if (error) {
device_printf(dev, "error: wc_AesSetKey: %d\n", error);
goto cbc_work_out;
}
/* set up the crypto buffers */
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_payload_start);
in_seg = crypto_cursor_segment(&cc_in, &in_len);
/* handle if the user supplied a separate out buffer. */
if (CRYPTO_HAS_OUTPUT_BUFFER(crp)) {
crypto_cursor_init(&cc_out, &crp->crp_obuf);
crypto_cursor_advance(&cc_out, crp->crp_payload_output_start);
}
else {
cc_out = cc_in;
}
out_seg = crypto_cursor_segment(&cc_out, &out_len);
while (data_len) {
/* set up input buffers */
if (in_len < WC_AES_BLOCK_SIZE) {
/* less than a block in segment */
crypto_cursor_copydata(&cc_in, WC_AES_BLOCK_SIZE, block);
in_block = block;
in_len = WC_AES_BLOCK_SIZE;
}
else {
in_block = in_seg;
}
/* set up output buffers */
if (out_len < WC_AES_BLOCK_SIZE) {
out_block = block;
out_len = WC_AES_BLOCK_SIZE;
}
else {
out_block = out_seg;
}
/* choose which of data_len, in_len, out_len, is shorter.
* round down to multiple of aes block size. */
seg_len = rounddown(MIN(data_len, MIN(in_len, out_len)),
WC_AES_BLOCK_SIZE);
if (is_encrypt) {
error = wc_AesCbcEncrypt(&aes, out_block, in_block, seg_len);
if (error) {
device_printf(dev, "error: wc_AesCbcEncrypt: %d\n", error);
goto cbc_work_out;
}
}
else {
error = wc_AesCbcDecrypt(&aes, out_block, in_block, seg_len);
if (error) {
device_printf(dev, "error: wc_AesCbcEncrypt: %d\n", error);
goto cbc_work_out;
}
}
if (out_block == block) {
/* we used the block as local output buffer. copy to cc_out,
* and grab the next out cursor segment. */
crypto_cursor_copyback(&cc_out, WC_AES_BLOCK_SIZE, block);
out_seg = crypto_cursor_segment(&cc_out, &out_len);
} else {
/* we worked directly in cc_out. advance the cursor. */
crypto_cursor_advance(&cc_out, seg_len);
out_seg += seg_len;
out_len -= seg_len;
}
if (in_block == block) {
/* grab a new in cursor segment. */
in_seg = crypto_cursor_segment(&cc_in, &in_len);
} else {
/* else advance existing in cursor. */
crypto_cursor_advance(&cc_in, seg_len);
in_seg += seg_len;
in_len -= seg_len;
}
data_len -= seg_len;
}
cbc_work_out:
/* cleanup. */
wc_ForceZero(iv, sizeof(iv));
wc_ForceZero(block, sizeof(block));
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: cbc_work: mode=%d, cipher_alg=%d, "
"payload_length=%d, error=%d\n",
csp->csp_mode, csp->csp_cipher_alg, crp->crp_payload_length,
error);
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
return (error);
}
static int wolfkdriv_gcm_work(device_t dev, wolfkdriv_session_t * session,
struct cryptop * crp,
const struct crypto_session_params * csp)
{
struct crypto_buffer_cursor cc_in;
struct crypto_buffer_cursor cc_out;
const unsigned char * in_seg = NULL;
unsigned char * out_seg = NULL;
Aes aes;
uint8_t iv[WC_AES_BLOCK_SIZE];
uint8_t auth_tag[WC_AES_BLOCK_SIZE];
size_t data_len = 0;
size_t seg_len = 0;
size_t in_len = 0;
size_t out_len = 0;
int error = 0;
int is_encrypt = 0;
memcpy(&aes, &session->aes_ctx.aes_encrypt, sizeof(aes));
if (csp->csp_cipher_alg != CRYPTO_AES_NIST_GCM_16) {
error = EINVAL;
goto gcm_work_out;
}
data_len = crp->crp_payload_length;
if (CRYPTO_OP_IS_ENCRYPT(crp->crp_op)) {
is_encrypt = 1;
}
else {
is_encrypt = 0;
}
error = wc_AesGcmSetKey(&aes, csp->csp_cipher_key,
csp->csp_cipher_klen);
if (error) {
device_printf(dev, "error: wc_AesGcmSetKey: %d\n", error);
goto gcm_work_out;
}
crypto_read_iv(crp, iv);
error = wc_AesGcmInit(&aes, NULL /* key */, 0 /* keylen */,
iv, csp->csp_ivlen);
if (error) {
device_printf(dev, "error: wc_AesGcmInit: %d\n", error);
goto gcm_work_out;
}
/* process aad first */
if (crp->crp_aad != NULL) {
/* they passed aad in separate buffer. */
if (is_encrypt) {
error = wc_AesGcmEncryptUpdate(&aes, NULL, NULL, 0,
crp->crp_aad, crp->crp_aad_length);
}
else {
error = wc_AesGcmDecryptUpdate(&aes, NULL, NULL, 0,
crp->crp_aad, crp->crp_aad_length);
}
if (error) {
error = EINVAL;
goto gcm_work_out;
}
}
else {
/* we need to pull aad out of crp->crp_buf from crp_aad_start. */
size_t aad_len = 0;
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_aad_start);
for (aad_len = crp->crp_aad_length; aad_len > 0; aad_len -= seg_len) {
in_seg = crypto_cursor_segment(&cc_in, &in_len);
seg_len = MIN(aad_len, in_len);
if (is_encrypt) {
error = wc_AesGcmEncryptUpdate(&aes, NULL, NULL, 0,
in_seg, seg_len);
}
else {
error = wc_AesGcmDecryptUpdate(&aes, NULL, NULL, 0,
in_seg, seg_len);
}
if (error) {
error = EINVAL;
goto gcm_work_out;
}
crypto_cursor_advance(&cc_in, seg_len);
}
}
/*
* process cipher/plaintext next
*/
/* set up the crypto buffers */
crypto_cursor_init(&cc_in, &crp->crp_buf);
crypto_cursor_advance(&cc_in, crp->crp_payload_start);
in_seg = crypto_cursor_segment(&cc_in, &in_len);
/* handle if the user supplied a separate out buffer. */
if (CRYPTO_HAS_OUTPUT_BUFFER(crp)) {
crypto_cursor_init(&cc_out, &crp->crp_obuf);
crypto_cursor_advance(&cc_out, crp->crp_payload_output_start);
}
else {
cc_out = cc_in;
}
out_seg = crypto_cursor_segment(&cc_out, &out_len);
while (data_len) {
/* process through the available segments. */
in_seg = crypto_cursor_segment(&cc_in, &in_len);
out_seg = crypto_cursor_segment(&cc_out, &out_len);
seg_len = MIN(data_len, MIN(in_len, out_len));
if (is_encrypt) {
error = wc_AesGcmEncryptUpdate(&aes, out_seg, in_seg, seg_len,
NULL, 0);
if (error) {
device_printf(dev, "error: wc_AesGcmEncrypt: %d\n", error);
goto gcm_work_out;
}
}
else {
error = wc_AesGcmDecryptUpdate(&aes, out_seg, in_seg, seg_len,
NULL, 0);
if (error) {
device_printf(dev, "error: wc_AesGcmDecrypt: %d\n", error);
goto gcm_work_out;
}
}
/* advance the cursors by amount processed */
crypto_cursor_advance(&cc_in, seg_len);
crypto_cursor_advance(&cc_out, seg_len);
data_len -= seg_len;
}
/* process auth tag finally */
if (is_encrypt) {
error = wc_AesGcmEncryptFinal(&aes, auth_tag, WC_AES_BLOCK_SIZE);
if (error == 0) {
crypto_copyback(crp, crp->crp_digest_start, WC_AES_BLOCK_SIZE,
auth_tag);
}
}
else {
crypto_copydata(crp, crp->crp_digest_start, WC_AES_BLOCK_SIZE,
auth_tag);
error = wc_AesGcmDecryptFinal(&aes, auth_tag, WC_AES_BLOCK_SIZE);
if (error) {
error = EBADMSG;
}
}
gcm_work_out:
/* cleanup. */
wc_ForceZero(iv, sizeof(iv));
wc_ForceZero(auth_tag, sizeof(auth_tag));
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: gcm_work: mode=%d, cipher_alg=%d, "
"payload_length=%d, error=%d\n",
csp->csp_mode, csp->csp_cipher_alg, crp->crp_payload_length,
error);
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
return (error);
}
static int wolfkdriv_process(device_t dev, struct cryptop * crp, int hint)
{
const struct crypto_session_params * csp = NULL;
wolfkdriv_session_t * session = NULL;
int error = 0;
(void)hint;
session = crypto_get_driver_session(crp->crp_session);
csp = crypto_get_params(crp->crp_session);
switch (csp->csp_mode) {
case CSP_MODE_CIPHER:
error = wolfkdriv_cbc_work(dev, session, crp, csp);
break;
case CSP_MODE_DIGEST:
case CSP_MODE_ETA:
error = EINVAL;
break;
case CSP_MODE_AEAD:
error = wolfkdriv_gcm_work(dev, session, crp, csp);
break;
default:
__assert_unreachable();
}
crp->crp_etype = error;
crypto_done(crp);
#if defined(WOLFSSL_BSDKM_VERBOSE_DEBUG)
device_printf(dev, "info: process: mode=%d, cipher_alg=%d, error=%d\n",
csp->csp_mode, csp->csp_cipher_alg, error);
#endif /* WOLFSSL_BSDKM_VERBOSE_DEBUG */
/* opencrypto(9) contract: return 0 after crypto_done(); error is in crp_etype. */
return (0);
}
/*
* wolfkmod as a crypto device driver.
*/
static device_method_t wolfkdriv_methods[] = {
/* device interface methods: called during device setup, etc. */
DEVMETHOD(device_identify, wolfkdriv_identify),
DEVMETHOD(device_probe, wolfkdriv_probe),
DEVMETHOD(device_attach, wolfkdriv_attach),
DEVMETHOD(device_detach, wolfkdriv_detach),
/* crypto device session methods: called during crypto session setup,
* work, etc. */
DEVMETHOD(cryptodev_probesession, wolfkdriv_probesession),
DEVMETHOD(cryptodev_newsession, wolfkdriv_newsession),
DEVMETHOD(cryptodev_freesession, wolfkdriv_freesession),
DEVMETHOD(cryptodev_process, wolfkdriv_process),
DEVMETHOD_END
};
static driver_t wolfkdriv_driver = {
.name = "libwolf",
.methods = wolfkdriv_methods,
.size = sizeof(struct wolfkdriv_softc),
};
/* on x86, software-only drivers usually attach to nexus bus. */
DRIVER_MODULE(libwolfssl, nexus, wolfkdriv_driver, NULL, NULL);
#endif /* BSDKM_CRYPTO_REGISTER */
#if !defined(BSDKM_CRYPTO_REGISTER)
/*
* wolfkmod as a pure kernel module.
*/
static moduledata_t libwolfmod = {
#ifdef HAVE_FIPS
"libwolfssl_fips", /* module name */
#else
"libwolfssl", /* module name */
#endif /* HAVE_FIPS */
wolfkmod_event, /* module event handler */
NULL /* extra data, unused */
};
DECLARE_MODULE(libwolfssl, libwolfmod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
#endif /* !BSDKM_CRYPTO_REGISTER */
MODULE_VERSION(libwolfssl, 1);
#endif /* WOLFSSL_BSDKM */