Peer review fixes

Address Skoll review findings on the SealSQ VaultIC port:

- CMake: compile vaultic.c (BUILD_VAULTIC flag + LIB_SOURCES) and stop
  excluding vaultic.h from install when the option is on (HIGH-1).
- Crypto callback EC keygen: resolve the curve via key->dp instead of the
  ECC_CURVE_DEF that wc_ecc_make_key() forwards, so P-256 keygen offloads to
  the device instead of silently falling back to software (HIGH-2).
- EccSharedSecretCb: test the 1.3 versions explicitly instead of a "< 1.3"
  ordering compare, which was false for DTLS and skipped client keygen (HIGH-3).
- LoadCertificates: reject cert sizes <= 0 and bound them with
  VAULTIC_MAX_CERT_SZ before XMALLOC (HIGH-4); load the device cert before the
  CA into the trust store; use WOLFSSL_FATAL_ERROR (not WC_HW_E) for cert
  parse/load failures (LOW-14/16).
- Guard the caller's output buffer size (BUFFER_E) before writing the ECDH
  shared secret in both the PK and crypto callbacks (MEDIUM-6).
- vaultic.h: guard the ECDH declarations with VLT_TLS_NO_ECDH to match
  vaultic.c and rename otherKey to otherPubKey (MEDIUM-7).
- configure.ac: parse --enable-vaultic before the crypto-callback aggregation
  so CRYPTOCB/PKCALLBACKS are promoted ahead of the dependent checks (MEDIUM-9).
- Add NULL/dp guards to the PK callbacks (LOW-15).
- Register WOLFSSL_VAULTIC_DEBUG in .wolfssl_known_macro_extras and document
  it, the single-device-key routing, and the one-handshake concurrency limit
  in the README (LOW-10, MEDIUM-5, MEDIUM-8).
pull/10974/head
David Garske 2026-08-04 17:56:08 -07:00
parent fdc8fbc078
commit d84179b3c6
7 changed files with 142 additions and 51 deletions

View File

@ -1059,6 +1059,7 @@ WOLFSSL_USE_FORCE_ZERO
WOLFSSL_USE_OPTIONS_H
WOLFSSL_VA416X0_TRNG
WOLFSSL_VALIDATE_DH_KEYGEN
WOLFSSL_VAULTIC_DEBUG
WOLFSSL_VERSAL_GEN2_ASU
WOLFSSL_VERSAL_GEN2_ASU_RTC
WOLFSSL_WC_SLHDSA_RECURSIVE

View File

@ -4574,7 +4574,6 @@ set(HEADER_EXCLUDE
"wolfssl/wolfcrypt/port/cypress"
"wolfssl/wolfcrypt/port/Espressif"
"wolfssl/wolfcrypt/port/iotsafe"
"wolfssl/wolfcrypt/port/sealsq"
"wolfssl/wolfcrypt/port/nxp"
"wolfssl/wolfcrypt/port/pic"
"wolfssl/wolfcrypt/port/Renesas"
@ -4598,6 +4597,11 @@ if(NOT BUILD_CRYPTOAUTHLIB)
"wolfssl/wolfcrypt/port/atmel")
endif()
if(NOT BUILD_VAULTIC)
list(APPEND HEADER_EXCLUDE
"wolfssl/wolfcrypt/port/sealsq")
endif()
if(NOT BUILD_AFALG)
list(APPEND HEADER_EXCLUDE
"wolfssl/wolfcrypt/port/af_alg")

View File

