diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 3c3879c33fa..a9e0a3e0b08 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -688,6 +688,7 @@ USE_TLSV13 USE_WINDOWS_API USE_WOLF_STRNSTR USS_API +VLT_TLS_NO_ECDH W64LIT_FORCE_LONG_LONG WC_AESXTS_STREAM_NO_REQUEST_ACCOUNTING WC_AES_BS_WORD_SIZE diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e9a60d93b8..6bc7cc87625 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -521,6 +521,16 @@ if(WOLFSSL_IOTSAFE) list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_IOTSAFE") endif() +add_option("WOLFSSL_VAULTIC" "Enable WISeKey/SealSQ VaultIC support (default: disabled)" "no" "yes;no") +if(WOLFSSL_VAULTIC) + list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_VAULTIC") + # VaultIC port supports PK callbacks and crypto callbacks (mirror autotools + # --enable-vaultic). These options are declared later, so force them before + # their add_option() calls. + force_option(WOLFSSL_PKCALLBACKS "yes") + force_option(WOLFSSL_CRYPTOCB "yes") +endif() + add_option("WOLFSSL_WOLFSENTRY" "Enable wolfSentry support (default: disabled)" "no" "yes;no") if(WOLFSSL_WOLFSENTRY) foreach(_o WOLFSSL_MD5) @@ -4583,6 +4593,7 @@ 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" diff --git a/configure.ac b/configure.ac index 6116178b059..0f210ef0795 100644 --- a/configure.ac +++ b/configure.ac @@ -11674,6 +11674,13 @@ 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 @@ -12477,6 +12484,21 @@ 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" @@ -13004,6 +13026,7 @@ AM_CONDITIONAL([BUILD_CAAM],[test "x$ENABLED_CAAM" != "xno"]) AM_CONDITIONAL([BUILD_QNXCAAM],[test "x$ENABLED_CAAM_QNX" = "xyes"]) AM_CONDITIONAL([BUILD_IOTSAFE],[test "x$ENABLED_IOTSAFE" = "xyes"]) AM_CONDITIONAL([BUILD_IOTSAFE_HWRNG],[test "x$ENABLED_IOTSAFE_HWRNG" = "xyes"]) +AM_CONDITIONAL([BUILD_VAULTIC],[test "x$ENABLED_VAULTIC" = "xyes"]) AM_CONDITIONAL([BUILD_SE050],[test "x$ENABLED_SE050" = "xyes"]) AM_CONDITIONAL([BUILD_STSAFE],[test "x$ENABLED_STSAFE" != "xno"]) AM_CONDITIONAL([BUILD_RTL8735B],[test "x$ENABLED_RTL8735B" != "xno"]) @@ -13601,6 +13624,7 @@ echo " * PK callbacks: $ENABLED_PKCALLBACKS" echo " * Crypto callbacks: $ENABLED_CRYPTOCB" echo " * i.MX CAAM: $ENABLED_CAAM" echo " * IoT-Safe: $ENABLED_IOTSAFE" +echo " * WISeKey/SealSQ VaultIC: $ENABLED_VAULTIC" echo " * IoT-Safe HWRNG: $ENABLED_IOTSAFE_HWRNG" echo " * NXP SE050: $ENABLED_SE050" echo " * STMicro STSAFE: $ENABLED_STSAFE" diff --git a/wolfcrypt/src/include.am b/wolfcrypt/src/include.am index 5125712417e..1d1b4a293ff 100644 --- a/wolfcrypt/src/include.am +++ b/wolfcrypt/src/include.am @@ -238,6 +238,11 @@ if BUILD_IOTSAFE src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/iotsafe/iotsafe.c endif +if BUILD_VAULTIC +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/sealsq/vaultic.c +endif +EXTRA_DIST += wolfcrypt/src/port/sealsq/README.md + if BUILD_CAAM src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/caam/wolfcaam_init.c diff --git a/wolfcrypt/src/port/sealsq/README.md b/wolfcrypt/src/port/sealsq/README.md new file mode 100644 index 00000000000..c8078ed3b3d --- /dev/null +++ b/wolfcrypt/src/port/sealsq/README.md @@ -0,0 +1,238 @@ +# WISeKey / SealSQ VaultIC Port + +This port offloads ECC P-256 operations to a WISeKey/SealSQ VaultIC secure +element (for example the VaultIC 408 on the DEVKIT_VIC408). The private key +never leaves the chip. + +It exposes the device through two mechanisms, which can be used independently +or together: + +* **TLS PK callbacks** - route the ECC operations of a TLS handshake + (`WOLFSSL_CTX` / `WOLFSSL`) to the device via wolfSSL's public-key (PK) + callback framework. +* **wolfCrypt crypto callback (devId)** - route wolfCrypt ECC operations (not + just the TLS handshake) to the device via the `WOLF_CRYPTO_CB` / devId + framework, so bare `wc_ecc_*` calls and a whole `WOLFSSL_CTX` can be pointed + at the chip with a single devId. + +Both share the same underlying VaultIC-TLS SDK entry points; see "Usage" for +which one to pick. + +## What it does + +`wolfcrypt/src/port/sealsq/vaultic.c` provides: + +ECC operations on the device (used by both mechanisms): + +* `WOLFSSL_VAULTIC_EccSignCb` - ECDSA P-256 sign on the device +* `WOLFSSL_VAULTIC_EccVerifyCb` - ECDSA P-256 verify on the device +* `WOLFSSL_VAULTIC_EccKeyGenCb` - ephemeral P-256 key generation on the device +* `WOLFSSL_VAULTIC_EccSharedSecretCb` - ECDH P-256 shared secret on the device + +TLS PK callback registration: + +* `WOLFSSL_VAULTIC_SetupPkCallbacks` / `WOLFSSL_VAULTIC_SetupPkCallbackCtx` - + register the PK callbacks on a `WOLFSSL_CTX` / `WOLFSSL` + +Crypto callback (devId) registration: + +* `WOLFSSL_VAULTIC_RegisterCryptoCb` - register the VaultIC crypto callback + under a devId (`WOLF_VAULTIC_DEVID`) + +Certificate helper: + +* `WOLFSSL_VAULTIC_LoadCertificates` - read the device and CA certificates + stored on the chip into a `WOLFSSL_CTX` + +The key-generation and shared-secret (ECDH) callbacks can be compiled out by +defining `VLT_TLS_NO_ECDH`, matching the vendor library configuration. + +## Dependencies + +This port is glue only. It requires the external SealSQ **VaultIC-TLS SDK**, +which provides `vaultic_tls.h` and the `vlt_tls_*` P-256 API that talks to the +chip. That SDK is distributed by SealSQ and is not part of wolfSSL. + +## Building + +Enable the port and point the compiler/linker at the vendor SDK: + +```sh +./configure --enable-vaultic \ + CFLAGS="-I/path/to/VaultIC-TLS/vaultic_tls-4xx/src" \ + LIBS="-lvaultic_tls_408" +make +``` + +`--enable-vaultic` defines `WOLFSSL_VAULTIC` and automatically enables both PK +callbacks (`HAVE_PK_CALLBACKS`) and crypto callbacks (`WOLF_CRYPTO_CB`), so +either mechanism is available out of the box. The port compiles to nothing when +`WOLFSSL_VAULTIC` is not defined. + +### Building the port outside libwolfssl + +Like other wolfSSL port files, `vaultic.c` is meant to be compiled as part of +libwolfssl (via `--enable-vaultic`), where it inherits the wolfSSL +configuration through `config.h`. If instead you compile `vaultic.c` in a +separate target (for example the SealSQ devkit builds it into its own +`vaultic_wolfssl` library alongside an `add_subdirectory(wolfssl)` CMake +build), two things are required so the file sees the wolfSSL configuration: + +* Make the file see the wolfSSL configuration. For an autotools build define + `WOLFSSL_USE_OPTIONS_H`, so `settings.h` pulls in the generated + `wolfssl/options.h`. For a build that does not use `./configure` (so there is + no generated `options.h`), define `WOLFSSL_USER_SETTINGS` instead and provide + a `user_settings.h` that lists all the build options. Without one of these the + wolfSSL headers fall back to defaults (`ecc_key` is incomplete, + `ECC_SECP256R1` is undeclared). +* Enable PK callbacks through the wolfSSL build option + (autotools `--enable-pkcallbacks`, CMake `WOLFSSL_PKCALLBACKS=yes`), not a + bare `-DHAVE_PK_CALLBACKS` compile flag. The generated `options.h` contains + `#undef HAVE_PK_CALLBACKS` for disabled options, which cancels a + command-line define once `options.h` is included. + +## Usage + +The port offers two ways to route ECC to the chip (see the overview at the +top). Use the PK callbacks when you only need the TLS handshake offloaded and +want per-`WOLFSSL` control; use the crypto callback (devId) when you want all +wolfCrypt ECC - including bare `wc_ecc_*` calls - routed to the device, or +prefer a single devId on the `WOLFSSL_CTX`. + +### TLS PK callbacks + +```c +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); + +WOLFSSL* ssl = wolfSSL_new(ctx); +WOLFSSL_VAULTIC_SetupPkCallbackCtx(ssl, NULL); +/* ... proceed with the TLS handshake ... */ +``` + +### Crypto callback (devId) + +The crypto callback dispatches wolfCrypt ECC operations (not just the TLS +handshake) to the VaultIC through the `WOLF_CRYPTO_CB` / devId framework. +Register the device once, then select it with a devId: + +```c +/* register the VaultIC crypto callback under a devId */ +WOLFSSL_VAULTIC_RegisterCryptoCb(WOLF_VAULTIC_DEVID); + +/* TLS: route this CTX's crypto to the device */ +wolfSSL_CTX_SetDevId(ctx, WOLF_VAULTIC_DEVID); + +/* or bare wolfCrypt: init a key against the device */ +wc_ecc_init_ex(&key, NULL, WOLF_VAULTIC_DEVID); +``` + +The VaultIC-TLS SDK must be initialized (`vlt_tls_init()`) before use and +closed (`vlt_tls_close()`) afterwards; see the SealSQ devkit sample +applications. + +### Curve support + +The port offloads P-256 (SECP256R1) only. The VaultIC 408 silicon supports +P-384, but the vendor `vlt_tls` API exposes P-256 entry points only, so +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. + +## Building and provisioning with the SealSQ devkit + +The SealSQ VaultIC-TLS devkit (for example `DEVKIT_VIC408_TLS_RPI`) drives the +whole build/provision/run flow with shell scripts. High-level orientation and +the files that matter: + +* SDK library: `VaultIC-TLS/vaultic_tls/vaultic_tls-4xx/src` (`vaultic_tls.h` + API, `vaultic_tls_config.h`), on top of the low-level chip library + `vaultic_tls/vaultic_elib_408` (its Raspberry Pi I2C/SPI drivers live under + `src/arch/embedded/raspberry/pi3`, e.g. `vaultic_twi_driver.c`). +* Glue build: `vaultic_tls/vaultic_wolfssl/CMakeLists.txt` compiles this + port's `vaultic.c` and links it against `wolfssl` and `vaultic_tls_408`. +* Config: `demos/config.cfg` selects `PRODUCT` (408), `VAULTIC_COMM` + (`SPI` or `I2C`), and log level. +* Scripts: `demos/wolfssl/tls_client/scripts` - `install.sh` (set up the + CMake build trees), `build.sh` (build the perso apps, the glue, and the + demos), `perso_client_vaultic.sh` (provision the chip), `run_server.sh` and + `run_client_vaultic.sh` (start the plain server, then the VaultIC client). + +Steps: + +1. Set `demos/config.cfg` (`PRODUCT=408`, `VAULTIC_COMM=I2C` or `SPI`), then + run `install.sh` and `build.sh`. Point the glue at a wolfSSL tree that + contains this port (the devkit's `wolfssl_patch` copy is no longer needed + once the port is upstream). When the glue compiles `vaultic.c` in its own + library, apply the two flags from "Building the port outside libwolfssl" + above. +2. Provision the chip once with `perso_client_vaultic.sh`. It runs the + `perso_tls` app (`vaultic_tls-4xx/apps/perso_tls`) with the device key, + device cert, and root CA cert from `VaultIC-TLS/certificates` + (`deviceKey.der`, `deviceCert.der`, `rootCACert.der`), submitting the + manufacturer password supplied by your SealSQ FAE, then runs + `check_tls_perso` (`vaultic_tls-4xx/apps/check_tls_perso`) to read the + certificates back off the chip and confirm success. +3. Run `run_server.sh` in one shell and `run_client_vaultic.sh` in another to + perform TLS 1.2 and TLS 1.3 handshakes whose ECC operations run on the + VaultIC. + +## Raspberry Pi I2C and clock stretching + +This section is about the external VaultIC-TLS SDK's I2C transport on a +Raspberry Pi, not about the wolfSSL port itself, but it is the most common +thing that blocks bring-up, so it is documented here. The VaultIC is an I2C +(or SPI) secure element; when it is attached over I2C it stretches the clock +(holds SCL low) while it performs internal operations, and the Raspberry Pi +I2C hardware has long-standing trouble with clock-stretching peripherals. + +Symptoms of the problem are `TWI Send/Receive failure` (status `CF0D`/`CF0F`), +`Block Protocol WARNING (comm error)` / `resync request` messages, and finally +`Block Protocol ERROR (too many rsync requests)`. Short exchanges (for example +reading a status) may succeed while longer, multi-block transfers (for example +the certificate writes done during personalization) fail. + +### Raspberry Pi 2/3/4 (BCM I2C) + +The classic workaround is to lower the I2C bus clock so the timing margins are +wide enough to tolerate the stretching. The VaultIC-TLS SDK's own I2C driver +tries to do this by writing the BCM I2C `DIV` register directly through +`/dev/mem` (which is why it wants to be run with `sudo`), using hardcoded +controller base addresses for the Pi 2/3 (`0x3f804000`) and Pi 4 +(`0xfe804000`). A bus clock around 50 kHz is typically reliable. + +### Raspberry Pi 5 (RP1 / DesignWare I2C) - use a bit-banged bus + +The Pi 5 does not work with the SDK's built-in bitrate control: its I2C +controller lives in the RP1 chip (a DesignWare I2C controller behind +PCIe) at a different address, so the SDK's `/dev/mem` register poke targets a +controller that is not there and has no effect. Worse, the RP1 DesignWare +controller itself hangs on the VaultIC's clock stretching during longer +transfers, reporting `i2c_designware ...: SDA stuck at low` in `dmesg`. +Lowering `dtparam=i2c_arm_baudrate` (even down to 10 kHz) does not fix this, +because the failure is the controller mishandling the stretch, not a timing +margin. + +The reliable fix on the Pi 5 is to drive the same SDA/SCL pins with the +kernel's software (bit-banged) I2C driver, which polls SCL and therefore +honours clock stretching correctly. In `/boot/firmware/config.txt`, disable +the hardware I2C on those pins and add a software bus on GPIO 2 (SDA) and +GPIO 3 (SCL): + +``` +# dtparam=i2c_arm=on +dtoverlay=i2c-gpio,i2c_gpio_sda=2,i2c_gpio_scl=3,i2c_gpio_delay_us=4 +``` + +After rebooting, find the new bus (its `name` reads `i2c-gpio`, for example +`/dev/i2c-13`) and point the SDK's I2C device path at it (the VaultIC-TLS +elib hardcodes `/dev/i2c-1` in its Raspberry Pi TWI driver). With the +bit-banged bus, personalization and full TLS 1.2 / TLS 1.3 handshakes to the +VaultIC 408 complete with no I2C errors. + +Alternatively, wiring the VaultIC over SPI avoids clock stretching entirely. diff --git a/wolfcrypt/src/port/sealsq/vaultic.c b/wolfcrypt/src/port/sealsq/vaultic.c new file mode 100644 index 00000000000..2dc0b3464de --- /dev/null +++ b/wolfcrypt/src/port/sealsq/vaultic.c @@ -0,0 +1,647 @@ +/* vaultic.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 + */ + +/* WISeKey/SealSQ VaultIC secure element port. Offloads TLS ECC P-256 + * operations (sign / verify / keygen / ECDH shared secret) to the VaultIC via + * wolfSSL's PK callbacks, and loads the device + CA certificates stored on the + * chip into a WOLFSSL_CTX. Requires the external SealSQ VaultIC-TLS SDK + * (vaultic_tls.h and the vlt_tls_* P256 API) on the include/link path. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_VAULTIC + +#if !defined(HAVE_PK_CALLBACKS) && !defined(WOLF_CRYPTO_CB) +#error WOLFSSL_VAULTIC requires HAVE_PK_CALLBACKS or WOLF_CRYPTO_CB +#endif + +#include +#include +#include +#include +#include +#ifdef WOLF_CRYPTO_CB + #include +#endif + +#include "vaultic_tls.h" /* vendor VaultIC-TLS SDK (external) */ + +#define P256_BYTE_SZ 32 + +/* Verify a P-256 ECDSA signature on the VaultIC. Shared by the PK verify + * callback and the crypto callback. Sets *result=1 and returns 0 on a valid + * signature; returns WC_HW_E if the device rejects it; returns other wolfCrypt + * errors on encode/decode failure. + * + * Note: the vendor vlt_tls_verify_signature_P256() reports both an invalid + * signature and a device/comms error the same way, so unlike the software + * wc_ecc_verify_hash() (which returns 0 with *result=0 for a merely invalid + * signature) this maps a device rejection to WC_HW_E. All intended callers + * (the TLS PK verify path and the WC_PK_TYPE_ECDSA_VERIFY crypto callback) + * treat any non-zero return as verification failure, so the security outcome + * is correct. */ +static int vaultic_p256_verify(ecc_key* key, const byte* hash, word32 hashSz, + const byte* sig, word32 sigSz, int* result) +{ + int err; + byte signature[2*P256_BYTE_SZ] = {0}; + byte *r, *s; + word32 r_len = P256_BYTE_SZ, s_len = P256_BYTE_SZ; + byte pubKeyX[P256_BYTE_SZ] = {0}; + byte pubKeyY[P256_BYTE_SZ] = {0}; + word32 pubKeyX_len = sizeof(pubKeyX); + word32 pubKeyY_len = sizeof(pubKeyY); + + *result = 0; + + /* Extract raw X and Y coordinates of the public key */ + if ((err = wc_ecc_export_public_raw(key, pubKeyX, &pubKeyX_len, + pubKeyY, &pubKeyY_len)) != 0) { + WOLFSSL_MSG("wc_ecc_export_public_raw"); + return err; + } + vlt_tls_left_pad_P256(pubKeyX, pubKeyX_len); + vlt_tls_left_pad_P256(pubKeyY, pubKeyY_len); + + /* Extract R and S from the signature */ + r = &signature[0]; + s = &signature[sizeof(signature)/2]; + if ((err = wc_ecc_sig_to_rs(sig, sigSz, r, &r_len, s, &s_len)) != 0) { + WOLFSSL_MSG("wc_ecc_sig_to_rs"); + return err; + } + vlt_tls_left_pad_P256(r, r_len); + vlt_tls_left_pad_P256(s, s_len); + + /* Verify signature with the VaultIC */ + if (vlt_tls_verify_signature_P256(hash, hashSz, signature, pubKeyX, + pubKeyY) != 0) { + WOLFSSL_MSG("vlt_tls_verify_signature_P256"); + return WC_HW_E; + } + + *result = 1; + return 0; +} + +#ifdef HAVE_PK_CALLBACKS + +#ifndef VLT_TLS_NO_ECDH +/** + * \brief Key Gen Callback (used by TLS server) + */ +int WOLFSSL_VAULTIC_EccKeyGenCb(WOLFSSL* ssl, ecc_key* key, word32 keySz, + int ecc_curve, void* ctx) +{ + int err; + byte pubKeyX[P256_BYTE_SZ] = {0}; + byte pubKeyY[P256_BYTE_SZ] = {0}; + + (void)ssl; + (void)ctx; + + WOLFSSL_MSG("WOLFSSL_VAULTIC_EccKeyGenCb"); + + /* check requested curve params */ + if (ecc_curve != ECC_SECP256R1) { + WOLFSSL_MSG("ecc_curve != ECC_SECP256R1"); + return NOT_COMPILED_IN; + } + if (keySz != P256_BYTE_SZ) { + WOLFSSL_MSG("keysize != 32. We are supporting ECC P256 case only"); + return NOT_COMPILED_IN; + } + + /* generate new ephemeral key on device */ + if (vlt_tls_keygen_P256(pubKeyX, pubKeyY) != 0) { + WOLFSSL_MSG("vlt_tls_keygen_P256"); + return WC_HW_E; + } + + /* load generated public key into key, used by wolfSSL */ + if ((err = wc_ecc_import_unsigned(key, pubKeyX, pubKeyY, NULL, + ecc_curve)) != 0) { + WOLFSSL_MSG("wc_ecc_import_unsigned"); + } + + return err; +} +#endif + +/** + * \brief Verify Certificate Callback. + * + */ +int WOLFSSL_VAULTIC_EccVerifyCb(WOLFSSL* ssl, + const unsigned char* sig, unsigned int sigSz, + const unsigned char* hash, unsigned int hashSz, + const unsigned char* keyDer, unsigned int keySz, + int* result, void* ctx) +{ + int err; +#ifdef WOLFSSL_SMALL_STACK + ecc_key* key = NULL; +#else + ecc_key key[1]; +#endif + word32 inOutIdx = 0; + + WOLFSSL_MSG("WOLFSSL_VAULTIC_EccVerifyCb"); + + (void)ssl; + (void)ctx; + + if (keyDer == NULL || sig == NULL || hash == NULL || result == NULL) { + return BAD_FUNC_ARG; + } + *result = 0; + +#ifdef WOLFSSL_SMALL_STACK + key = (ecc_key*)XMALLOC(sizeof(ecc_key), NULL, DYNAMIC_TYPE_ECC); + if (key == NULL) { + return MEMORY_E; + } +#endif + + if ((err = wc_ecc_init(key)) != 0) { + WOLFSSL_MSG("wc_ecc_init"); +#ifdef WOLFSSL_SMALL_STACK + XFREE(key, NULL, DYNAMIC_TYPE_ECC); +#endif + return err; + } + + /* Decode the public key */ + if ((err = wc_EccPublicKeyDecode(keyDer, &inOutIdx, key, keySz)) != 0) { + WOLFSSL_MSG("wc_EccPublicKeyDecode"); + goto free_key; + } + + /* Check requested curve */ + if (key->dp->id != ECC_SECP256R1) { + WOLFSSL_MSG("id != ECC_SECP256R1"); + err = NOT_COMPILED_IN; + goto free_key; + } + + err = vaultic_p256_verify(key, hash, hashSz, sig, sigSz, result); + +free_key: + wc_ecc_free(key); +#ifdef WOLFSSL_SMALL_STACK + XFREE(key, NULL, DYNAMIC_TYPE_ECC); +#endif + + return err; +} + + +/** + * \brief Sign Certificate Callback. + */ +int WOLFSSL_VAULTIC_EccSignCb(WOLFSSL* ssl, const byte* in, + word32 inSz, byte* out, word32* outSz, + const byte* key, word32 keySz, void* ctx) +{ + int err; + byte sig_R[P256_BYTE_SZ] = {0}; + byte sig_S[P256_BYTE_SZ] = {0}; + + (void)ssl; + (void)ctx; + (void)key; + (void)keySz; + + WOLFSSL_MSG("WOLFSSL_VAULTIC_EccSignCb"); + + /* Sign input message using VaultIC */ + if (vlt_tls_compute_signature_P256(in, inSz, sig_R, sig_S) != 0) { + WOLFSSL_MSG("vlt_tls_compute_signature_P256"); + return WC_HW_E; + } + + /* Convert R and S to signature */ + if ((err = wc_ecc_rs_raw_to_sig(sig_R, P256_BYTE_SZ, sig_S, P256_BYTE_SZ, + out, outSz)) != 0) { + WOLFSSL_MSG("wc_ecc_rs_raw_to_sig"); + return err; + } + + return err; +} + +#ifndef VLT_TLS_NO_ECDH +/** + * \brief Create pre master secret using peer's public key and self private key. + */ +int WOLFSSL_VAULTIC_EccSharedSecretCb(WOLFSSL* ssl, ecc_key* otherPubKey, + unsigned char* pubKeyDer, unsigned int* pubKeySz, + unsigned char* out, unsigned int* outlen, + int side, void* ctx) +{ + int err; + byte otherPubKeyX[P256_BYTE_SZ] = {0}; + byte otherPubKeyY[P256_BYTE_SZ] = {0}; + word32 otherPubKeyX_len = sizeof(otherPubKeyX); + word32 otherPubKeyY_len = sizeof(otherPubKeyY); + byte pubKeyX[P256_BYTE_SZ] = {0}; + byte pubKeyY[P256_BYTE_SZ] = {0}; +#ifdef WOLFSSL_SMALL_STACK + ecc_key* tmpKey = NULL; +#else + ecc_key tmpKey[1]; +#endif + int tmpKey_inited = 0; + + (void)ssl; + (void)ctx; + + WOLFSSL_MSG("WOLFSSL_VAULTIC_EccSharedSecretCb"); + + /* check requested curve */ + if (otherPubKey->dp->id != ECC_SECP256R1) { + WOLFSSL_MSG("id != ECC_SECP256R1"); + return NOT_COMPILED_IN; + } + +#ifdef WOLFSSL_SMALL_STACK + tmpKey = (ecc_key*)XMALLOC(sizeof(ecc_key), NULL, DYNAMIC_TYPE_ECC); + if (tmpKey == NULL) { + return MEMORY_E; + } +#endif + + /* for client: create and export public key */ + if (side == WOLFSSL_CLIENT_END) { + + /* Export otherPubKey raw X and Y */ + err = wc_ecc_export_public_raw(otherPubKey, + &otherPubKeyX[0], &otherPubKeyX_len, + &otherPubKeyY[0], &otherPubKeyY_len); + if (err != 0) { + WOLFSSL_MSG("wc_ecc_export_public_raw"); + goto cleanup; + } + vlt_tls_left_pad_P256(otherPubKeyX, otherPubKeyX_len); + 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) { + + if (vlt_tls_keygen_P256(pubKeyX, pubKeyY) != 0) { + WOLFSSL_MSG("vlt_tls_keygen_P256"); + err = WC_HW_E; + goto cleanup; + } + + /* convert raw unsigned public key to X.963 format for TLS */ + if ((err = wc_ecc_init(tmpKey)) != 0) { + WOLFSSL_MSG("wc_ecc_init"); + goto cleanup; + } + tmpKey_inited = 1; + + if ((err = wc_ecc_import_unsigned(tmpKey, pubKeyX, pubKeyY, + NULL, ECC_SECP256R1)) != 0) { + WOLFSSL_MSG("wc_ecc_import_unsigned"); + goto cleanup; + } + + if ((err = wc_ecc_export_x963(tmpKey, pubKeyDer, pubKeySz)) != 0) { + WOLFSSL_MSG("wc_ecc_export_x963"); + goto cleanup; + } + } + } + + /* for server: import public key */ + else if (side == WOLFSSL_SERVER_END) { + if ((err = wc_ecc_init(tmpKey)) != 0) { + WOLFSSL_MSG("wc_ecc_init"); + goto cleanup; + } + tmpKey_inited = 1; + + /* import peer's key and export as raw unsigned for hardware */ + if ((err = wc_ecc_import_x963_ex(pubKeyDer, *pubKeySz, tmpKey, + ECC_SECP256R1)) != 0) { + WOLFSSL_MSG("wc_ecc_import_x963_ex"); + goto cleanup; + } + + if ((err = wc_ecc_export_public_raw(tmpKey, otherPubKeyX, + &otherPubKeyX_len, otherPubKeyY, &otherPubKeyY_len)) != 0) { + WOLFSSL_MSG("wc_ecc_export_public_raw"); + goto cleanup; + } + vlt_tls_left_pad_P256(otherPubKeyX, otherPubKeyX_len); + vlt_tls_left_pad_P256(otherPubKeyY, otherPubKeyY_len); + } + else { + err = BAD_FUNC_ARG; + goto cleanup; + } + + /* Compute shared secret */ + if (vlt_tls_compute_shared_secret_P256(otherPubKeyX, otherPubKeyY, + out) != 0) { + WOLFSSL_MSG("vlt_tls_compute_shared_secret_P256"); + err = WC_HW_E; + goto cleanup; + } + + *outlen = P256_BYTE_SZ; + err = 0; + +cleanup: + if (tmpKey_inited) { + wc_ecc_free(tmpKey); + } +#ifdef WOLFSSL_SMALL_STACK + XFREE(tmpKey, NULL, DYNAMIC_TYPE_ECC); +#endif + + return err; +} +#endif /* VLT_TLS_NO_ECDH */ + +#endif /* HAVE_PK_CALLBACKS */ + +/** + * \brief Read VaultIC Certificates and add them to wolfssl context + */ +int WOLFSSL_VAULTIC_LoadCertificates(WOLFSSL_CTX* ctx) +{ + int ret = WOLFSSL_FATAL_ERROR; + + /* CA certificate */ + unsigned char *ca_cert = NULL; + int sizeof_ca_cert = 0; + + /* Device certificate */ + unsigned char *device_cert = NULL; + int sizeof_device_cert = 0; + + /* 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 WC_HW_E; + } + + device_cert = (unsigned char*)XMALLOC(sizeof_device_cert, NULL, + DYNAMIC_TYPE_CERT); + if (device_cert == NULL) { + WOLFSSL_MSG("malloc device_cert"); + return MEMORY_E; + } + + if (vlt_tls_read_cert(device_cert, SSL_VIC_DEVICE_CERT) != 0) { + WOLFSSL_MSG("vlt_tls_read_cert Device"); + ret = WC_HW_E; + goto free_cert_buffers; + } + + WOLFSSL_MSG("[Device certificate]"); +#ifdef WOLFSSL_VAULTIC_DEBUG + WOLFSSL_BUFFER(device_cert, sizeof_device_cert); +#endif + + /* 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"); + ret = WC_HW_E; + goto free_cert_buffers; + } + + ca_cert = (unsigned char*)XMALLOC(sizeof_ca_cert, NULL, + DYNAMIC_TYPE_CERT); + if (ca_cert == NULL) { + WOLFSSL_MSG("malloc ca_cert"); + ret = MEMORY_E; + goto free_cert_buffers; + } + + if (vlt_tls_read_cert(ca_cert, SSL_VIC_CA_CERT) != 0) { + WOLFSSL_MSG("vlt_tls_read_cert CA"); + ret = WC_HW_E; + goto free_cert_buffers; + } + + WOLFSSL_MSG("[CA certificate]"); +#ifdef WOLFSSL_VAULTIC_DEBUG + 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 */ + 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; + goto free_cert_buffers; + } + + /* VaultIC certificates successfully injected into wolfSSL */ + ret = 0; + +free_cert_buffers: + if (ca_cert != NULL) + XFREE(ca_cert, NULL, DYNAMIC_TYPE_CERT); + if (device_cert != NULL) + XFREE(device_cert, NULL, DYNAMIC_TYPE_CERT); + + return ret; +} + +#ifdef HAVE_PK_CALLBACKS +int WOLFSSL_VAULTIC_SetupPkCallbacks(WOLFSSL_CTX* ctx) +{ + wolfSSL_CTX_SetEccSignCb(ctx, WOLFSSL_VAULTIC_EccSignCb); + wolfSSL_CTX_SetEccVerifyCb(ctx, WOLFSSL_VAULTIC_EccVerifyCb); +#ifndef VLT_TLS_NO_ECDH + wolfSSL_CTX_SetEccKeyGenCb(ctx, WOLFSSL_VAULTIC_EccKeyGenCb); + wolfSSL_CTX_SetEccSharedSecretCb(ctx, WOLFSSL_VAULTIC_EccSharedSecretCb); +#endif + return 0; +} + +int WOLFSSL_VAULTIC_SetupPkCallbackCtx(WOLFSSL* ssl, void* user_ctx) +{ +#ifndef VLT_TLS_NO_ECDH + wolfSSL_SetEccKeyGenCtx(ssl, user_ctx); + wolfSSL_SetEccSharedSecretCtx(ssl, user_ctx); +#endif + wolfSSL_SetEccSignCtx(ssl, user_ctx); + wolfSSL_SetEccVerifyCtx(ssl, user_ctx); + return 0; +} +#endif /* HAVE_PK_CALLBACKS */ + +#ifdef WOLF_CRYPTO_CB + +/** + * \brief wolfCrypt crypto callback. Dispatches ECC P-256 operations to the + * VaultIC. Register with WOLFSSL_VAULTIC_RegisterCryptoCb() and select + * it with a devId (wolfSSL_CTX_SetDevId / wc_ecc_init_ex). Unsupported + * curves and operations return CRYPTOCB_UNAVAILABLE so wolfCrypt falls + * back to software. Note: the VaultIC 408 silicon supports P-384, but + * the vendor vlt_tls API exposes P-256 only, so P-384 is not offloaded. + */ +int WOLFSSL_VAULTIC_CryptoCb(int devId, wc_CryptoInfo* info, void* ctx) +{ + int rc = CRYPTOCB_UNAVAILABLE; + + (void)devId; + (void)ctx; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type != WC_ALGO_TYPE_PK) { + return CRYPTOCB_UNAVAILABLE; + } + + switch (info->pk.type) { + case WC_PK_TYPE_ECDSA_SIGN: + { + byte sig_R[P256_BYTE_SZ] = {0}; + byte sig_S[P256_BYTE_SZ] = {0}; + + WOLFSSL_MSG("WOLFSSL_VAULTIC_CryptoCb: ECDSA sign"); + if (info->pk.eccsign.key == NULL || + info->pk.eccsign.key->dp == NULL || + info->pk.eccsign.key->dp->id != ECC_SECP256R1) { + /* P-384 is a vendor vlt_tls gap; let software handle it */ + break; + } + if (vlt_tls_compute_signature_P256(info->pk.eccsign.in, + info->pk.eccsign.inlen, sig_R, sig_S) != 0) { + WOLFSSL_MSG("vlt_tls_compute_signature_P256"); + rc = WC_HW_E; + break; + } + rc = wc_ecc_rs_raw_to_sig(sig_R, P256_BYTE_SZ, sig_S, P256_BYTE_SZ, + info->pk.eccsign.out, info->pk.eccsign.outlen); + break; + } + + case WC_PK_TYPE_ECDSA_VERIFY: + WOLFSSL_MSG("WOLFSSL_VAULTIC_CryptoCb: ECDSA verify"); + if (info->pk.eccverify.key == NULL || + info->pk.eccverify.key->dp == NULL || + info->pk.eccverify.key->dp->id != ECC_SECP256R1) { + break; + } + rc = vaultic_p256_verify(info->pk.eccverify.key, + info->pk.eccverify.hash, info->pk.eccverify.hashlen, + info->pk.eccverify.sig, info->pk.eccverify.siglen, + info->pk.eccverify.res); + break; + +#ifndef VLT_TLS_NO_ECDH + case WC_PK_TYPE_ECDH: + { + byte peerX[P256_BYTE_SZ] = {0}; + byte peerY[P256_BYTE_SZ] = {0}; + word32 peerX_len = sizeof(peerX); + word32 peerY_len = sizeof(peerY); + + 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) { + break; + } + if ((rc = wc_ecc_export_public_raw(info->pk.ecdh.public_key, + peerX, &peerX_len, peerY, &peerY_len)) != 0) { + WOLFSSL_MSG("wc_ecc_export_public_raw"); + break; + } + vlt_tls_left_pad_P256(peerX, peerX_len); + vlt_tls_left_pad_P256(peerY, peerY_len); + if (vlt_tls_compute_shared_secret_P256(peerX, peerY, + info->pk.ecdh.out) != 0) { + WOLFSSL_MSG("vlt_tls_compute_shared_secret_P256"); + rc = WC_HW_E; + break; + } + *info->pk.ecdh.outlen = P256_BYTE_SZ; + rc = 0; + break; + } + + case WC_PK_TYPE_EC_KEYGEN: + { + byte pubKeyX[P256_BYTE_SZ] = {0}; + byte pubKeyY[P256_BYTE_SZ] = {0}; + + WOLFSSL_MSG("WOLFSSL_VAULTIC_CryptoCb: EC keygen"); + if (info->pk.eckg.curveId != ECC_SECP256R1) { + break; + } + if (vlt_tls_keygen_P256(pubKeyX, pubKeyY) != 0) { + WOLFSSL_MSG("vlt_tls_keygen_P256"); + rc = WC_HW_E; + break; + } + rc = wc_ecc_import_unsigned(info->pk.eckg.key, pubKeyX, pubKeyY, + NULL, ECC_SECP256R1); + break; + } +#endif /* VLT_TLS_NO_ECDH */ + + default: + /* rc stays CRYPTOCB_UNAVAILABLE -> software fallback */ + break; + } + + return rc; +} + +/** + * \brief Register the VaultIC crypto callback for the given devId. + */ +int WOLFSSL_VAULTIC_RegisterCryptoCb(int devId) +{ + return wc_CryptoCb_RegisterDevice(devId, WOLFSSL_VAULTIC_CryptoCb, NULL); +} + +#endif /* WOLF_CRYPTO_CB */ + +#endif /* WOLFSSL_VAULTIC */ diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index e49082f6ae4..022e39edefc 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -229,6 +229,10 @@ if BUILD_IOTSAFE nobase_include_HEADERS+= wolfssl/wolfcrypt/port/iotsafe/iotsafe.h endif +if BUILD_VAULTIC +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/sealsq/vaultic.h +endif + if BUILD_PSA nobase_include_HEADERS+= wolfssl/wolfcrypt/port/psa/psa.h endif diff --git a/wolfssl/wolfcrypt/port/sealsq/vaultic.h b/wolfssl/wolfcrypt/port/sealsq/vaultic.h new file mode 100644 index 00000000000..a7957349402 --- /dev/null +++ b/wolfssl/wolfcrypt/port/sealsq/vaultic.h @@ -0,0 +1,88 @@ +/* vaultic.h + * + * 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 + */ + +/* WISeKey/SealSQ VaultIC secure element port (TLS PK callbacks and wolfCrypt + * crypto callback / devId) */ + +#ifndef WOLFPORT_SEALSQ_VAULTIC_H +#define WOLFPORT_SEALSQ_VAULTIC_H + +#include + +#ifdef WOLFSSL_VAULTIC + +#include +#include +#ifdef WOLF_CRYPTO_CB + #include +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef HAVE_PK_CALLBACKS +WOLFSSL_API int WOLFSSL_VAULTIC_EccKeyGenCb(WOLFSSL* ssl, ecc_key* key, + word32 keySz, int ecc_curve, void* ctx); + +WOLFSSL_API int WOLFSSL_VAULTIC_EccVerifyCb(WOLFSSL* ssl, + const unsigned char* sig, unsigned int sigSz, + const unsigned char* hash, unsigned int hashSz, + const unsigned char* keyDer, unsigned int keySz, + int* result, void* ctx); + +WOLFSSL_API int WOLFSSL_VAULTIC_EccSignCb(WOLFSSL* ssl, + const byte* in, word32 inSz, + byte* out, word32* outSz, + const byte* key, word32 keySz, void* ctx); + +WOLFSSL_API int WOLFSSL_VAULTIC_EccSharedSecretCb(WOLFSSL* ssl, + ecc_key* otherKey, + unsigned char* pubKeyDer, unsigned int* pubKeySz, + unsigned char* out, unsigned int* outlen, + int side, void* ctx); +#endif /* HAVE_PK_CALLBACKS */ + +WOLFSSL_API int WOLFSSL_VAULTIC_LoadCertificates(WOLFSSL_CTX* ctx); + +#ifdef HAVE_PK_CALLBACKS +/* Helper APIs for setting up PK callbacks */ +WOLFSSL_API int WOLFSSL_VAULTIC_SetupPkCallbacks(WOLFSSL_CTX* ctx); +WOLFSSL_API int WOLFSSL_VAULTIC_SetupPkCallbackCtx(WOLFSSL* ssl, + void* user_ctx); +#endif /* HAVE_PK_CALLBACKS */ + +#ifdef WOLF_CRYPTO_CB +/* devId for selecting the VaultIC via the crypto callback ("VLTC") */ +#define WOLF_VAULTIC_DEVID 0x564C5443 + +WOLFSSL_API int WOLFSSL_VAULTIC_CryptoCb(int devId, wc_CryptoInfo* info, + void* ctx); +WOLFSSL_API int WOLFSSL_VAULTIC_RegisterCryptoCb(int devId); +#endif /* WOLF_CRYPTO_CB */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFSSL_VAULTIC */ + +#endif /* WOLFPORT_SEALSQ_VAULTIC_H */