mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
2486 lines
73 KiB
C
2486 lines
73 KiB
C
/* swdev.c
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/* wc_swdev callback. */
|
|
|
|
#include "swdev.h"
|
|
|
|
#include <wolfssl/wolfcrypt/settings.h>
|
|
#include <wolfssl/wolfcrypt/types.h>
|
|
#include <wolfssl/wolfcrypt/error-crypt.h>
|
|
#include <wolfssl/wolfcrypt/wc_port.h>
|
|
|
|
#ifndef NO_RSA
|
|
#include <wolfssl/wolfcrypt/rsa.h>
|
|
#endif
|
|
#ifdef HAVE_ECC
|
|
#include <wolfssl/wolfcrypt/ecc.h>
|
|
#endif
|
|
#ifndef NO_SHA256
|
|
#include <wolfssl/wolfcrypt/sha256.h>
|
|
#endif
|
|
#ifndef NO_AES
|
|
#include <wolfssl/wolfcrypt/aes.h>
|
|
#endif
|
|
#ifdef HAVE_ED25519
|
|
#include <wolfssl/wolfcrypt/ed25519.h>
|
|
#endif
|
|
#ifdef HAVE_CURVE25519
|
|
#include <wolfssl/wolfcrypt/curve25519.h>
|
|
#endif
|
|
#ifdef HAVE_CURVE448
|
|
#include <wolfssl/wolfcrypt/curve448.h>
|
|
#endif
|
|
#ifdef HAVE_ED448
|
|
#include <wolfssl/wolfcrypt/ed448.h>
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_SLHDSA
|
|
#include <wolfssl/wolfcrypt/wc_slhdsa.h>
|
|
#endif
|
|
#ifdef HAVE_FALCON
|
|
#include <wolfssl/wolfcrypt/falcon.h>
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_MLKEM
|
|
#include <wolfssl/wolfcrypt/wc_mlkem.h>
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_FRODOKEM
|
|
#include <wolfssl/wolfcrypt/wc_frodokem.h>
|
|
#endif
|
|
|
|
static int swdev_initialized = 0;
|
|
|
|
static int swdev_ensure_init(void)
|
|
{
|
|
if (!swdev_initialized) {
|
|
int ret = wolfCrypt_Init();
|
|
if (ret != 0)
|
|
return ret;
|
|
swdev_initialized = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
|
|
static void swdev_stateful_cleanup(void);
|
|
#endif
|
|
|
|
WC_SWDEV_EXPORT void wc_SwDev_InternalCleanup(void)
|
|
{
|
|
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
|
|
swdev_stateful_cleanup();
|
|
#endif
|
|
if (swdev_initialized) {
|
|
wolfCrypt_Cleanup();
|
|
swdev_initialized = 0;
|
|
}
|
|
}
|
|
|
|
#ifndef NO_RSA
|
|
static int swdev_rsa(wc_CryptoInfo* info)
|
|
{
|
|
switch (info->pk.rsa.type) {
|
|
case RSA_PUBLIC_ENCRYPT:
|
|
case RSA_PUBLIC_DECRYPT:
|
|
case RSA_PRIVATE_ENCRYPT:
|
|
case RSA_PRIVATE_DECRYPT:
|
|
return wc_RsaFunction(info->pk.rsa.in, info->pk.rsa.inLen,
|
|
info->pk.rsa.out, info->pk.rsa.outLen, info->pk.rsa.type,
|
|
info->pk.rsa.key, info->pk.rsa.rng);
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
|
|
#ifdef WOLFSSL_KEY_GEN
|
|
static int swdev_rsa_keygen(wc_CryptoInfo* info)
|
|
{
|
|
return wc_MakeRsaKey(info->pk.rsakg.key, info->pk.rsakg.size,
|
|
info->pk.rsakg.e, info->pk.rsakg.rng);
|
|
}
|
|
#endif
|
|
#endif /* !NO_RSA */
|
|
|
|
#ifdef HAVE_ECC
|
|
static int swdev_ecc_keygen(wc_CryptoInfo* info)
|
|
{
|
|
#ifdef HAVE_ECC_DHE
|
|
return wc_ecc_make_key_ex(info->pk.eckg.rng, info->pk.eckg.size,
|
|
info->pk.eckg.key, info->pk.eckg.curveId);
|
|
#else
|
|
(void)info;
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
}
|
|
|
|
static int swdev_ecdh(wc_CryptoInfo* info)
|
|
{
|
|
#ifdef HAVE_ECC_DHE
|
|
return wc_ecc_shared_secret(info->pk.ecdh.private_key,
|
|
info->pk.ecdh.public_key, info->pk.ecdh.out,
|
|
info->pk.ecdh.outlen);
|
|
#else
|
|
(void)info;
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
}
|
|
|
|
static int swdev_ecc_sign(wc_CryptoInfo* info)
|
|
{
|
|
#ifdef HAVE_ECC_SIGN
|
|
return wc_ecc_sign_hash(info->pk.eccsign.in, info->pk.eccsign.inlen,
|
|
info->pk.eccsign.out, info->pk.eccsign.outlen,
|
|
info->pk.eccsign.rng, info->pk.eccsign.key);
|
|
#else
|
|
(void)info;
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
}
|
|
|
|
static int swdev_ecc_verify(wc_CryptoInfo* info)
|
|
{
|
|
#ifdef HAVE_ECC_VERIFY
|
|
return wc_ecc_verify_hash(info->pk.eccverify.sig,
|
|
info->pk.eccverify.siglen, info->pk.eccverify.hash,
|
|
info->pk.eccverify.hashlen, info->pk.eccverify.res,
|
|
info->pk.eccverify.key);
|
|
#else
|
|
(void)info;
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
}
|
|
|
|
static int swdev_ecc_get_size(wc_CryptoInfo* info)
|
|
{
|
|
int sz = wc_ecc_size((ecc_key*)info->pk.ecc_get_size.key);
|
|
if (sz <= 0)
|
|
return sz; /* propagate negative error */
|
|
*info->pk.ecc_get_size.keySize = sz;
|
|
return 0;
|
|
}
|
|
|
|
static int swdev_ecc_get_sig_size(wc_CryptoInfo* info)
|
|
{
|
|
int sz = wc_ecc_sig_size(info->pk.ecc_get_sig_size.key);
|
|
if (sz <= 0)
|
|
return sz;
|
|
*info->pk.ecc_get_sig_size.sigSize = sz;
|
|
return 0;
|
|
}
|
|
|
|
static int swdev_ecc_make_pub(wc_CryptoInfo* info)
|
|
{
|
|
int ret;
|
|
ecc_key* key = info->pk.ecc_make_pub.key;
|
|
ecc_point* pub = wc_ecc_new_point_h(key->heap);
|
|
|
|
if (pub == NULL)
|
|
return MEMORY_E;
|
|
|
|
/* derive Q = d*G in software, then emit X9.63 uncompressed bytes. The
|
|
* point is serialized with the curve size from key->dp so custom-curve
|
|
* keys (idx == ECC_CUSTOM_IDX) work too; wc_ecc_export_point_der rejects
|
|
* negative curve indices. */
|
|
ret = wc_ecc_make_pub(key, pub);
|
|
if (ret == 0) {
|
|
byte* out = info->pk.ecc_make_pub.pubOut;
|
|
word32 curveSz = (word32)key->dp->size;
|
|
word32 ptSz = 1 + 2 * curveSz;
|
|
word32 xSz = curveSz;
|
|
word32 ySz = curveSz;
|
|
|
|
if (*info->pk.ecc_make_pub.pubOutSz < ptSz) {
|
|
ret = BUFFER_E;
|
|
}
|
|
else {
|
|
out[0] = ECC_POINT_UNCOMP;
|
|
ret = wc_export_int(pub->x, out + 1, &xSz, curveSz,
|
|
WC_TYPE_UNSIGNED_BIN);
|
|
if (ret == MP_OKAY)
|
|
ret = wc_export_int(pub->y, out + 1 + curveSz, &ySz, curveSz,
|
|
WC_TYPE_UNSIGNED_BIN);
|
|
if (ret == MP_OKAY)
|
|
*info->pk.ecc_make_pub.pubOutSz = ptSz;
|
|
}
|
|
}
|
|
|
|
wc_ecc_del_point_h(pub, key->heap);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef HAVE_ECC_CHECK_KEY
|
|
static int swdev_ecc_check_pub(wc_CryptoInfo* info)
|
|
{
|
|
ecc_key* key = info->pk.ecc_check_pub.key;
|
|
int ret = 0;
|
|
int validatedFromWire = 0;
|
|
|
|
if (info->pk.ecc_check_pub.pubKeySz == 0) {
|
|
return ECC_INF_E;
|
|
}
|
|
#ifdef HAVE_ECC_KEY_IMPORT
|
|
if (key->idx >= 0) {
|
|
WC_DECLARE_VAR(pubOnly, ecc_key, 1, key->heap);
|
|
WC_ALLOC_VAR(pubOnly, ecc_key, 1, key->heap);
|
|
if (!WC_VAR_OK(pubOnly))
|
|
ret = MEMORY_E;
|
|
else
|
|
ret = wc_ecc_init_ex(pubOnly, key->heap, INVALID_DEVID);
|
|
if (ret == 0) {
|
|
ret = wc_ecc_import_x963_ex(info->pk.ecc_check_pub.pubKey,
|
|
info->pk.ecc_check_pub.pubKeySz, pubOnly, key->dp->id);
|
|
if (ret == 0 &&
|
|
wc_ecc_cmp_point(&pubOnly->pubkey, &key->pubkey) != MP_EQ) {
|
|
/* wire bytes disagree with key->pubkey */
|
|
ret = BAD_STATE_E;
|
|
}
|
|
if (ret == 0)
|
|
ret = wc_ecc_check_key(pubOnly);
|
|
wc_ecc_free(pubOnly);
|
|
}
|
|
WC_FREE_VAR(pubOnly, key->heap);
|
|
validatedFromWire = 1;
|
|
}
|
|
#endif
|
|
if (ret == 0 && (!validatedFromWire ||
|
|
(info->pk.ecc_check_pub.checkPriv &&
|
|
key->type == ECC_PRIVATEKEY))) {
|
|
ret = wc_ecc_check_key(key);
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_ECC_CHECK_KEY */
|
|
#endif /* HAVE_ECC */
|
|
|
|
#ifdef HAVE_ED25519
|
|
#ifdef HAVE_ED25519_MAKE_KEY
|
|
static int swdev_ed25519_keygen(wc_CryptoInfo* info)
|
|
{
|
|
return wc_ed25519_make_key(info->pk.ed25519kg.rng,
|
|
info->pk.ed25519kg.size, info->pk.ed25519kg.key);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_ED25519_SIGN
|
|
static int swdev_ed25519_sign(wc_CryptoInfo* info)
|
|
{
|
|
return wc_ed25519_sign_msg_ex(info->pk.ed25519sign.in,
|
|
info->pk.ed25519sign.inLen, info->pk.ed25519sign.out,
|
|
info->pk.ed25519sign.outLen, info->pk.ed25519sign.key,
|
|
info->pk.ed25519sign.type, info->pk.ed25519sign.context,
|
|
info->pk.ed25519sign.contextLen);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_ED25519_VERIFY
|
|
static int swdev_ed25519_verify(wc_CryptoInfo* info)
|
|
{
|
|
return wc_ed25519_verify_msg_ex(info->pk.ed25519verify.sig,
|
|
info->pk.ed25519verify.sigLen, info->pk.ed25519verify.msg,
|
|
info->pk.ed25519verify.msgLen, info->pk.ed25519verify.res,
|
|
info->pk.ed25519verify.key, info->pk.ed25519verify.type,
|
|
info->pk.ed25519verify.context, info->pk.ed25519verify.contextLen);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_ED25519_MAKE_KEY
|
|
static int swdev_ed25519_make_pub(wc_CryptoInfo* info)
|
|
{
|
|
if (info->pk.ed25519makepub.pubOutSz != ED25519_PUB_KEY_SIZE)
|
|
return BUFFER_E;
|
|
return wc_ed25519_make_public(info->pk.ed25519makepub.key,
|
|
info->pk.ed25519makepub.pubOut, info->pk.ed25519makepub.pubOutSz);
|
|
}
|
|
#endif
|
|
|
|
static int swdev_ed25519_check_key(wc_CryptoInfo* info)
|
|
{
|
|
ed25519_key* key = info->pk.ed25519checkkey.key;
|
|
int ret = 0;
|
|
int validatedFromWire = 0;
|
|
|
|
#ifdef HAVE_ED25519_KEY_IMPORT
|
|
{
|
|
WC_DECLARE_VAR(pubOnly, ed25519_key, 1, key->heap);
|
|
|
|
/* vault-style consumption: rebuild a public-only key from the wire
|
|
* bytes and validate it, proving the serialized form is sufficient.
|
|
* trusted=0 runs the full public-key validation during import. */
|
|
WC_ALLOC_VAR(pubOnly, ed25519_key, 1, key->heap);
|
|
if (!WC_VAR_OK(pubOnly))
|
|
return MEMORY_E;
|
|
ret = wc_ed25519_init_ex(pubOnly, key->heap, INVALID_DEVID);
|
|
if (ret == 0) {
|
|
ret = wc_ed25519_import_public_ex(info->pk.ed25519checkkey.pubKey,
|
|
info->pk.ed25519checkkey.pubKeySz, pubOnly, 0);
|
|
}
|
|
wc_ed25519_free(pubOnly);
|
|
WC_FREE_VAR(pubOnly, key->heap);
|
|
validatedFromWire = 1;
|
|
}
|
|
#endif
|
|
/* private part (and public-only validation when the import path is
|
|
* unavailable): validate via the key handle */
|
|
if (ret == 0 && (!validatedFromWire ||
|
|
info->pk.ed25519checkkey.checkPriv)) {
|
|
ret = wc_ed25519_check_key(key);
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_ED25519 */
|
|
|
|
#ifdef HAVE_ED448
|
|
#ifdef HAVE_ED448_SIGN
|
|
static int swdev_ed448_sign(wc_CryptoInfo* info)
|
|
{
|
|
return wc_ed448_sign_msg_ex(info->pk.ed448sign.in,
|
|
info->pk.ed448sign.inLen, info->pk.ed448sign.out,
|
|
info->pk.ed448sign.outLen, info->pk.ed448sign.key,
|
|
info->pk.ed448sign.type, info->pk.ed448sign.context,
|
|
info->pk.ed448sign.contextLen);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_ED448_VERIFY
|
|
static int swdev_ed448_verify(wc_CryptoInfo* info)
|
|
{
|
|
return wc_ed448_verify_msg_ex(info->pk.ed448verify.sig,
|
|
info->pk.ed448verify.sigLen, info->pk.ed448verify.msg,
|
|
info->pk.ed448verify.msgLen, info->pk.ed448verify.res,
|
|
info->pk.ed448verify.key, info->pk.ed448verify.type,
|
|
info->pk.ed448verify.context, info->pk.ed448verify.contextLen);
|
|
}
|
|
#endif
|
|
|
|
static int swdev_ed448_make_pub(wc_CryptoInfo* info)
|
|
{
|
|
if (info->pk.ed448makepub.pubOutSz != ED448_PUB_KEY_SIZE)
|
|
return BUFFER_E;
|
|
return wc_ed448_make_public(info->pk.ed448makepub.key,
|
|
info->pk.ed448makepub.pubOut, info->pk.ed448makepub.pubOutSz);
|
|
}
|
|
|
|
#ifdef HAVE_ED448_KEY_IMPORT
|
|
/* Validate the public key from its wire bytes alone. */
|
|
static int swdev_ed448_check_wire(wc_CryptoInfo* info)
|
|
{
|
|
int ret;
|
|
void* heap = info->pk.ed448checkkey.key->heap;
|
|
WC_DECLARE_VAR(pubOnly, ed448_key, 1, heap);
|
|
|
|
WC_ALLOC_VAR(pubOnly, ed448_key, 1, heap);
|
|
if (!WC_VAR_OK(pubOnly))
|
|
return MEMORY_E;
|
|
ret = wc_ed448_init_ex(pubOnly, heap, INVALID_DEVID);
|
|
if (ret == 0) {
|
|
ret = wc_ed448_import_public_ex(info->pk.ed448checkkey.pubKey,
|
|
info->pk.ed448checkkey.pubKeySz, pubOnly, 0);
|
|
}
|
|
wc_ed448_free(pubOnly);
|
|
WC_FREE_VAR(pubOnly, heap);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int swdev_ed448_check_key(wc_CryptoInfo* info)
|
|
{
|
|
int ret = 0;
|
|
int validatedFromWire = 0;
|
|
|
|
#ifdef HAVE_ED448_KEY_IMPORT
|
|
ret = swdev_ed448_check_wire(info);
|
|
validatedFromWire = 1;
|
|
#endif
|
|
if (ret == 0 && (!validatedFromWire ||
|
|
info->pk.ed448checkkey.checkPriv)) {
|
|
ret = wc_ed448_check_key(info->pk.ed448checkkey.key);
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_ED448 */
|
|
|
|
#ifdef HAVE_CURVE25519
|
|
static int swdev_curve25519_keygen(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve25519_make_key(info->pk.curve25519kg.rng,
|
|
info->pk.curve25519kg.size, info->pk.curve25519kg.key);
|
|
}
|
|
|
|
#ifdef HAVE_CURVE25519_SHARED_SECRET
|
|
static int swdev_curve25519(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve25519_shared_secret_ex(info->pk.curve25519.private_key,
|
|
info->pk.curve25519.public_key, info->pk.curve25519.out,
|
|
info->pk.curve25519.outlen, info->pk.curve25519.endian);
|
|
}
|
|
#endif /* HAVE_CURVE25519_SHARED_SECRET */
|
|
|
|
static int swdev_curve25519_make_pub(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve25519_make_pub((int)info->pk.curve25519makepub.pubSz,
|
|
info->pk.curve25519makepub.pub,
|
|
(int)info->pk.curve25519makepub.privSz,
|
|
info->pk.curve25519makepub.priv);
|
|
}
|
|
|
|
static int swdev_curve25519_generic(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve25519_generic((int)info->pk.curve25519generic.pubSz,
|
|
info->pk.curve25519generic.pub,
|
|
(int)info->pk.curve25519generic.privSz,
|
|
info->pk.curve25519generic.priv,
|
|
(int)info->pk.curve25519generic.basepointSz,
|
|
info->pk.curve25519generic.basepoint);
|
|
}
|
|
#endif /* HAVE_CURVE25519 */
|
|
|
|
#ifdef HAVE_CURVE448
|
|
static int swdev_curve448_keygen(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve448_make_key(info->pk.curve448kg.rng,
|
|
info->pk.curve448kg.size, info->pk.curve448kg.key);
|
|
}
|
|
|
|
#ifdef HAVE_CURVE448_SHARED_SECRET
|
|
static int swdev_curve448(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve448_shared_secret_ex(info->pk.curve448.private_key,
|
|
info->pk.curve448.public_key, info->pk.curve448.out,
|
|
info->pk.curve448.outlen, info->pk.curve448.endian);
|
|
}
|
|
#endif /* HAVE_CURVE448_SHARED_SECRET */
|
|
|
|
static int swdev_curve448_make_pub(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve448_make_pub((int)info->pk.curve448makepub.pubSz,
|
|
info->pk.curve448makepub.pub,
|
|
(int)info->pk.curve448makepub.privSz,
|
|
info->pk.curve448makepub.priv);
|
|
}
|
|
|
|
static int swdev_curve448_generic(wc_CryptoInfo* info)
|
|
{
|
|
return wc_curve448_generic((int)info->pk.curve448generic.pubSz,
|
|
info->pk.curve448generic.pub,
|
|
(int)info->pk.curve448generic.privSz,
|
|
info->pk.curve448generic.priv,
|
|
(int)info->pk.curve448generic.basepointSz,
|
|
info->pk.curve448generic.basepoint);
|
|
}
|
|
#endif /* HAVE_CURVE448 */
|
|
#ifdef WOLFSSL_HAVE_SLHDSA
|
|
/* Take the key over for the duration of one operation.
|
|
*
|
|
* The key handed across the callback boundary still carries the device id, so
|
|
* the software calls below would dispatch straight back into swdev. Detach it
|
|
* and hand the old value back to the caller to restore.
|
|
*
|
|
* The parent library skips deriving the SHA-2 midstates from PK.seed when the
|
|
* software implementation is stripped, so re-import the public key here to
|
|
* bring them up to date. Importing the public key only sets the public flag,
|
|
* which is already set, so it is safe for private keys too.
|
|
*/
|
|
static int swdev_slhdsa_take(SlhDsaKey* key, int* devId)
|
|
{
|
|
int ret = 0;
|
|
|
|
*devId = INVALID_DEVID;
|
|
if ((key == NULL) || (key->params == NULL))
|
|
return BAD_FUNC_ARG;
|
|
|
|
*devId = key->devId;
|
|
key->devId = INVALID_DEVID;
|
|
|
|
if ((key->flags & WC_SLHDSA_FLAG_PUBLIC) != 0) {
|
|
byte pub[2 * WC_SLHDSA_MAX_SEED];
|
|
word32 pubSz = 2U * key->params->n;
|
|
|
|
/* Stage through a local buffer: import copies into the key and the
|
|
* source would otherwise be the destination. */
|
|
XMEMCPY(pub, key->sk + 2 * key->params->n, pubSz);
|
|
ret = wc_SlhDsaKey_ImportPublic(key, pub, pubSz);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void swdev_slhdsa_give_back(SlhDsaKey* key, int devId)
|
|
{
|
|
if (key != NULL)
|
|
key->devId = devId;
|
|
}
|
|
|
|
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
|
|
static int swdev_slhdsa_keygen(wc_CryptoInfo* info)
|
|
{
|
|
SlhDsaKey* key = (SlhDsaKey*)info->pk.pqc_sig_kg.key;
|
|
const byte* seed = info->pk.pqc_sig_kg.seed;
|
|
int devId;
|
|
int ret;
|
|
|
|
ret = swdev_slhdsa_take(key, &devId);
|
|
if (ret == 0) {
|
|
if (seed != NULL) {
|
|
byte n = key->params->n;
|
|
|
|
if (info->pk.pqc_sig_kg.seedSz != 3U * (word32)n) {
|
|
ret = BUFFER_E;
|
|
}
|
|
else {
|
|
ret = wc_SlhDsaKey_MakeKeyWithRandom(key, seed, n, seed + n, n,
|
|
seed + 2 * n, n);
|
|
}
|
|
}
|
|
else {
|
|
ret = wc_SlhDsaKey_MakeKey(key, info->pk.pqc_sig_kg.rng);
|
|
}
|
|
}
|
|
swdev_slhdsa_give_back(key, devId);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int swdev_slhdsa_sign(wc_CryptoInfo* info)
|
|
{
|
|
SlhDsaKey* key = (SlhDsaKey*)info->pk.pqc_sign.key;
|
|
const byte* addRnd = info->pk.pqc_sign.addRnd;
|
|
enum wc_HashType hashType = (enum wc_HashType)info->pk.pqc_sign.preHashType;
|
|
int devId;
|
|
int ret;
|
|
|
|
ret = swdev_slhdsa_take(key, &devId);
|
|
if (ret == 0) {
|
|
if (hashType == WC_HASH_TYPE_NONE) {
|
|
/* Pure SLH-DSA. */
|
|
if (addRnd != NULL) {
|
|
ret = wc_SlhDsaKey_SignWithRandom(key,
|
|
info->pk.pqc_sign.context, info->pk.pqc_sign.contextLen,
|
|
info->pk.pqc_sign.in, info->pk.pqc_sign.inlen,
|
|
info->pk.pqc_sign.out, info->pk.pqc_sign.outlen, addRnd);
|
|
}
|
|
else {
|
|
ret = wc_SlhDsaKey_Sign(key, info->pk.pqc_sign.context,
|
|
info->pk.pqc_sign.contextLen, info->pk.pqc_sign.in,
|
|
info->pk.pqc_sign.inlen, info->pk.pqc_sign.out,
|
|
info->pk.pqc_sign.outlen, info->pk.pqc_sign.rng);
|
|
}
|
|
}
|
|
else {
|
|
/* HashSLH-DSA over a caller supplied digest. */
|
|
if (addRnd != NULL) {
|
|
ret = wc_SlhDsaKey_SignHashWithRandom(key,
|
|
info->pk.pqc_sign.context, info->pk.pqc_sign.contextLen,
|
|
info->pk.pqc_sign.in, info->pk.pqc_sign.inlen, hashType,
|
|
info->pk.pqc_sign.out, info->pk.pqc_sign.outlen, addRnd);
|
|
}
|
|
else {
|
|
ret = wc_SlhDsaKey_SignHash(key, info->pk.pqc_sign.context,
|
|
info->pk.pqc_sign.contextLen, info->pk.pqc_sign.in,
|
|
info->pk.pqc_sign.inlen, hashType, info->pk.pqc_sign.out,
|
|
info->pk.pqc_sign.outlen, info->pk.pqc_sign.rng);
|
|
}
|
|
}
|
|
}
|
|
swdev_slhdsa_give_back(key, devId);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int swdev_slhdsa_sign_msg(wc_CryptoInfo* info)
|
|
{
|
|
SlhDsaKey* key = (SlhDsaKey*)info->pk.pqc_sign.key;
|
|
int devId;
|
|
int ret;
|
|
|
|
ret = swdev_slhdsa_take(key, &devId);
|
|
if (ret == 0) {
|
|
ret = wc_SlhDsaKey_SignMsgWithRandom(key, info->pk.pqc_sign.in,
|
|
info->pk.pqc_sign.inlen, info->pk.pqc_sign.out,
|
|
info->pk.pqc_sign.outlen, info->pk.pqc_sign.addRnd);
|
|
}
|
|
swdev_slhdsa_give_back(key, devId);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
|
|
|
|
static int swdev_slhdsa_verify(wc_CryptoInfo* info)
|
|
{
|
|
SlhDsaKey* key = (SlhDsaKey*)info->pk.pqc_verify.key;
|
|
enum wc_HashType hashType =
|
|
(enum wc_HashType)info->pk.pqc_verify.preHashType;
|
|
int devId;
|
|
int ret;
|
|
|
|
ret = swdev_slhdsa_take(key, &devId);
|
|
if (ret == 0) {
|
|
if (hashType == WC_HASH_TYPE_NONE) {
|
|
ret = wc_SlhDsaKey_Verify(key, info->pk.pqc_verify.context,
|
|
info->pk.pqc_verify.contextLen, info->pk.pqc_verify.msg,
|
|
info->pk.pqc_verify.msglen, info->pk.pqc_verify.sig,
|
|
info->pk.pqc_verify.siglen);
|
|
}
|
|
else {
|
|
ret = wc_SlhDsaKey_VerifyHash(key, info->pk.pqc_verify.context,
|
|
info->pk.pqc_verify.contextLen, info->pk.pqc_verify.msg,
|
|
info->pk.pqc_verify.msglen, hashType, info->pk.pqc_verify.sig,
|
|
info->pk.pqc_verify.siglen);
|
|
}
|
|
}
|
|
swdev_slhdsa_give_back(key, devId);
|
|
|
|
/* A bad signature is reported through res, not the return code. */
|
|
if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) {
|
|
*info->pk.pqc_verify.res = 0;
|
|
ret = 0;
|
|
}
|
|
else if (ret == 0) {
|
|
*info->pk.pqc_verify.res = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int swdev_slhdsa_verify_msg(wc_CryptoInfo* info)
|
|
{
|
|
SlhDsaKey* key = (SlhDsaKey*)info->pk.pqc_verify.key;
|
|
int devId;
|
|
int ret;
|
|
|
|
ret = swdev_slhdsa_take(key, &devId);
|
|
if (ret == 0) {
|
|
ret = wc_SlhDsaKey_VerifyMsg(key, info->pk.pqc_verify.msg,
|
|
info->pk.pqc_verify.msglen, info->pk.pqc_verify.sig,
|
|
info->pk.pqc_verify.siglen);
|
|
}
|
|
swdev_slhdsa_give_back(key, devId);
|
|
|
|
/* A bad signature is reported through res, not the return code. */
|
|
if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) {
|
|
*info->pk.pqc_verify.res = 0;
|
|
ret = 0;
|
|
}
|
|
else if (ret == 0) {
|
|
*info->pk.pqc_verify.res = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
|
|
static int swdev_slhdsa_check_priv_key(wc_CryptoInfo* info)
|
|
{
|
|
SlhDsaKey* key = (SlhDsaKey*)info->pk.pqc_sig_check.key;
|
|
int devId;
|
|
int ret;
|
|
|
|
/* The public key travels alongside the private key data, so check the
|
|
* caller's copy agrees before recomputing the root from the seeds. */
|
|
if ((info->pk.pqc_sig_check.pubKey != NULL) &&
|
|
(info->pk.pqc_sig_check.pubKeySz != 2U * key->params->n)) {
|
|
return BUFFER_E;
|
|
}
|
|
|
|
ret = swdev_slhdsa_take(key, &devId);
|
|
if (ret == 0) {
|
|
ret = wc_SlhDsaKey_CheckKey(key);
|
|
}
|
|
swdev_slhdsa_give_back(key, devId);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
|
|
|
|
static int swdev_slhdsa(wc_CryptoInfo* info, int pkType)
|
|
{
|
|
switch (pkType) {
|
|
#ifndef WOLFSSL_SLHDSA_VERIFY_ONLY
|
|
case WC_PK_TYPE_PQC_SIG_KEYGEN:
|
|
case WC_PK_TYPE_PQC_SIG_KEYGEN_SEED:
|
|
return swdev_slhdsa_keygen(info);
|
|
case WC_PK_TYPE_PQC_SIG_SIGN:
|
|
return swdev_slhdsa_sign(info);
|
|
case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
|
|
return swdev_slhdsa_sign_msg(info);
|
|
case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
|
|
return swdev_slhdsa_check_priv_key(info);
|
|
#endif /* !WOLFSSL_SLHDSA_VERIFY_ONLY */
|
|
case WC_PK_TYPE_PQC_SIG_VERIFY:
|
|
return swdev_slhdsa_verify(info);
|
|
case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
|
|
return swdev_slhdsa_verify_msg(info);
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
#endif /* WOLFSSL_HAVE_SLHDSA */
|
|
|
|
#ifdef HAVE_FALCON
|
|
#ifndef WOLFSSL_FALCON_VERIFY_ONLY
|
|
static int swdev_falcon_keygen(wc_CryptoInfo* info)
|
|
{
|
|
return wc_falcon_make_key((falcon_key*)info->pk.pqc_sig_kg.key,
|
|
info->pk.pqc_sig_kg.rng);
|
|
}
|
|
|
|
static int swdev_falcon_sign(wc_CryptoInfo* info)
|
|
{
|
|
return wc_falcon_sign_msg(info->pk.pqc_sign.in, info->pk.pqc_sign.inlen,
|
|
info->pk.pqc_sign.out, info->pk.pqc_sign.outlen,
|
|
(falcon_key*)info->pk.pqc_sign.key, info->pk.pqc_sign.rng);
|
|
}
|
|
#endif
|
|
|
|
static int swdev_falcon_verify(wc_CryptoInfo* info)
|
|
{
|
|
return wc_falcon_verify_msg(info->pk.pqc_verify.sig,
|
|
info->pk.pqc_verify.siglen, info->pk.pqc_verify.msg,
|
|
info->pk.pqc_verify.msglen, info->pk.pqc_verify.res,
|
|
(falcon_key*)info->pk.pqc_verify.key);
|
|
}
|
|
|
|
static int swdev_falcon(wc_CryptoInfo* info, int pkType)
|
|
{
|
|
switch (pkType) {
|
|
#ifndef WOLFSSL_FALCON_VERIFY_ONLY
|
|
case WC_PK_TYPE_PQC_SIG_KEYGEN:
|
|
return swdev_falcon_keygen(info);
|
|
case WC_PK_TYPE_PQC_SIG_SIGN:
|
|
return swdev_falcon_sign(info);
|
|
#endif
|
|
case WC_PK_TYPE_PQC_SIG_VERIFY:
|
|
return swdev_falcon_verify(info);
|
|
case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
|
|
/* The public key sent alongside is the key's own copy. */
|
|
return wc_falcon_check_key((falcon_key*)info->pk.pqc_sig_check.key);
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
#endif /* HAVE_FALCON */
|
|
|
|
#if defined(WOLFSSL_HAVE_SLHDSA) || defined(HAVE_FALCON)
|
|
/* Decline the families swdev has no handler for, so the caller falls back. */
|
|
static int swdev_pqc_sig(wc_CryptoInfo* info, int type, int pkType)
|
|
{
|
|
switch (type) {
|
|
#ifdef WOLFSSL_HAVE_SLHDSA
|
|
case WC_PQC_SIG_TYPE_SLHDSA:
|
|
return swdev_slhdsa(info, pkType);
|
|
#endif
|
|
#ifdef HAVE_FALCON
|
|
case WC_PQC_SIG_TYPE_FALCON:
|
|
return swdev_falcon(info, pkType);
|
|
#endif
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
#endif /* WOLFSSL_HAVE_SLHDSA || HAVE_FALCON */
|
|
|
|
#ifdef WOLFSSL_HAVE_MLKEM
|
|
#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY
|
|
static int swdev_mlkem_keygen(wc_CryptoInfo* info)
|
|
{
|
|
return wc_MlKemKey_MakeKey((MlKemKey*)info->pk.pqc_kem_kg.key,
|
|
info->pk.pqc_kem_kg.rng);
|
|
}
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE
|
|
static int swdev_mlkem_encaps(wc_CryptoInfo* info)
|
|
{
|
|
MlKemKey* key = (MlKemKey*)info->pk.pqc_encaps.key;
|
|
word32 ctSz = 0;
|
|
int ret;
|
|
|
|
ret = wc_MlKemKey_CipherTextSize(key, &ctSz);
|
|
if ((ret == 0) && ((info->pk.pqc_encaps.ciphertextLen != ctSz) ||
|
|
(info->pk.pqc_encaps.sharedSecretLen != WC_ML_KEM_SS_SZ))) {
|
|
ret = BUFFER_E;
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_MlKemKey_Encapsulate(key, info->pk.pqc_encaps.ciphertext,
|
|
info->pk.pqc_encaps.sharedSecret, info->pk.pqc_encaps.rng);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
|
|
static int swdev_mlkem_decaps(wc_CryptoInfo* info)
|
|
{
|
|
if (info->pk.pqc_decaps.sharedSecretLen != WC_ML_KEM_SS_SZ)
|
|
return BUFFER_E;
|
|
return wc_MlKemKey_Decapsulate((MlKemKey*)info->pk.pqc_decaps.key,
|
|
info->pk.pqc_decaps.sharedSecret, info->pk.pqc_decaps.ciphertext,
|
|
info->pk.pqc_decaps.ciphertextLen);
|
|
}
|
|
#endif
|
|
|
|
static int swdev_mlkem(wc_CryptoInfo* info, int pkType)
|
|
{
|
|
switch (pkType) {
|
|
#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY
|
|
case WC_PK_TYPE_PQC_KEM_KEYGEN:
|
|
return swdev_mlkem_keygen(info);
|
|
#endif
|
|
#ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE
|
|
case WC_PK_TYPE_PQC_KEM_ENCAPS:
|
|
return swdev_mlkem_encaps(info);
|
|
#endif
|
|
#ifndef WOLFSSL_MLKEM_NO_DECAPSULATE
|
|
case WC_PK_TYPE_PQC_KEM_DECAPS:
|
|
return swdev_mlkem_decaps(info);
|
|
#endif
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
#endif /* WOLFSSL_HAVE_MLKEM */
|
|
|
|
#ifdef WOLFSSL_HAVE_FRODOKEM
|
|
#ifndef WOLFSSL_FRODOKEM_NO_MAKE_KEY
|
|
static int swdev_frodokem_keygen(wc_CryptoInfo* info)
|
|
{
|
|
return wc_FrodoKemKey_MakeKey((FrodoKemKey*)info->pk.pqc_kem_kg.key,
|
|
info->pk.pqc_kem_kg.rng);
|
|
}
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_FRODOKEM_NO_ENCAPSULATE
|
|
static int swdev_frodokem_encaps(wc_CryptoInfo* info)
|
|
{
|
|
FrodoKemKey* key = (FrodoKemKey*)info->pk.pqc_encaps.key;
|
|
word32 ctSz = 0;
|
|
word32 ssSz = 0;
|
|
int ret;
|
|
|
|
ret = wc_FrodoKemKey_CipherTextSize(key, &ctSz);
|
|
if (ret == 0)
|
|
ret = wc_FrodoKemKey_SharedSecretSize(key, &ssSz);
|
|
if ((ret == 0) && ((info->pk.pqc_encaps.ciphertextLen != ctSz) ||
|
|
(info->pk.pqc_encaps.sharedSecretLen != ssSz))) {
|
|
ret = BUFFER_E;
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_FrodoKemKey_Encapsulate(key, info->pk.pqc_encaps.ciphertext,
|
|
info->pk.pqc_encaps.sharedSecret, info->pk.pqc_encaps.rng);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_FRODOKEM_NO_DECAPSULATE
|
|
static int swdev_frodokem_decaps(wc_CryptoInfo* info)
|
|
{
|
|
FrodoKemKey* key = (FrodoKemKey*)info->pk.pqc_decaps.key;
|
|
word32 ssSz = 0;
|
|
int ret;
|
|
|
|
ret = wc_FrodoKemKey_SharedSecretSize(key, &ssSz);
|
|
if ((ret == 0) && (info->pk.pqc_decaps.sharedSecretLen != ssSz))
|
|
ret = BUFFER_E;
|
|
if (ret == 0) {
|
|
ret = wc_FrodoKemKey_Decapsulate(key, info->pk.pqc_decaps.sharedSecret,
|
|
info->pk.pqc_decaps.ciphertext, info->pk.pqc_decaps.ciphertextLen);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int swdev_frodokem(wc_CryptoInfo* info, int pkType)
|
|
{
|
|
switch (pkType) {
|
|
#ifndef WOLFSSL_FRODOKEM_NO_MAKE_KEY
|
|
case WC_PK_TYPE_PQC_KEM_KEYGEN:
|
|
return swdev_frodokem_keygen(info);
|
|
#endif
|
|
#ifndef WOLFSSL_FRODOKEM_NO_ENCAPSULATE
|
|
case WC_PK_TYPE_PQC_KEM_ENCAPS:
|
|
return swdev_frodokem_encaps(info);
|
|
#endif
|
|
#ifndef WOLFSSL_FRODOKEM_NO_DECAPSULATE
|
|
case WC_PK_TYPE_PQC_KEM_DECAPS:
|
|
return swdev_frodokem_decaps(info);
|
|
#endif
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
#endif /* WOLFSSL_HAVE_FRODOKEM */
|
|
|
|
#if defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_HAVE_FRODOKEM)
|
|
/* Dispatch a PQC KEM operation, declining the families swdev has no handler
|
|
* for so the caller can fall back. */
|
|
static int swdev_pqc_kem(wc_CryptoInfo* info, int type, int pkType)
|
|
{
|
|
switch (type) {
|
|
#ifdef WOLFSSL_HAVE_MLKEM
|
|
case WC_PQC_KEM_TYPE_MLKEM:
|
|
return swdev_mlkem(info, pkType);
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_FRODOKEM
|
|
case WC_PQC_KEM_TYPE_FRODOKEM:
|
|
return swdev_frodokem(info, pkType);
|
|
#endif
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
#endif /* WOLFSSL_HAVE_MLKEM || WOLFSSL_HAVE_FRODOKEM */
|
|
|
|
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
|
|
|
|
#if (defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)) || \
|
|
(defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY))
|
|
#define SWDEV_STATEFUL_SIGN
|
|
#endif
|
|
|
|
/* Signing key to model a device-resident key */
|
|
typedef struct swdev_stateful_key {
|
|
struct swdev_stateful_key* next;
|
|
int type;
|
|
byte* priv;
|
|
word32 privSz;
|
|
#ifdef WOLFSSL_HAVE_LMS
|
|
LmsKey lms;
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_XMSS
|
|
XmssKey xmss;
|
|
#endif
|
|
} swdev_stateful_key;
|
|
|
|
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
|
|
static int swdev_lms_write(const byte* priv, word32 privSz, void* context)
|
|
{
|
|
swdev_stateful_key* dev = (swdev_stateful_key*)context;
|
|
|
|
if ((dev == NULL) || (priv == NULL))
|
|
return WC_LMS_RC_BAD_ARG;
|
|
if (privSz > dev->privSz)
|
|
return WC_LMS_RC_WRITE_FAIL;
|
|
|
|
XMEMCPY(dev->priv, priv, privSz);
|
|
return WC_LMS_RC_SAVED_TO_NV_MEMORY;
|
|
}
|
|
|
|
static int swdev_lms_read(byte* priv, word32 privSz, void* context)
|
|
{
|
|
swdev_stateful_key* dev = (swdev_stateful_key*)context;
|
|
|
|
if ((dev == NULL) || (priv == NULL))
|
|
return WC_LMS_RC_BAD_ARG;
|
|
if (privSz > dev->privSz)
|
|
return WC_LMS_RC_READ_FAIL;
|
|
|
|
XMEMCPY(priv, dev->priv, privSz);
|
|
return WC_LMS_RC_READ_TO_MEMORY;
|
|
}
|
|
#endif
|
|
|
|
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
|
|
static enum wc_XmssRc swdev_xmss_write(const byte* priv, word32 privSz,
|
|
void* context)
|
|
{
|
|
swdev_stateful_key* dev = (swdev_stateful_key*)context;
|
|
|
|
if ((dev == NULL) || (priv == NULL) || (privSz > dev->privSz))
|
|
return WC_XMSS_RC_WRITE_FAIL;
|
|
|
|
XMEMCPY(dev->priv, priv, privSz);
|
|
return WC_XMSS_RC_SAVED_TO_NV_MEMORY;
|
|
}
|
|
|
|
static enum wc_XmssRc swdev_xmss_read(byte* priv, word32 privSz, void* context)
|
|
{
|
|
swdev_stateful_key* dev = (swdev_stateful_key*)context;
|
|
|
|
if ((dev == NULL) || (priv == NULL) || (privSz > dev->privSz))
|
|
return WC_XMSS_RC_READ_FAIL;
|
|
|
|
XMEMCPY(priv, dev->priv, privSz);
|
|
return WC_XMSS_RC_READ_TO_MEMORY;
|
|
}
|
|
#endif
|
|
|
|
/* Track freed keys and sweep at cleanup to avoid sanitizer issues. */
|
|
static swdev_stateful_key* swdev_stateful_list = NULL;
|
|
|
|
static void swdev_stateful_untrack(swdev_stateful_key* dev)
|
|
{
|
|
swdev_stateful_key** p = &swdev_stateful_list;
|
|
|
|
while (*p != NULL) {
|
|
if (*p == dev) {
|
|
*p = dev->next;
|
|
return;
|
|
}
|
|
p = &(*p)->next;
|
|
}
|
|
}
|
|
|
|
static void swdev_stateful_free(swdev_stateful_key* dev);
|
|
|
|
static void swdev_stateful_cleanup(void)
|
|
{
|
|
while (swdev_stateful_list != NULL) {
|
|
swdev_stateful_free(swdev_stateful_list);
|
|
}
|
|
}
|
|
|
|
static void swdev_stateful_free(swdev_stateful_key* dev)
|
|
{
|
|
if (dev == NULL)
|
|
return;
|
|
|
|
swdev_stateful_untrack(dev);
|
|
|
|
switch (dev->type) {
|
|
#ifdef WOLFSSL_HAVE_LMS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
wc_LmsKey_Free(&dev->lms);
|
|
break;
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_XMSS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
wc_XmssKey_Free(&dev->xmss);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (dev->priv != NULL) {
|
|
wc_ForceZero(dev->priv, dev->privSz);
|
|
XFREE(dev->priv, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
}
|
|
wc_ForceZero(dev, sizeof(*dev));
|
|
XFREE(dev, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
}
|
|
|
|
#if defined(SWDEV_STATEFUL_SIGN) || defined(WOLF_CRYPTO_CB_FREE)
|
|
static swdev_stateful_key* swdev_stateful_get(int type, void* key)
|
|
{
|
|
switch (type) {
|
|
#ifdef WOLFSSL_HAVE_LMS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
return (swdev_stateful_key*)((LmsKey*)key)->devCtx;
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_XMSS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
return (swdev_stateful_key*)((XmssKey*)key)->devCtx;
|
|
#endif
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
#endif /* SWDEV_STATEFUL_SIGN || WOLF_CRYPTO_CB_FREE */
|
|
|
|
/* Clear the key's devId so the public API takes its software path instead of
|
|
* dispatching straight back here. */
|
|
static int swdev_stateful_take(int type, void* key, int* devId)
|
|
{
|
|
int ret = 0;
|
|
|
|
*devId = INVALID_DEVID;
|
|
if (key == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
switch (type) {
|
|
#ifdef WOLFSSL_HAVE_LMS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
*devId = ((LmsKey*)key)->devId;
|
|
((LmsKey*)key)->devId = INVALID_DEVID;
|
|
break;
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_XMSS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
*devId = ((XmssKey*)key)->devId;
|
|
((XmssKey*)key)->devId = INVALID_DEVID;
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void swdev_stateful_give_back(int type, void* key, int devId)
|
|
{
|
|
if (key == NULL)
|
|
return;
|
|
|
|
switch (type) {
|
|
#ifdef WOLFSSL_HAVE_LMS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
((LmsKey*)key)->devId = devId;
|
|
break;
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_XMSS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
((XmssKey*)key)->devId = devId;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef SWDEV_STATEFUL_SIGN
|
|
/* Generate a key that stays on the device. Only the public half is copied
|
|
* back, so the caller can perform export and verify. */
|
|
static int swdev_stateful_keygen(wc_CryptoInfo* info)
|
|
{
|
|
int type = info->pk.pqc_stateful_sig_kg.type;
|
|
void* key = info->pk.pqc_stateful_sig_kg.key;
|
|
swdev_stateful_key* dev;
|
|
word32 privSz = 0;
|
|
int ret;
|
|
|
|
if (key == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
dev = (swdev_stateful_key*)XMALLOC(sizeof(*dev), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (dev == NULL)
|
|
return MEMORY_E;
|
|
XMEMSET(dev, 0, sizeof(*dev));
|
|
dev->type = type;
|
|
|
|
switch (type) {
|
|
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS: {
|
|
const LmsKey* src = (const LmsKey*)key;
|
|
int levels = 0, height = 0, winternitz = 0, hash = 0;
|
|
|
|
ret = wc_LmsKey_Init(&dev->lms, NULL, INVALID_DEVID);
|
|
if (ret == 0) {
|
|
ret = wc_LmsKey_GetParameters_ex(src, &levels, &height,
|
|
&winternitz, &hash);
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_LmsKey_SetParameters_ex(&dev->lms, levels, height,
|
|
winternitz, hash);
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_LmsKey_GetPrivLen(&dev->lms, &privSz);
|
|
}
|
|
#ifdef WOLFSSL_WC_LMS_SERIALIZE_STATE
|
|
/* The write callback receives the traversal state as well. */
|
|
if (ret == 0) {
|
|
privSz += LMS_PRIV_DATA_LEN(dev->lms.params->levels,
|
|
dev->lms.params->height, dev->lms.params->p,
|
|
dev->lms.params->rootLevels, dev->lms.params->cacheBits,
|
|
dev->lms.params->hash_len);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
#endif
|
|
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS: {
|
|
const XmssKey* src = (const XmssKey*)key;
|
|
|
|
ret = wc_XmssKey_Init(&dev->xmss, NULL, INVALID_DEVID);
|
|
if (ret == 0) {
|
|
/* Mirror the caller's parameter set. */
|
|
dev->xmss.oid = src->oid;
|
|
dev->xmss.params = src->params;
|
|
dev->xmss.sk_len = src->sk_len;
|
|
dev->xmss.is_xmssmt = src->is_xmssmt;
|
|
dev->xmss.state = WC_XMSS_STATE_PARMSET;
|
|
ret = wc_XmssKey_GetPrivLen(&dev->xmss, &privSz);
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
default:
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
break;
|
|
}
|
|
|
|
/* Stands in for the device's nonvolatile storage. */
|
|
if (ret == 0) {
|
|
dev->privSz = privSz;
|
|
dev->priv = (byte*)XMALLOC(privSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (dev->priv == NULL) {
|
|
ret = MEMORY_E;
|
|
}
|
|
else {
|
|
XMEMSET(dev->priv, 0, privSz);
|
|
}
|
|
}
|
|
|
|
switch ((ret == 0) ? type : WC_PQC_STATEFUL_SIG_TYPE_NONE) {
|
|
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
{
|
|
const LmsKey* src = (const LmsKey*)key;
|
|
/* Persist where the caller asked, or in the device otherwise. */
|
|
if (src->write_private_key != NULL) {
|
|
ret = wc_LmsKey_SetWriteCb(&dev->lms, src->write_private_key);
|
|
if ((ret == 0) && (src->read_private_key != NULL)) {
|
|
ret = wc_LmsKey_SetReadCb(&dev->lms,
|
|
src->read_private_key);
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_LmsKey_SetContext(&dev->lms, src->context);
|
|
}
|
|
}
|
|
else {
|
|
ret = wc_LmsKey_SetWriteCb(&dev->lms, swdev_lms_write);
|
|
if (ret == 0) {
|
|
ret = wc_LmsKey_SetReadCb(&dev->lms, swdev_lms_read);
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_LmsKey_SetContext(&dev->lms, dev);
|
|
}
|
|
}
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_LmsKey_MakeKey(&dev->lms,
|
|
info->pk.pqc_stateful_sig_kg.rng);
|
|
}
|
|
if (ret == 0) {
|
|
LmsKey* dst = (LmsKey*)key;
|
|
XMEMCPY(dst->pub, dev->lms.pub, sizeof(dst->pub));
|
|
dst->devCtx = dev;
|
|
}
|
|
break;
|
|
#endif
|
|
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
{
|
|
const XmssKey* src = (const XmssKey*)key;
|
|
/* Persist where the caller asked, or in the device otherwise. */
|
|
if (src->write_private_key != NULL) {
|
|
ret = wc_XmssKey_SetWriteCb(&dev->xmss,
|
|
src->write_private_key);
|
|
if ((ret == 0) && (src->read_private_key != NULL)) {
|
|
ret = wc_XmssKey_SetReadCb(&dev->xmss,
|
|
src->read_private_key);
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_XmssKey_SetContext(&dev->xmss, src->context);
|
|
}
|
|
}
|
|
else {
|
|
ret = wc_XmssKey_SetWriteCb(&dev->xmss, swdev_xmss_write);
|
|
if (ret == 0) {
|
|
ret = wc_XmssKey_SetReadCb(&dev->xmss, swdev_xmss_read);
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_XmssKey_SetContext(&dev->xmss, dev);
|
|
}
|
|
}
|
|
}
|
|
if (ret == 0) {
|
|
ret = wc_XmssKey_MakeKey(&dev->xmss,
|
|
info->pk.pqc_stateful_sig_kg.rng);
|
|
}
|
|
if (ret == 0) {
|
|
XmssKey* dst = (XmssKey*)key;
|
|
XMEMCPY(dst->pk, dev->xmss.pk, sizeof(dst->pk));
|
|
dst->devCtx = dev;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (ret != 0) {
|
|
swdev_stateful_free(dev);
|
|
}
|
|
else {
|
|
dev->next = swdev_stateful_list;
|
|
swdev_stateful_list = dev;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int swdev_stateful_sign(wc_CryptoInfo* info)
|
|
{
|
|
int type = info->pk.pqc_stateful_sig_sign.type;
|
|
swdev_stateful_key* dev;
|
|
int ret;
|
|
|
|
dev = swdev_stateful_get(type, info->pk.pqc_stateful_sig_sign.key);
|
|
if (dev == NULL)
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
|
|
switch (type) {
|
|
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
ret = wc_LmsKey_Sign(&dev->lms, info->pk.pqc_stateful_sig_sign.out,
|
|
info->pk.pqc_stateful_sig_sign.outSz,
|
|
info->pk.pqc_stateful_sig_sign.msg,
|
|
(int)info->pk.pqc_stateful_sig_sign.msgSz);
|
|
break;
|
|
#endif
|
|
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
ret = wc_XmssKey_Sign(&dev->xmss, info->pk.pqc_stateful_sig_sign.out,
|
|
info->pk.pqc_stateful_sig_sign.outSz,
|
|
info->pk.pqc_stateful_sig_sign.msg,
|
|
(int)info->pk.pqc_stateful_sig_sign.msgSz);
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int swdev_stateful_sigsleft(wc_CryptoInfo* info)
|
|
{
|
|
int type = info->pk.pqc_stateful_sig_sigs_left.type;
|
|
swdev_stateful_key* dev;
|
|
int ret = 0;
|
|
|
|
if (info->pk.pqc_stateful_sig_sigs_left.sigsLeft == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
dev = swdev_stateful_get(type, info->pk.pqc_stateful_sig_sigs_left.key);
|
|
if (dev == NULL)
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
|
|
switch (type) {
|
|
#if defined(WOLFSSL_HAVE_LMS) && !defined(WOLFSSL_LMS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
*info->pk.pqc_stateful_sig_sigs_left.sigsLeft =
|
|
(word32)wc_LmsKey_SigsLeft(&dev->lms);
|
|
break;
|
|
#endif
|
|
#if defined(WOLFSSL_HAVE_XMSS) && !defined(WOLFSSL_XMSS_VERIFY_ONLY)
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
*info->pk.pqc_stateful_sig_sigs_left.sigsLeft =
|
|
(word32)wc_XmssKey_SigsLeft(&dev->xmss);
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* SWDEV_STATEFUL_SIGN */
|
|
|
|
static int swdev_stateful_verify(wc_CryptoInfo* info)
|
|
{
|
|
int type = info->pk.pqc_stateful_sig_verify.type;
|
|
void* key = info->pk.pqc_stateful_sig_verify.key;
|
|
int* res = info->pk.pqc_stateful_sig_verify.res;
|
|
int devId;
|
|
int ret;
|
|
|
|
ret = swdev_stateful_take(type, key, &devId);
|
|
if (ret == 0) {
|
|
switch (type) {
|
|
#ifdef WOLFSSL_HAVE_LMS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
ret = wc_LmsKey_Verify((LmsKey*)key,
|
|
info->pk.pqc_stateful_sig_verify.sig,
|
|
info->pk.pqc_stateful_sig_verify.sigSz,
|
|
info->pk.pqc_stateful_sig_verify.msg,
|
|
(int)info->pk.pqc_stateful_sig_verify.msgSz);
|
|
break;
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_XMSS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
ret = wc_XmssKey_Verify((XmssKey*)key,
|
|
info->pk.pqc_stateful_sig_verify.sig,
|
|
info->pk.pqc_stateful_sig_verify.sigSz,
|
|
info->pk.pqc_stateful_sig_verify.msg,
|
|
(int)info->pk.pqc_stateful_sig_verify.msgSz);
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
break;
|
|
}
|
|
swdev_stateful_give_back(type, key, devId);
|
|
}
|
|
|
|
/* Only a bad signature is a verdict; every other failure propagates. */
|
|
if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E)) {
|
|
if (res != NULL) {
|
|
*res = 0;
|
|
}
|
|
ret = 0;
|
|
}
|
|
else if ((ret == 0) && (res != NULL)) {
|
|
*res = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(WOLF_CRYPTO_CB_FREE)
|
|
/* Release the device-resident key the caller is done with. */
|
|
static int swdev_stateful_free_cb(wc_CryptoInfo* info)
|
|
{
|
|
swdev_stateful_key* dev = swdev_stateful_get(info->free.subType,
|
|
info->free.obj);
|
|
|
|
if (dev != NULL) {
|
|
swdev_stateful_free(dev);
|
|
switch (info->free.subType) {
|
|
#ifdef WOLFSSL_HAVE_LMS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_LMS:
|
|
((LmsKey*)info->free.obj)->devCtx = NULL;
|
|
break;
|
|
#endif
|
|
#ifdef WOLFSSL_HAVE_XMSS
|
|
case WC_PQC_STATEFUL_SIG_TYPE_XMSS:
|
|
((XmssKey*)info->free.obj)->devCtx = NULL;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* WOLF_CRYPTO_CB_FREE */
|
|
|
|
static int swdev_pqc_stateful_sig(wc_CryptoInfo* info, int pkType)
|
|
{
|
|
switch (pkType) {
|
|
#ifdef SWDEV_STATEFUL_SIGN
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN:
|
|
return swdev_stateful_keygen(info);
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN:
|
|
return swdev_stateful_sign(info);
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT:
|
|
return swdev_stateful_sigsleft(info);
|
|
#endif
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY:
|
|
return swdev_stateful_verify(info);
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|
|
|
|
#endif /* WOLFSSL_HAVE_LMS || WOLFSSL_HAVE_XMSS */
|
|
|
|
#if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256)
|
|
/* Copy sponge state between the caller's wc_Shake and swdev's shadow */
|
|
static void swdev_shake_copy_state(wc_Shake* dst, const wc_Shake* src)
|
|
{
|
|
XMEMCPY(dst->s, src->s, sizeof(dst->s));
|
|
XMEMCPY(dst->t, src->t, sizeof(dst->t));
|
|
dst->i = src->i;
|
|
#ifdef WOLFSSL_HASH_FLAGS
|
|
dst->flags = src->flags;
|
|
#endif
|
|
}
|
|
|
|
static int swdev_shake_op(const wc_CryptoInfo* info)
|
|
{
|
|
#ifdef WOLF_CRYPTO_CB_SHAKE_XOF
|
|
return info->hash.shakeOp;
|
|
#else
|
|
(void)info;
|
|
return WC_SHAKE_OP_NONE;
|
|
#endif
|
|
}
|
|
|
|
typedef struct swdev_shake_funcs {
|
|
int type;
|
|
word32 rate;
|
|
int (*initFn)(wc_Shake*, void*, int);
|
|
int (*updateFn)(wc_Shake*, const byte*, word32);
|
|
int (*finalFn)(wc_Shake*, byte*, word32);
|
|
int (*absorbFn)(wc_Shake*, const byte*, word32);
|
|
int (*squeezeFn)(wc_Shake*, byte*, word32);
|
|
void (*freeFn)(wc_Shake*);
|
|
} swdev_shake_funcs;
|
|
|
|
static const swdev_shake_funcs swdev_shake_table[] = {
|
|
#ifdef WOLFSSL_SHAKE128
|
|
{ WC_HASH_TYPE_SHAKE128, WC_SHA3_128_COUNT * 8U, wc_InitShake128,
|
|
wc_Shake128_Update, wc_Shake128_Final, wc_Shake128_Absorb,
|
|
wc_Shake128_SqueezeBlocks, wc_Shake128_Free },
|
|
#endif
|
|
#ifdef WOLFSSL_SHAKE256
|
|
{ WC_HASH_TYPE_SHAKE256, WC_SHA3_256_COUNT * 8U, wc_InitShake256,
|
|
wc_Shake256_Update, wc_Shake256_Final, wc_Shake256_Absorb,
|
|
wc_Shake256_SqueezeBlocks, wc_Shake256_Free },
|
|
#endif
|
|
};
|
|
|
|
/* SHAKE handler. When shakeOp is WC_SHAKE_OP_NONE, update and final operations
|
|
* are determined by hash.digest. Otherwise hash.shakeOp selects the op. */
|
|
static int swdev_shake(wc_CryptoInfo* info)
|
|
{
|
|
wc_Shake* shake = info->hash.sha3;
|
|
wc_Shake shadow;
|
|
const swdev_shake_funcs* f = NULL;
|
|
size_t idx;
|
|
int ret;
|
|
|
|
if (shake == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
for (idx = 0; idx < sizeof(swdev_shake_table) /
|
|
sizeof(swdev_shake_table[0]); idx++) {
|
|
if (swdev_shake_table[idx].type == info->hash.type) {
|
|
f = &swdev_shake_table[idx];
|
|
break;
|
|
}
|
|
}
|
|
if (f == NULL)
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
|
|
ret = f->initFn(&shadow, NULL, INVALID_DEVID);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
swdev_shake_copy_state(&shadow, shake);
|
|
|
|
switch (swdev_shake_op(info)) {
|
|
case WC_SHAKE_OP_ABSORB:
|
|
ret = f->absorbFn(&shadow, info->hash.in, info->hash.inSz);
|
|
break;
|
|
|
|
case WC_SHAKE_OP_SQUEEZE:
|
|
/* outSz is the byte count; the API takes whole blocks. */
|
|
if ((info->hash.outSz % f->rate) != 0) {
|
|
ret = BAD_FUNC_ARG;
|
|
break;
|
|
}
|
|
ret = f->squeezeFn(&shadow, info->hash.digest,
|
|
info->hash.outSz / f->rate);
|
|
break;
|
|
|
|
default:
|
|
if (info->hash.in != NULL) {
|
|
ret = f->updateFn(&shadow, info->hash.in, info->hash.inSz);
|
|
}
|
|
if ((ret == 0) && (info->hash.digest != NULL)) {
|
|
ret = f->finalFn(&shadow, info->hash.digest, info->hash.outSz);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
swdev_shake_copy_state(shake, &shadow);
|
|
}
|
|
|
|
f->freeFn(&shadow);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_SHAKE128 || WOLFSSL_SHAKE256 */
|
|
|
|
#ifndef NO_SHA256
|
|
/* Copy hash state between caller's wc_Sha256 and swdev's shadow, leaving
|
|
* admin fields (heap, devId, devCtx, W, async, HW ctx) per-side. */
|
|
static void swdev_sha256_copy_state(wc_Sha256* dst, const wc_Sha256* src)
|
|
{
|
|
XMEMCPY(dst->digest, src->digest, sizeof(dst->digest));
|
|
XMEMCPY(dst->buffer, src->buffer, sizeof(dst->buffer));
|
|
dst->buffLen = src->buffLen;
|
|
dst->loLen = src->loLen;
|
|
dst->hiLen = src->hiLen;
|
|
#if defined(WC_C_DYNAMIC_FALLBACK) && defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
|
|
dst->sha_method = src->sha_method;
|
|
#endif
|
|
#ifdef WOLFSSL_HASH_FLAGS
|
|
dst->flags = src->flags;
|
|
#endif
|
|
}
|
|
|
|
/* Run the op on a per-call shadow wc_Sha256 owned by swdev, copying state
|
|
* in and out around it. The caller's struct, allocated by libwolfssl with
|
|
* the software init stripped, can't be used directly. */
|
|
static int swdev_sha256(wc_CryptoInfo* info)
|
|
{
|
|
wc_Sha256* sha256 = info->hash.sha256;
|
|
wc_Sha256 shadow;
|
|
int ret;
|
|
|
|
if (sha256 == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_InitSha256(&shadow);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
swdev_sha256_copy_state(&shadow, sha256);
|
|
|
|
if (info->hash.in != NULL) {
|
|
ret = wc_Sha256Update(&shadow, info->hash.in, info->hash.inSz);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (info->hash.digest != NULL) {
|
|
ret = wc_Sha256Final(&shadow, info->hash.digest);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
swdev_sha256_copy_state(sha256, &shadow);
|
|
|
|
out:
|
|
wc_Sha256Free(&shadow);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef WOLFSSL_SHA224
|
|
/* SHA-224 is SHA-256 with a different IV/truncation; wc_Sha224 is a typedef
|
|
* of wc_Sha256, so the same shadow/copy-state dance applies. */
|
|
static int swdev_sha224(wc_CryptoInfo* info)
|
|
{
|
|
wc_Sha224* sha224 = info->hash.sha224;
|
|
wc_Sha224 shadow;
|
|
int ret;
|
|
|
|
if (sha224 == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_InitSha224(&shadow);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
swdev_sha256_copy_state((wc_Sha256*)&shadow, (const wc_Sha256*)sha224);
|
|
|
|
if (info->hash.in != NULL) {
|
|
ret = wc_Sha224Update(&shadow, info->hash.in, info->hash.inSz);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (info->hash.digest != NULL) {
|
|
ret = wc_Sha224Final(&shadow, info->hash.digest);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
swdev_sha256_copy_state((wc_Sha256*)sha224, (const wc_Sha256*)&shadow);
|
|
|
|
out:
|
|
wc_Sha224Free(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_SHA224 */
|
|
#endif /* !NO_SHA256 */
|
|
|
|
#if defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384)
|
|
/* Copy hash state between caller's wc_Sha512 and swdev's shadow, leaving
|
|
* admin fields (heap, devId, devCtx, W, async, HW ctx) per-side. The same
|
|
* helper works for SHA-384, SHA-512/224, SHA-512/256 since they all typedef
|
|
* to wc_Sha512. */
|
|
static void swdev_sha512_copy_state(wc_Sha512* dst, const wc_Sha512* src)
|
|
{
|
|
XMEMCPY(dst->digest, src->digest, sizeof(dst->digest));
|
|
XMEMCPY(dst->buffer, src->buffer, sizeof(dst->buffer));
|
|
dst->buffLen = src->buffLen;
|
|
dst->loLen = src->loLen;
|
|
dst->hiLen = src->hiLen;
|
|
#if defined(WC_C_DYNAMIC_FALLBACK) && defined(HAVE_FIPS) && FIPS_VERSION3_LT(7,0,0)
|
|
dst->sha_method = src->sha_method;
|
|
#endif
|
|
#ifdef WOLFSSL_HASH_FLAGS
|
|
dst->flags = src->flags;
|
|
#endif
|
|
#if defined(WOLFSSL_SHA512_HASHTYPE)
|
|
dst->hashType = src->hashType;
|
|
#endif
|
|
}
|
|
#endif /* WOLFSSL_SHA512 || WOLFSSL_SHA384 */
|
|
|
|
#ifdef WOLFSSL_SHA512
|
|
static int swdev_sha512(wc_CryptoInfo* info)
|
|
{
|
|
wc_Sha512* sha512 = info->hash.sha512;
|
|
wc_Sha512 shadow;
|
|
int ret;
|
|
|
|
if (sha512 == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_InitSha512(&shadow);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
swdev_sha512_copy_state(&shadow, sha512);
|
|
|
|
if (info->hash.in != NULL) {
|
|
ret = wc_Sha512Update(&shadow, info->hash.in, info->hash.inSz);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
if (info->hash.digest != NULL) {
|
|
ret = wc_Sha512Final(&shadow, info->hash.digest);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
swdev_sha512_copy_state(sha512, &shadow);
|
|
|
|
out:
|
|
wc_Sha512Free(&shadow);
|
|
return ret;
|
|
}
|
|
|
|
#if !defined(WOLFSSL_NOSHA512_224) && \
|
|
!defined(WOLFSSL_SWDEV_SHA512_GENERAL_ONLY)
|
|
static int swdev_sha512_224(wc_CryptoInfo* info)
|
|
{
|
|
wc_Sha512 shadow;
|
|
wc_Sha512* sha = info->hash.sha512;
|
|
int ret;
|
|
|
|
if (sha == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_InitSha512_224(&shadow);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
swdev_sha512_copy_state(&shadow, sha);
|
|
|
|
if (info->hash.in != NULL) {
|
|
ret = wc_Sha512_224Update(&shadow, info->hash.in, info->hash.inSz);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
if (info->hash.digest != NULL) {
|
|
ret = wc_Sha512_224Final(&shadow, info->hash.digest);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
swdev_sha512_copy_state(sha, &shadow);
|
|
|
|
out:
|
|
wc_Sha512_224Free(&shadow);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(WOLFSSL_NOSHA512_256) && \
|
|
!defined(WOLFSSL_SWDEV_SHA512_GENERAL_ONLY)
|
|
static int swdev_sha512_256(wc_CryptoInfo* info)
|
|
{
|
|
wc_Sha512 shadow;
|
|
wc_Sha512* sha = info->hash.sha512;
|
|
int ret;
|
|
|
|
if (sha == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_InitSha512_256(&shadow);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
swdev_sha512_copy_state(&shadow, sha);
|
|
|
|
if (info->hash.in != NULL) {
|
|
ret = wc_Sha512_256Update(&shadow, info->hash.in, info->hash.inSz);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
if (info->hash.digest != NULL) {
|
|
ret = wc_Sha512_256Final(&shadow, info->hash.digest);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
swdev_sha512_copy_state(sha, &shadow);
|
|
|
|
out:
|
|
wc_Sha512_256Free(&shadow);
|
|
return ret;
|
|
}
|
|
#endif
|
|
#endif /* WOLFSSL_SHA512 */
|
|
|
|
#if defined(WOLFSSL_SHA384) && !defined(WOLFSSL_SWDEV_SHA512_GENERAL_ONLY)
|
|
/* SHA-384 is SHA-512 with a different IV/truncation; wc_Sha384 is a typedef
|
|
* of wc_Sha512, so the shadow/copy-state dance is identical to swdev_sha512.
|
|
* When WOLFSSL_SWDEV_SHA512_GENERAL_ONLY is set this is omitted so swdev declines
|
|
* SHA-384 and the cryptocb dispatcher's SHA-512 fallback path is exercised. */
|
|
static int swdev_sha384(wc_CryptoInfo* info)
|
|
{
|
|
wc_Sha384* sha384 = info->hash.sha384;
|
|
wc_Sha384 shadow;
|
|
int ret;
|
|
|
|
if (sha384 == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_InitSha384(&shadow);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
swdev_sha512_copy_state(&shadow, sha384);
|
|
|
|
if (info->hash.in != NULL) {
|
|
ret = wc_Sha384Update(&shadow, info->hash.in, info->hash.inSz);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
if (info->hash.digest != NULL) {
|
|
ret = wc_Sha384Final(&shadow, info->hash.digest);
|
|
if (ret != 0)
|
|
goto out;
|
|
}
|
|
|
|
swdev_sha512_copy_state(sha384, &shadow);
|
|
|
|
out:
|
|
wc_Sha384Free(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_SHA384 && !WOLFSSL_SWDEV_SHA512_GENERAL_ONLY */
|
|
|
|
#ifndef NO_AES
|
|
/* Rebuild a software AES shadow from the caller's raw devKey, since the
|
|
* caller's Aes has no software round-key schedule under CB_ONLY_AES. */
|
|
static int swdev_aes_shadow_init(Aes* shadow, const Aes* aes, int dir)
|
|
{
|
|
int ret;
|
|
|
|
if (shadow == NULL || aes == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (aes->keylen <= 0 || aes->keylen > (int)sizeof(aes->devKey))
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_AesInit(shadow, aes->heap, INVALID_DEVID);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = wc_AesSetKey(shadow, (const byte*)aes->devKey,
|
|
(word32)aes->keylen, (const byte*)aes->reg, dir);
|
|
if (ret != 0) {
|
|
wc_AesFree(shadow);
|
|
return ret;
|
|
}
|
|
|
|
XMEMCPY(shadow->tmp, aes->tmp, sizeof(shadow->tmp));
|
|
#if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \
|
|
defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \
|
|
defined(WOLFSSL_AES_CTS)
|
|
shadow->left = aes->left;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void swdev_aes_shadow_sync(Aes* dst, const Aes* src)
|
|
{
|
|
XMEMCPY(dst->reg, src->reg, sizeof(dst->reg));
|
|
XMEMCPY(dst->tmp, src->tmp, sizeof(dst->tmp));
|
|
#if defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_CFB) || \
|
|
defined(WOLFSSL_AES_OFB) || defined(WOLFSSL_AES_XTS) || \
|
|
defined(WOLFSSL_AES_CTS)
|
|
dst->left = src->left;
|
|
#endif
|
|
}
|
|
|
|
#ifdef HAVE_AES_CBC
|
|
static int swdev_aes_cbc(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.aescbc.aes;
|
|
byte* out = info->cipher.aescbc.out;
|
|
const byte* in = info->cipher.aescbc.in;
|
|
word32 sz = info->cipher.aescbc.sz;
|
|
Aes shadow;
|
|
int ret;
|
|
|
|
ret = swdev_aes_shadow_init(&shadow, aes,
|
|
info->cipher.enc ? AES_ENCRYPTION : AES_DECRYPTION);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
if (info->cipher.enc)
|
|
ret = wc_AesCbcEncrypt(&shadow, out, in, sz);
|
|
#ifdef HAVE_AES_DECRYPT
|
|
else
|
|
ret = wc_AesCbcDecrypt(&shadow, out, in, sz);
|
|
#else
|
|
else
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
swdev_aes_shadow_sync(aes, &shadow);
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_AES_CBC */
|
|
|
|
#ifdef WOLFSSL_AES_COUNTER
|
|
static int swdev_aes_ctr(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.aesctr.aes;
|
|
Aes shadow;
|
|
int ret;
|
|
|
|
ret = swdev_aes_shadow_init(&shadow, aes, AES_ENCRYPTION);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = wc_AesCtrEncrypt(&shadow, info->cipher.aesctr.out,
|
|
info->cipher.aesctr.in, info->cipher.aesctr.sz);
|
|
swdev_aes_shadow_sync(aes, &shadow);
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_AES_CFB
|
|
static int swdev_aes_cfb(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.aescfb.aes;
|
|
byte* out = info->cipher.aescfb.out;
|
|
const byte* in = info->cipher.aescfb.in;
|
|
word32 sz = info->cipher.aescfb.sz;
|
|
Aes shadow;
|
|
int ret;
|
|
|
|
/* CFB is a stream mode built on the forward cipher, so the shadow key
|
|
* schedule is always AES_ENCRYPTION (as for CTR), even when decrypting. */
|
|
ret = swdev_aes_shadow_init(&shadow, aes, AES_ENCRYPTION);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
if (info->cipher.enc)
|
|
ret = wc_AesCfbEncrypt(&shadow, out, in, sz);
|
|
#ifdef HAVE_AES_DECRYPT
|
|
else
|
|
ret = wc_AesCfbDecrypt(&shadow, out, in, sz);
|
|
#else
|
|
else
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
swdev_aes_shadow_sync(aes, &shadow);
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_AES_CFB */
|
|
|
|
#ifdef WOLFSSL_AES_OFB
|
|
static int swdev_aes_ofb(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.aesofb.aes;
|
|
byte* out = info->cipher.aesofb.out;
|
|
const byte* in = info->cipher.aesofb.in;
|
|
word32 sz = info->cipher.aesofb.sz;
|
|
Aes shadow;
|
|
int ret;
|
|
|
|
/* OFB is a stream mode built on the forward cipher, so the shadow key
|
|
* schedule is always AES_ENCRYPTION (as for CTR), even when decrypting. */
|
|
ret = swdev_aes_shadow_init(&shadow, aes, AES_ENCRYPTION);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
if (info->cipher.enc)
|
|
ret = wc_AesOfbEncrypt(&shadow, out, in, sz);
|
|
#ifdef HAVE_AES_DECRYPT
|
|
else
|
|
ret = wc_AesOfbDecrypt(&shadow, out, in, sz);
|
|
#else
|
|
else
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
swdev_aes_shadow_sync(aes, &shadow);
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* WOLFSSL_AES_OFB */
|
|
|
|
#if defined(HAVE_AES_ECB) || defined(WOLFSSL_AES_DIRECT)
|
|
static int swdev_aes_ecb(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.aesecb.aes;
|
|
byte* out = info->cipher.aesecb.out;
|
|
const byte* in = info->cipher.aesecb.in;
|
|
word32 sz = info->cipher.aesecb.sz;
|
|
Aes shadow;
|
|
int ret;
|
|
|
|
ret = swdev_aes_shadow_init(&shadow, aes,
|
|
info->cipher.enc ? AES_ENCRYPTION : AES_DECRYPTION);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
#ifdef HAVE_AES_ECB
|
|
if (info->cipher.enc)
|
|
ret = wc_AesEcbEncrypt(&shadow, out, in, sz);
|
|
#ifdef HAVE_AES_DECRYPT
|
|
else
|
|
ret = wc_AesEcbDecrypt(&shadow, out, in, sz);
|
|
#else
|
|
else
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
#elif defined(WOLFSSL_AES_DIRECT)
|
|
if (sz != WC_AES_BLOCK_SIZE) {
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
else if (info->cipher.enc) {
|
|
ret = wc_AesEncryptDirect(&shadow, out, in);
|
|
}
|
|
#ifdef HAVE_AES_DECRYPT
|
|
else {
|
|
ret = wc_AesDecryptDirect(&shadow, out, in);
|
|
}
|
|
#else
|
|
else {
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
#endif
|
|
#else
|
|
(void)out;
|
|
(void)in;
|
|
(void)sz;
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_AES_ECB || WOLFSSL_AES_DIRECT */
|
|
|
|
#if defined(HAVE_AES_KEYWRAP) && !defined(SWDEV_AES_ONLYECB)
|
|
/* AES Key Wrap (RFC 3394) and, when built, Key Wrap with Padding (RFC 5649).
|
|
* enc selects wrap (forward cipher) vs unwrap (inverse cipher); pad selects the
|
|
* RFC 5649 variant. The wrap/unwrap helpers return the produced byte count,
|
|
* which the cryptocb contract reports via outResSz with a 0 return. Gated like
|
|
* swdev_aes_gcm so SWDEV_AES_ONLYECB instead forces the parent's CB_ONLY_AES
|
|
* host-side key wrap (block ops dispatch back through cryptocb ECB). */
|
|
static int swdev_aes_keywrap(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.aeskeywrap.aes;
|
|
const byte* in = info->cipher.aeskeywrap.in;
|
|
word32 inSz = info->cipher.aeskeywrap.inSz;
|
|
byte* out = info->cipher.aeskeywrap.out;
|
|
word32 outSz = info->cipher.aeskeywrap.outSz;
|
|
const byte* iv = info->cipher.aeskeywrap.iv;
|
|
Aes shadow;
|
|
int ret;
|
|
int dir;
|
|
|
|
if (info->cipher.enc)
|
|
dir = AES_ENCRYPTION;
|
|
else
|
|
dir = AES_DECRYPTION;
|
|
|
|
ret = swdev_aes_shadow_init(&shadow, aes, dir);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
if (info->cipher.enc) {
|
|
#ifdef WOLFSSL_AES_KEYWRAP_PADDING
|
|
if (info->cipher.aeskeywrap.pad)
|
|
ret = wc_AesKeyWrap_Pad_ex(&shadow, in, inSz, out, outSz, iv);
|
|
else
|
|
#endif
|
|
ret = wc_AesKeyWrap_ex(&shadow, in, inSz, out, outSz, iv);
|
|
}
|
|
else {
|
|
#ifdef WOLFSSL_AES_KEYWRAP_PADDING
|
|
if (info->cipher.aeskeywrap.pad)
|
|
ret = wc_AesKeyUnWrap_Pad_ex(&shadow, in, inSz, out, outSz, iv);
|
|
else
|
|
#endif
|
|
ret = wc_AesKeyUnWrap_ex(&shadow, in, inSz, out, outSz, iv);
|
|
}
|
|
|
|
wc_AesFree(&shadow);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* success: report produced length; cryptocb returns outResSz on ret==0 */
|
|
info->cipher.aeskeywrap.outResSz = (word32)ret;
|
|
return 0;
|
|
}
|
|
#endif /* HAVE_AES_KEYWRAP && !SWDEV_AES_ONLYECB */
|
|
|
|
/* SWDEV_AES_ONLYECB: when defined, swdev's AES backend returns
|
|
* CRYPTOCB_UNAVAILABLE for AES-GCM so the parent's CB_ONLY_AES host-side
|
|
* GCM software path runs (GHASH on the host; AES-CTR blocks dispatch back
|
|
* through cryptocb ECB). Without this macro, swdev handles GCM end-to-end
|
|
* and the host-side software path is never exercised. */
|
|
#if defined(HAVE_AESGCM) && !defined(SWDEV_AES_ONLYECB)
|
|
static int swdev_aes_gcm(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.enc ? info->cipher.aesgcm_enc.aes :
|
|
info->cipher.aesgcm_dec.aes;
|
|
Aes shadow;
|
|
int ret;
|
|
|
|
if (aes == NULL || aes->keylen <= 0 || aes->keylen > (int)sizeof(aes->devKey))
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_AesInit(&shadow, aes->heap, INVALID_DEVID);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = wc_AesGcmSetKey(&shadow, (const byte*)aes->devKey, (word32)aes->keylen);
|
|
if (ret != 0) {
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
|
|
if (info->cipher.enc) {
|
|
ret = wc_AesGcmEncrypt(&shadow,
|
|
info->cipher.aesgcm_enc.out, info->cipher.aesgcm_enc.in,
|
|
info->cipher.aesgcm_enc.sz,
|
|
info->cipher.aesgcm_enc.iv, info->cipher.aesgcm_enc.ivSz,
|
|
info->cipher.aesgcm_enc.authTag,
|
|
info->cipher.aesgcm_enc.authTagSz,
|
|
info->cipher.aesgcm_enc.authIn,
|
|
info->cipher.aesgcm_enc.authInSz);
|
|
}
|
|
else {
|
|
ret = wc_AesGcmDecrypt(&shadow, info->cipher.aesgcm_dec.out,
|
|
info->cipher.aesgcm_dec.in, info->cipher.aesgcm_dec.sz,
|
|
info->cipher.aesgcm_dec.iv, info->cipher.aesgcm_dec.ivSz,
|
|
info->cipher.aesgcm_dec.authTag,
|
|
info->cipher.aesgcm_dec.authTagSz,
|
|
info->cipher.aesgcm_dec.authIn,
|
|
info->cipher.aesgcm_dec.authInSz);
|
|
}
|
|
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_AESGCM && !SWDEV_AES_ONLYECB */
|
|
|
|
#ifdef HAVE_AESCCM
|
|
static int swdev_aes_ccm(wc_CryptoInfo* info)
|
|
{
|
|
Aes* aes = info->cipher.enc ? info->cipher.aesccm_enc.aes :
|
|
info->cipher.aesccm_dec.aes;
|
|
Aes shadow;
|
|
int ret;
|
|
|
|
if (aes == NULL || aes->keylen <= 0 || aes->keylen > (int)sizeof(aes->devKey))
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = wc_AesInit(&shadow, aes->heap, INVALID_DEVID);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
ret = wc_AesCcmSetKey(&shadow, (const byte*)aes->devKey, (word32)aes->keylen);
|
|
if (ret != 0) {
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
|
|
if (info->cipher.enc) {
|
|
ret = wc_AesCcmEncrypt(&shadow,
|
|
info->cipher.aesccm_enc.out, info->cipher.aesccm_enc.in,
|
|
info->cipher.aesccm_enc.sz,
|
|
info->cipher.aesccm_enc.nonce,
|
|
info->cipher.aesccm_enc.nonceSz,
|
|
info->cipher.aesccm_enc.authTag,
|
|
info->cipher.aesccm_enc.authTagSz,
|
|
info->cipher.aesccm_enc.authIn,
|
|
info->cipher.aesccm_enc.authInSz);
|
|
}
|
|
#ifdef HAVE_AES_DECRYPT
|
|
else {
|
|
ret = wc_AesCcmDecrypt(&shadow, info->cipher.aesccm_dec.out,
|
|
info->cipher.aesccm_dec.in, info->cipher.aesccm_dec.sz,
|
|
info->cipher.aesccm_dec.nonce,
|
|
info->cipher.aesccm_dec.nonceSz,
|
|
info->cipher.aesccm_dec.authTag,
|
|
info->cipher.aesccm_dec.authTagSz,
|
|
info->cipher.aesccm_dec.authIn,
|
|
info->cipher.aesccm_dec.authInSz);
|
|
}
|
|
#else
|
|
else {
|
|
ret = CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
#endif
|
|
|
|
wc_AesFree(&shadow);
|
|
return ret;
|
|
}
|
|
#endif /* HAVE_AESCCM */
|
|
#endif /* !NO_AES */
|
|
|
|
WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info,
|
|
void* ctx)
|
|
{
|
|
int ret;
|
|
|
|
(void)devId;
|
|
(void)ctx;
|
|
|
|
if (info == NULL)
|
|
return BAD_FUNC_ARG;
|
|
|
|
ret = swdev_ensure_init();
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
switch (info->algo_type) {
|
|
#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
|
|
defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) || \
|
|
defined(HAVE_ED448) || defined(WOLFSSL_HAVE_SLHDSA) || \
|
|
defined(HAVE_FALCON) || \
|
|
defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_HAVE_FRODOKEM) || \
|
|
defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
|
|
#if defined(WOLF_CRYPTO_CB_FREE) && \
|
|
(defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS))
|
|
case WC_ALGO_TYPE_FREE:
|
|
if ((info->free.algo == WC_ALGO_TYPE_PK) &&
|
|
(info->free.type == WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN)) {
|
|
return swdev_stateful_free_cb(info);
|
|
}
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
#endif
|
|
case WC_ALGO_TYPE_PK:
|
|
switch (info->pk.type) {
|
|
#ifndef NO_RSA
|
|
case WC_PK_TYPE_RSA:
|
|
return swdev_rsa(info);
|
|
#ifdef WOLFSSL_KEY_GEN
|
|
case WC_PK_TYPE_RSA_KEYGEN:
|
|
return swdev_rsa_keygen(info);
|
|
#endif
|
|
#endif /* !NO_RSA */
|
|
#ifdef HAVE_ECC
|
|
case WC_PK_TYPE_EC_KEYGEN:
|
|
return swdev_ecc_keygen(info);
|
|
case WC_PK_TYPE_ECDH:
|
|
return swdev_ecdh(info);
|
|
case WC_PK_TYPE_ECDSA_SIGN:
|
|
return swdev_ecc_sign(info);
|
|
case WC_PK_TYPE_ECDSA_VERIFY:
|
|
return swdev_ecc_verify(info);
|
|
case WC_PK_TYPE_EC_GET_SIZE:
|
|
return swdev_ecc_get_size(info);
|
|
case WC_PK_TYPE_EC_GET_SIG_SIZE:
|
|
return swdev_ecc_get_sig_size(info);
|
|
case WC_PK_TYPE_EC_MAKE_PUB:
|
|
return swdev_ecc_make_pub(info);
|
|
#ifdef HAVE_ECC_CHECK_KEY
|
|
case WC_PK_TYPE_EC_CHECK_PUB_KEY:
|
|
return swdev_ecc_check_pub(info);
|
|
#endif
|
|
#endif /* HAVE_ECC */
|
|
#ifdef HAVE_ED25519
|
|
#ifdef HAVE_ED25519_MAKE_KEY
|
|
case WC_PK_TYPE_ED25519_KEYGEN:
|
|
return swdev_ed25519_keygen(info);
|
|
#endif
|
|
#ifdef HAVE_ED25519_SIGN
|
|
case WC_PK_TYPE_ED25519_SIGN:
|
|
return swdev_ed25519_sign(info);
|
|
#endif
|
|
#ifdef HAVE_ED25519_VERIFY
|
|
case WC_PK_TYPE_ED25519_VERIFY:
|
|
return swdev_ed25519_verify(info);
|
|
#endif
|
|
#ifdef HAVE_ED25519_MAKE_KEY
|
|
case WC_PK_TYPE_ED25519_MAKE_PUB:
|
|
return swdev_ed25519_make_pub(info);
|
|
#endif
|
|
case WC_PK_TYPE_ED25519_CHECK_KEY:
|
|
return swdev_ed25519_check_key(info);
|
|
#endif /* HAVE_ED25519 */
|
|
#ifdef HAVE_CURVE25519
|
|
case WC_PK_TYPE_CURVE25519_KEYGEN:
|
|
return swdev_curve25519_keygen(info);
|
|
#ifdef HAVE_CURVE25519_SHARED_SECRET
|
|
case WC_PK_TYPE_CURVE25519:
|
|
return swdev_curve25519(info);
|
|
#endif
|
|
case WC_PK_TYPE_CURVE25519_MAKE_PUB:
|
|
return swdev_curve25519_make_pub(info);
|
|
case WC_PK_TYPE_CURVE25519_GENERIC:
|
|
return swdev_curve25519_generic(info);
|
|
#endif /* HAVE_CURVE25519 */
|
|
#ifdef HAVE_CURVE448
|
|
case WC_PK_TYPE_CURVE448_KEYGEN:
|
|
return swdev_curve448_keygen(info);
|
|
#ifdef HAVE_CURVE448_SHARED_SECRET
|
|
case WC_PK_TYPE_CURVE448:
|
|
return swdev_curve448(info);
|
|
#endif
|
|
case WC_PK_TYPE_CURVE448_MAKE_PUB:
|
|
return swdev_curve448_make_pub(info);
|
|
case WC_PK_TYPE_CURVE448_GENERIC:
|
|
return swdev_curve448_generic(info);
|
|
#endif /* HAVE_CURVE448 */
|
|
#ifdef HAVE_ED448
|
|
#ifdef HAVE_ED448_SIGN
|
|
case WC_PK_TYPE_ED448:
|
|
return swdev_ed448_sign(info);
|
|
#endif
|
|
#ifdef HAVE_ED448_VERIFY
|
|
case WC_PK_TYPE_ED448_VERIFY:
|
|
return swdev_ed448_verify(info);
|
|
#endif
|
|
case WC_PK_TYPE_ED448_MAKE_PUB:
|
|
return swdev_ed448_make_pub(info);
|
|
case WC_PK_TYPE_ED448_CHECK_KEY:
|
|
return swdev_ed448_check_key(info);
|
|
#endif /* HAVE_ED448 */
|
|
#if defined(WOLFSSL_HAVE_SLHDSA) || defined(HAVE_FALCON)
|
|
case WC_PK_TYPE_PQC_SIG_KEYGEN:
|
|
case WC_PK_TYPE_PQC_SIG_KEYGEN_SEED:
|
|
return swdev_pqc_sig(info, info->pk.pqc_sig_kg.type,
|
|
info->pk.type);
|
|
case WC_PK_TYPE_PQC_SIG_SIGN:
|
|
case WC_PK_TYPE_PQC_SIG_SIGN_MSG:
|
|
return swdev_pqc_sig(info, info->pk.pqc_sign.type, info->pk.type);
|
|
case WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY:
|
|
return swdev_pqc_sig(info, info->pk.pqc_sig_check.type,
|
|
info->pk.type);
|
|
case WC_PK_TYPE_PQC_SIG_VERIFY:
|
|
case WC_PK_TYPE_PQC_SIG_VERIFY_MSG:
|
|
return swdev_pqc_sig(info, info->pk.pqc_verify.type, info->pk.type);
|
|
#endif /* WOLFSSL_HAVE_SLHDSA || HAVE_FALCON */
|
|
#if defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_HAVE_FRODOKEM)
|
|
case WC_PK_TYPE_PQC_KEM_KEYGEN:
|
|
return swdev_pqc_kem(info, info->pk.pqc_kem_kg.type,
|
|
info->pk.type);
|
|
case WC_PK_TYPE_PQC_KEM_ENCAPS:
|
|
return swdev_pqc_kem(info, info->pk.pqc_encaps.type,
|
|
info->pk.type);
|
|
case WC_PK_TYPE_PQC_KEM_DECAPS:
|
|
return swdev_pqc_kem(info, info->pk.pqc_decaps.type,
|
|
info->pk.type);
|
|
#endif /* WOLFSSL_HAVE_MLKEM || WOLFSSL_HAVE_FRODOKEM */
|
|
#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_KEYGEN:
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGN:
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_VERIFY:
|
|
case WC_PK_TYPE_PQC_STATEFUL_SIG_SIGS_LEFT:
|
|
return swdev_pqc_stateful_sig(info, info->pk.type);
|
|
#endif
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
#endif
|
|
#if !defined(NO_SHA256) || defined(WOLFSSL_SHA512) || \
|
|
defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHAKE128) || \
|
|
defined(WOLFSSL_SHAKE256)
|
|
case WC_ALGO_TYPE_HASH:
|
|
switch (info->hash.type) {
|
|
#ifndef NO_SHA256
|
|
case WC_HASH_TYPE_SHA256:
|
|
return swdev_sha256(info);
|
|
#endif
|
|
#ifdef WOLFSSL_SHA224
|
|
case WC_HASH_TYPE_SHA224:
|
|
return swdev_sha224(info);
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
case WC_HASH_TYPE_SHA512:
|
|
return swdev_sha512(info);
|
|
#if !defined(WOLFSSL_NOSHA512_224) && \
|
|
!defined(WOLFSSL_SWDEV_SHA512_GENERAL_ONLY)
|
|
case WC_HASH_TYPE_SHA512_224:
|
|
return swdev_sha512_224(info);
|
|
#endif
|
|
#if !defined(WOLFSSL_NOSHA512_256) && \
|
|
!defined(WOLFSSL_SWDEV_SHA512_GENERAL_ONLY)
|
|
case WC_HASH_TYPE_SHA512_256:
|
|
return swdev_sha512_256(info);
|
|
#endif
|
|
#endif
|
|
#if defined(WOLFSSL_SHA384) && \
|
|
!defined(WOLFSSL_SWDEV_SHA512_GENERAL_ONLY)
|
|
case WC_HASH_TYPE_SHA384:
|
|
return swdev_sha384(info);
|
|
#endif
|
|
#if defined(WOLFSSL_SHAKE128) || defined(WOLFSSL_SHAKE256)
|
|
#ifdef WOLFSSL_SHAKE128
|
|
case WC_HASH_TYPE_SHAKE128:
|
|
#endif
|
|
#ifdef WOLFSSL_SHAKE256
|
|
case WC_HASH_TYPE_SHAKE256:
|
|
#endif
|
|
return swdev_shake(info);
|
|
#endif
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
#endif
|
|
#ifndef NO_AES
|
|
case WC_ALGO_TYPE_CIPHER:
|
|
switch (info->cipher.type) {
|
|
#ifdef HAVE_AES_CBC
|
|
case WC_CIPHER_AES_CBC:
|
|
return swdev_aes_cbc(info);
|
|
#endif
|
|
#ifdef WOLFSSL_AES_COUNTER
|
|
case WC_CIPHER_AES_CTR:
|
|
return swdev_aes_ctr(info);
|
|
#endif
|
|
#ifdef WOLFSSL_AES_CFB
|
|
case WC_CIPHER_AES_CFB:
|
|
return swdev_aes_cfb(info);
|
|
#endif
|
|
#ifdef WOLFSSL_AES_OFB
|
|
case WC_CIPHER_AES_OFB:
|
|
return swdev_aes_ofb(info);
|
|
#endif
|
|
#if defined(HAVE_AES_ECB) || defined(WOLFSSL_AES_DIRECT)
|
|
case WC_CIPHER_AES_ECB:
|
|
return swdev_aes_ecb(info);
|
|
#endif
|
|
#if defined(HAVE_AESGCM) && !defined(SWDEV_AES_ONLYECB)
|
|
case WC_CIPHER_AES_GCM:
|
|
return swdev_aes_gcm(info);
|
|
#endif
|
|
#ifdef HAVE_AESCCM
|
|
case WC_CIPHER_AES_CCM:
|
|
return swdev_aes_ccm(info);
|
|
#endif
|
|
#if defined(HAVE_AES_KEYWRAP) && !defined(SWDEV_AES_ONLYECB)
|
|
case WC_CIPHER_AES_KEYWRAP:
|
|
return swdev_aes_keywrap(info);
|
|
#endif
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
#endif /* !NO_AES */
|
|
default:
|
|
return CRYPTOCB_UNAVAILABLE;
|
|
}
|
|
}
|