@ -365,6 +365,9 @@ function(generate_build_flags)
if(WOLFSSL_CRYPTOCB OR WOLFSSL_USER_SETTINGS)
set(BUILD_CRYPTOCB "yes" PARENT_SCOPE)
endif()
if(WOLFSSL_VAULTIC)
set(BUILD_VAULTIC "yes" PARENT_SCOPE)
endif()
set(BUILD_PSK ${WOLFSSL_PSK} PARENT_SCOPE)
set(BUILD_TRUST_PEER_CERT ${WOLFSSL_TRUSTED_PEER_CERT} PARENT_SCOPE)
set(BUILD_PKI ${WOLFSSL_PKI} PARENT_SCOPE)
@ -1322,6 +1325,10 @@ function(generate_lib_src_list LIB_SOURCES)
list(APPEND LIB_SOURCES wolfcrypt/src/port/atmel/atmel.c)
endif()
if(BUILD_VAULTIC)
list(APPEND LIB_SOURCES wolfcrypt/src/port/sealsq/vaultic.c)
endif()
if(BUILD_CAAM)
list(APPEND LIB_SOURCES
wolfcrypt/src/port/caam/wolfcaam_init.c

View File

@ -11321,7 +11321,29 @@ AC_ARG_ENABLE([cryptocb-sw-test],
[ ENABLED_CRYPTOCB_SW_TEST=yes ]
)
if test "x$ENABLED_PKCS11" = "xyes" || test "x$ENABLED_WOLFTPM" = "xyes" || test "$ENABLED_CAAM" != "no" || test "x$ENABLED_RTL8735B" != "xno"
# WISeKey/SealSQ VaultIC secure element. Parsed here, ahead of the crypto
# callback aggregation below, so it can promote PK callbacks and crypto
# callbacks before the checks that depend on them.
AC_ARG_ENABLE([vaultic],
[AS_HELP_STRING([--enable-vaultic],[Enable WISeKey/SealSQ VaultIC secure element support (default: disabled)])],
[ ENABLED_VAULTIC=$enableval ],
[ ENABLED_VAULTIC=no ]
)
if test "$ENABLED_VAULTIC" != "no"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_VAULTIC"
ENABLED_VAULTIC=yes
# VaultIC port supports TLS PK callbacks and wolfCrypt crypto callbacks.
# CRYPTOCB is promoted through the aggregation below; promote PK callbacks
# here.
if test "$ENABLED_PKCALLBACKS" = "no"; then
ENABLED_PKCALLBACKS=yes
AM_CFLAGS="$AM_CFLAGS -DHAVE_PK_CALLBACKS"
fi
fi
if test "x$ENABLED_PKCS11" = "xyes" || test "x$ENABLED_WOLFTPM" = "xyes" || test "$ENABLED_CAAM" != "no" || test "x$ENABLED_RTL8735B" != "xno" || test "x$ENABLED_VAULTIC" = "xyes"
then
ENABLED_CRYPTOCB=yes
fi
@ -11801,13 +11823,6 @@ AC_ARG_ENABLE([iotsafe-hwrng],
[ ENABLED_IOTSAFE_HWRNG=no ]
)
# WISeKey/SealSQ VaultIC secure element
AC_ARG_ENABLE([vaultic],
[AS_HELP_STRING([--enable-vaultic],[Enable WISeKey/SealSQ VaultIC secure element support (default: disabled)])],
[ ENABLED_VAULTIC=$enableval ],
[ ENABLED_VAULTIC=no ]
)
# Make clean
AC_ARG_ENABLE([makeclean],
[AS_HELP_STRING([--enable-makeclean], [Enables forced "make clean" at the
@ -12611,21 +12626,6 @@ then
ENABLED_IOTSAFE_HWRNG=yes
fi
if test "$ENABLED_VAULTIC" != "no"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_VAULTIC"
ENABLED_VAULTIC=yes
# VaultIC port supports TLS PK callbacks and wolfCrypt crypto callbacks
if test "$ENABLED_PKCALLBACKS" = "no"; then
ENABLED_PKCALLBACKS=yes
AM_CFLAGS="$AM_CFLAGS -DHAVE_PK_CALLBACKS"
fi
if test "$ENABLED_CRYPTOCB" = "no"; then
ENABLED_CRYPTOCB=yes
AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB"
fi
fi
if test "x$ENABLED_WOLFENGINE" = "xyes"
then
AM_CFLAGS="$AM_CFLAGS -DHAVE_AES_ECB"

View File

@ -76,8 +76,11 @@ WOLFSSL_CTX* ctx = wolfSSL_CTX_new(wolfTLS_client_method());
/* register the VaultIC ECC PK callbacks */
WOLFSSL_VAULTIC_SetupPkCallbacks(ctx);
/* load the device + CA certificates stored on the chip */
WOLFSSL_VAULTIC_LoadCertificates(ctx);
/* load the device + CA certificates stored on the chip. Check the return
* value: on failure the CTX may be left partially configured. */
if (WOLFSSL_VAULTIC_LoadCertificates(ctx) != 0) {
/* handle error */
}
WOLFSSL* ssl = wolfSSL_new(ctx);
WOLFSSL_VAULTIC_SetupPkCallbackCtx(ssl, NULL);
@ -105,6 +108,17 @@ The VaultIC-TLS SDK must be initialized (`vlt_tls_init()`) before use and
closed (`vlt_tls_close()`) afterwards; see the SealSQ devkit sample
applications.
**Warning - single device key:** the crypto callback always operates on the
single provisioned on-chip key; it does not consult the private scalar of the
key passed by the caller. `wolfSSL_CTX_SetDevId(ctx, WOLF_VAULTIC_DEVID)` sets
that devId on *every* P-256 key created from the CTX, so all P-256 private-key
operations (sign and ECDH) are routed to the on-chip key regardless of any
software private key the application also loads (for example via
`wolfSSL_CTX_use_PrivateKey_file()`). Do not mix a software private key with the
device devId on the same CTX: the handshake would be signed with the device key
while presenting the software certificate. Use the devId path only when the
device key is the intended identity.
### Curve support
The port offloads P-256 (SECP256R1) only. The VaultIC 408 silicon supports
@ -113,6 +127,28 @@ P-384 would require vendor `vlt_tls_*_P384` functions. For any other curve
the crypto callback returns `CRYPTOCB_UNAVAILABLE` and the PK callbacks
return `NOT_COMPILED_IN`, so wolfSSL falls back to software.
## Concurrency
The port is otherwise stateless, but the VaultIC has a single on-chip ephemeral
key slot. `vlt_tls_keygen_P256()` generates into that one slot and
`vlt_tls_compute_shared_secret_P256()` derives from whatever is currently in it,
and the port keeps no per-session handle and takes no lock. Only one handshake
that uses the ECDH/keygen path may be in flight at a time. Two `WOLFSSL` objects
handshaking concurrently on the same `WOLFSSL_CTX` (for example a threaded
server, or a client opening several connections from multiple threads) can
interleave keygen and shared-secret calls, so one session may derive its
premaster secret from another session's ephemeral key. A TLS 1.3
HelloRetryRequest also forces a second keygen mid-handshake. Serialize
handshakes that touch the device (one at a time) if the application is
multi-threaded.
## Debugging
Define `WOLFSSL_VAULTIC_DEBUG` to dump the device and CA certificates read off
the chip in `WOLFSSL_VAULTIC_LoadCertificates`. This depends on wolfSSL's debug
logging: the dump uses `WOLFSSL_BUFFER()`, which expands to nothing unless
`DEBUG_WOLFSSL` is also defined (autotools `--enable-debug`). Define both.
## Building and provisioning with the SealSQ devkit
The SealSQ VaultIC-TLS devkit (for example `DEVKIT_VIC408_TLS_RPI`) drives the

View File

@ -179,7 +179,7 @@ int WOLFSSL_VAULTIC_EccVerifyCb(WOLFSSL* ssl,
}
/* Check requested curve */
if (key.dp->id != ECC_SECP256R1) {
if (key.dp == NULL || key.dp->id != ECC_SECP256R1) {
WOLFSSL_MSG("id != ECC_SECP256R1");
wc_ecc_free(&key);
return NOT_COMPILED_IN;
@ -236,6 +236,7 @@ int WOLFSSL_VAULTIC_EccSharedSecretCb(WOLFSSL* ssl, ecc_key* otherPubKey,
int side, void* ctx)
{
int err;
int ver;
byte otherPubKeyX[P256_BYTE_SZ] = {0};
byte otherPubKeyY[P256_BYTE_SZ] = {0};
word32 otherPubKeyX_len = sizeof(otherPubKeyX);
@ -250,11 +251,18 @@ int WOLFSSL_VAULTIC_EccSharedSecretCb(WOLFSSL* ssl, ecc_key* otherPubKey,
WOLFSSL_MSG("WOLFSSL_VAULTIC_EccSharedSecretCb");
/* check requested curve */
if (otherPubKey->dp->id != ECC_SECP256R1) {
if (otherPubKey == NULL || otherPubKey->dp == NULL ||
otherPubKey->dp->id != ECC_SECP256R1) {
WOLFSSL_MSG("id != ECC_SECP256R1");
return NOT_COMPILED_IN;
}
/* the device writes P256_BYTE_SZ bytes of shared secret */
if (out == NULL || outlen == NULL || *outlen < P256_BYTE_SZ) {
WOLFSSL_MSG("shared secret output buffer too small");
return BUFFER_E;
}
/* for client: create and export public key */
if (side == WOLFSSL_CLIENT_END) {
@ -270,9 +278,12 @@ int WOLFSSL_VAULTIC_EccSharedSecretCb(WOLFSSL* ssl, ecc_key* otherPubKey,
vlt_tls_left_pad_P256(otherPubKeyY, otherPubKeyY_len);
/* TLS v1.2 and older we must generate a key here for the client only.
* TLS v1.3 calls key gen early with key share */
if (wolfSSL_GetVersion(ssl) < WOLFSSL_TLSV1_3) {
/* TLS v1.2 and older (and their DTLS equivalents) must generate a key
* here for the client only. TLS/DTLS v1.3 call key gen early with the
* key share. The version enum is not ordered (DTLS values sort after
* WOLFSSL_TLSV1_3), so test the 1.3 versions explicitly. */
ver = wolfSSL_GetVersion(ssl);
if (ver != WOLFSSL_TLSV1_3 && ver != WOLFSSL_DTLSV1_3) {
if (vlt_tls_keygen_P256(pubKeyX, pubKeyY) != 0) {
WOLFSSL_MSG("vlt_tls_keygen_P256");
@ -352,7 +363,7 @@ int WOLFSSL_VAULTIC_EccSharedSecretCb(WOLFSSL* ssl, ecc_key* otherPubKey,
*/
int WOLFSSL_VAULTIC_LoadCertificates(WOLFSSL_CTX* ctx)
{
int ret = -1;
int ret = WC_HW_E;
/* CA certificate */
unsigned char *ca_cert = NULL;
@ -365,10 +376,10 @@ int WOLFSSL_VAULTIC_LoadCertificates(WOLFSSL_CTX* ctx)
/* Read Device certificate in VaultIC */
WOLFSSL_MSG("Read Device Certificate in VaultIC");
if ((sizeof_device_cert = vlt_tls_get_cert_size(SSL_VIC_DEVICE_CERT))
== -1) {
WOLFSSL_MSG("No Device Certificate found in VaultIC");
return -1;
sizeof_device_cert = vlt_tls_get_cert_size(SSL_VIC_DEVICE_CERT);
if (sizeof_device_cert <= 0 || sizeof_device_cert > VAULTIC_MAX_CERT_SZ) {
WOLFSSL_MSG("No valid Device Certificate found in VaultIC");
return WC_HW_E;
}
device_cert = (unsigned char*)XMALLOC(sizeof_device_cert, NULL,
@ -390,8 +401,9 @@ int WOLFSSL_VAULTIC_LoadCertificates(WOLFSSL_CTX* ctx)
/* Read CA certificate in VaultIC */
WOLFSSL_MSG("Read CA Certificate in VaultIC");
if ((sizeof_ca_cert = vlt_tls_get_cert_size(SSL_VIC_CA_CERT)) == -1) {
WOLFSSL_MSG("No CA Certificate found in VaultIC");
sizeof_ca_cert = vlt_tls_get_cert_size(SSL_VIC_CA_CERT);
if (sizeof_ca_cert <= 0 || sizeof_ca_cert > VAULTIC_MAX_CERT_SZ) {
WOLFSSL_MSG("No valid CA Certificate found in VaultIC");
goto free_cert_buffers;
}
@ -412,19 +424,23 @@ int WOLFSSL_VAULTIC_LoadCertificates(WOLFSSL_CTX* ctx)
WOLFSSL_BUFFER(ca_cert, sizeof_ca_cert);
#endif
/* Load CA certificate into WOLFSSL_CTX */
if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert,
sizeof_ca_cert, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("failed to load CA certificate");
ret = WC_HW_E;
goto free_cert_buffers;
}
/* Load Device certificate into WOLFSSL_CTX */
/* Load the Device certificate into the WOLFSSL_CTX first, then the CA into
* the trust store, so the trust anchor is only added once the device
* certificate has parsed successfully. A parse/load failure here is not a
* hardware error (the bytes were read off the chip), so report it that
* way. */
if (wolfSSL_CTX_use_certificate_buffer(ctx, device_cert,
sizeof_device_cert, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("failed to load Device certificate");
ret = WC_HW_E;
ret = WOLFSSL_FATAL_ERROR;
goto free_cert_buffers;
}
/* Load CA certificate into WOLFSSL_CTX trust store */
if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert,
sizeof_ca_cert, WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("failed to load CA certificate");
ret = WOLFSSL_FATAL_ERROR;
goto free_cert_buffers;
}
@ -537,7 +553,16 @@ int WOLFSSL_VAULTIC_CryptoCb(int devId, wc_CryptoInfo* info, void* ctx)
WOLFSSL_MSG("WOLFSSL_VAULTIC_CryptoCb: ECDH");
if (info->pk.ecdh.private_key == NULL ||
info->pk.ecdh.private_key->dp == NULL ||
info->pk.ecdh.private_key->dp->id != ECC_SECP256R1) {
info->pk.ecdh.private_key->dp->id != ECC_SECP256R1 ||
info->pk.ecdh.public_key == NULL ||
info->pk.ecdh.public_key->dp == NULL ||
info->pk.ecdh.public_key->dp->id != ECC_SECP256R1) {
break;
}
if (info->pk.ecdh.out == NULL || info->pk.ecdh.outlen == NULL ||
*info->pk.ecdh.outlen < P256_BYTE_SZ) {
WOLFSSL_MSG("shared secret output buffer too small");
rc = BUFFER_E;
break;
}
if ((rc = wc_ecc_export_public_raw(info->pk.ecdh.public_key,
@ -564,7 +589,13 @@ int WOLFSSL_VAULTIC_CryptoCb(int devId, wc_CryptoInfo* info, void* ctx)
byte pubKeyY[P256_BYTE_SZ] = {0};
WOLFSSL_MSG("WOLFSSL_VAULTIC_CryptoCb: EC keygen");
if (info->pk.eckg.curveId != ECC_SECP256R1) {
/* wc_ecc_make_key() forwards ECC_CURVE_DEF (0), not the concrete
* curve id, so resolve via key->dp (wc_ecc_set_curve() ran before
* the callback). Otherwise P-256 keygen would silently fall back
* to software. */
if (info->pk.eckg.key == NULL ||
info->pk.eckg.key->dp == NULL ||
info->pk.eckg.key->dp->id != ECC_SECP256R1) {
break;
}
if (vlt_tls_keygen_P256(pubKeyX, pubKeyY) != 0) {

View File

@ -38,9 +38,19 @@
extern "C" {
#endif
/* Upper bound on a certificate read off the VaultIC. Guards the XMALLOC in
* WOLFSSL_VAULTIC_LoadCertificates against a bogus/hostile size returned by the
* device or transport. Override at build time if the provisioned certificates
* are larger. */
#ifndef VAULTIC_MAX_CERT_SZ
#define VAULTIC_MAX_CERT_SZ 4096
#endif
#ifdef HAVE_PK_CALLBACKS
#ifndef VLT_TLS_NO_ECDH
WOLFSSL_API int WOLFSSL_VAULTIC_EccKeyGenCb(WOLFSSL* ssl, ecc_key* key,
word32 keySz, int ecc_curve, void* ctx);
#endif /* VLT_TLS_NO_ECDH */
WOLFSSL_API int WOLFSSL_VAULTIC_EccVerifyCb(WOLFSSL* ssl,
const unsigned char* sig, unsigned int sigSz,
@ -53,11 +63,13 @@ WOLFSSL_API int WOLFSSL_VAULTIC_EccSignCb(WOLFSSL* ssl,
byte* out, word32* outSz,
const byte* key, word32 keySz, void* ctx);
#ifndef VLT_TLS_NO_ECDH
WOLFSSL_API int WOLFSSL_VAULTIC_EccSharedSecretCb(WOLFSSL* ssl,
ecc_key* otherKey,
ecc_key* otherPubKey,
unsigned char* pubKeyDer, unsigned int* pubKeySz,
unsigned char* out, unsigned int* outlen,
int side, void* ctx);
#endif /* VLT_TLS_NO_ECDH */
#endif /* HAVE_PK_CALLBACKS */
WOLFSSL_API int WOLFSSL_VAULTIC_LoadCertificates(WOLFSSL_CTX* ctx);