/* 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 #include #include #if defined(BSDKM_CRYPTO_REGISTER) #include #include #include "cryptodev_if.h" #endif /* wolf includes */ #include #ifdef WOLFCRYPT_ONLY #include #else #include #endif #ifdef HAVE_FIPS #ifdef USE_CONTESTMUTEX #error USE_CONTESTMUTEX is incompatible with WOLFSSL_BSDKM #endif #include #endif /* HAVE_FIPS */ #if !defined(NO_CRYPT_TEST) #include #endif #if defined(WOLFSSL_KERNEL_BENCHMARKS) #include #endif #include 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 */