diff --git a/.github/scripts/check-headers.sh b/.github/scripts/check-headers.sh index 33bdd7f345..f53dbc5b5d 100755 --- a/.github/scripts/check-headers.sh +++ b/.github/scripts/check-headers.sh @@ -66,8 +66,7 @@ else | grep -vE '^wolfssl/wolfcrypt/port/caam/(caam_driver|caam_qnx|wolfcaam_hash)\.h$' \ | grep -vE '^wolfssl/wolfcrypt/port/kcapi/' \ | grep -vE '^wolfssl/wolfcrypt/port/nxp/(dcp_port|se050_port)\.h$' \ - | grep -vE '^wolfssl/wolfcrypt/port/Renesas/(renesas_fspsm_internal|renesas-rx64-hw-crypt|renesas-tsip-crypt|renesas_tsip_internal)\.h$' \ - | grep -vE '^wolfssl/wolfcrypt/port/silabs/silabs_aes\.h$' + | grep -vE '^wolfssl/wolfcrypt/port/Renesas/(renesas_fspsm_internal|renesas-rx64-hw-crypt|renesas-tsip-crypt|renesas_tsip_internal)\.h$' ) fi diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index e140ff5a2a..f4749fcee3 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -1111,6 +1111,7 @@ WOLFSSL_SHA3_NO_AVX2 WOLFSSL_SHA3_PPC64_BLOCKS_N WOLFSSL_SHA512_HASHTYPE WOLFSSL_SHUTDOWNONCE +WOLFSSL_SILABS_NO_VAULT_KEYS WOLFSSL_SILABS_TRNG WOLFSSL_SLHDSA_FULL_HASH WOLFSSL_SLHDSA_NO_VERIFY_ONLY diff --git a/configure.ac b/configure.ac index c1b4212bc2..be917b6e9f 100644 --- a/configure.ac +++ b/configure.ac @@ -3830,6 +3830,31 @@ then fi +# Silicon Labs EFR32 Secure Element crypto-callback port. +# On-target the application supplies the Simplicity SDK include path. This +# option is a host compile-test of the port: it swaps the SE Manager headers +# for a shim (WOLFSSL_SILABS_HOST_TEST) so the cryptocb dispatch and wiring +# build without the vendor SDK. It forces crypto callbacks on (see the +# cryptocb block). +# Example: "./configure --enable-silabs-cryptocb" +ENABLED_SILABS_CRYPTOCB="no" +AC_ARG_ENABLE([silabs-cryptocb], + [AS_HELP_STRING([--enable-silabs-cryptocb], + [Enable Silicon Labs EFR32 Secure Element crypto-callback port (host compile-test).])], + [ ENABLED_SILABS_CRYPTOCB=$enableval ], + [ ENABLED_SILABS_CRYPTOCB=no ]) + +if test "x$ENABLED_SILABS_CRYPTOCB" != "xno" +then + # The shim models an EFR32xG25 (Series 2 Config 5) with Secure Vault High. + # The port's AES-ECB dispatch is gated on HAVE_AES_ECB and compiles out + # without it, so this option does not turn AES-ECB on. To compile-test that + # path too, add --enable-aesecb, which enables the (public, + # unauthenticated) AES-ECB API through the normal option. + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SILABS_CRYPTOCB -DWOLFSSL_SILABS_HOST_TEST" +fi + + # NXP SE050 # Example: "./configure --with-se050=/home/pi/simw_top" ENABLED_SE050="no" @@ -11731,7 +11756,7 @@ then 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" +if test "x$ENABLED_PKCS11" = "xyes" || test "x$ENABLED_WOLFTPM" = "xyes" || test "$ENABLED_CAAM" != "no" || test "x$ENABLED_RTL8735B" != "xno" || test "x$ENABLED_SILABS_CRYPTOCB" != "xno" || test "x$ENABLED_VAULTIC" = "xyes" then ENABLED_CRYPTOCB=yes fi @@ -13646,6 +13671,7 @@ 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"]) +AM_CONDITIONAL([BUILD_SILABS_CRYPTOCB],[test "x$ENABLED_SILABS_CRYPTOCB" != "xno"]) AM_CONDITIONAL([BUILD_TROPIC01],[test "x$ENABLED_TROPIC01" = "xyes"]) AM_CONDITIONAL([BUILD_KDF],[test "x$ENABLED_KDF" = "xyes"]) AM_CONDITIONAL([BUILD_HMAC],[test "x$ENABLED_HMAC" = "xyes"]) diff --git a/src/keys.c b/src/keys.c index f9e7d3b7b3..1d2016148a 100644 --- a/src/keys.c +++ b/src/keys.c @@ -2477,15 +2477,17 @@ int SetKeys(Ciphers* enc, Ciphers* dec, Keys* keys, CipherSpecs* specs, #endif if (side == WOLFSSL_CLIENT_END) { if (enc) { - chachaRet = wc_Chacha_SetKey(enc->chacha, keys->client_write_key, - specs->key_size); + chachaRet = wc_Chacha_SetKey_ex(enc->chacha, + keys->client_write_key, + specs->key_size, heap, devId); XMEMCPY(keys->aead_enc_imp_IV, keys->client_write_IV, CHACHA20_IMP_IV_SZ); if (chachaRet != 0) return chachaRet; } if (dec) { - chachaRet = wc_Chacha_SetKey(dec->chacha, keys->server_write_key, - specs->key_size); + chachaRet = wc_Chacha_SetKey_ex(dec->chacha, + keys->server_write_key, + specs->key_size, heap, devId); XMEMCPY(keys->aead_dec_imp_IV, keys->server_write_IV, CHACHA20_IMP_IV_SZ); if (chachaRet != 0) return chachaRet; @@ -2493,15 +2495,17 @@ int SetKeys(Ciphers* enc, Ciphers* dec, Keys* keys, CipherSpecs* specs, } else { if (enc) { - chachaRet = wc_Chacha_SetKey(enc->chacha, keys->server_write_key, - specs->key_size); + chachaRet = wc_Chacha_SetKey_ex(enc->chacha, + keys->server_write_key, + specs->key_size, heap, devId); XMEMCPY(keys->aead_enc_imp_IV, keys->server_write_IV, CHACHA20_IMP_IV_SZ); if (chachaRet != 0) return chachaRet; } if (dec) { - chachaRet = wc_Chacha_SetKey(dec->chacha, keys->client_write_key, - specs->key_size); + chachaRet = wc_Chacha_SetKey_ex(dec->chacha, + keys->client_write_key, + specs->key_size, heap, devId); XMEMCPY(keys->aead_dec_imp_IV, keys->client_write_IV, CHACHA20_IMP_IV_SZ); if (chachaRet != 0) return chachaRet; diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index f26e689fd3..9853805dad 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -6136,6 +6136,13 @@ static void AesSetKey_C(Aes* aes, const byte* key, word32 keySz, int dir) return BAD_FUNC_ARG; } + #ifdef WOLFSSL_SILABS_SE_TYPES + /* A plaintext key supersedes any resident key bound by + * wc_SilabsSe_AesUse*Key(); without clearing the binding the SE would + * keep using the key it still holds. */ + aes->ctx.keySet = 0; + #endif + /* sometimes hardware may not support all keylengths (e.g. ESP32-S3) */ #if defined(WOLFSSL_ESPIDF) && defined(NEED_AES_HW_FALLBACK) ESP_LOGV(TAG, "wc_AesSetKey fallback check %d", keylen); diff --git a/wolfcrypt/src/chacha.c b/wolfcrypt/src/chacha.c index 67214e5f7e..5d00a85b94 100644 --- a/wolfcrypt/src/chacha.c +++ b/wolfcrypt/src/chacha.c @@ -199,7 +199,8 @@ static const word32 tau[4] = {0x61707865, 0x3120646e, 0x79622d36, 0x6b206574}; /** * Key setup. 8 word iv (nonce) */ -int wc_Chacha_SetKey(ChaCha* ctx, const byte* key, word32 keySz) +int wc_Chacha_SetKey_ex(ChaCha* ctx, const byte* key, word32 keySz, + void* heap, int devId) { #if (!defined(USE_ARM_CHACHA_SPEEDUP) || defined(WOLFSSL_ARM_CHACHA_NEED_C)) && \ !defined(USE_RISCV_CHACHA_SPEEDUP) @@ -276,9 +277,23 @@ int wc_Chacha_SetKey(ChaCha* ctx, const byte* key, word32 keySz) ctx->left = 0; /* resets state */ ctx->keySet = 1; +#ifdef WOLF_CRYPTO_CB + /* A device takes the plaintext key, not the expanded state above. */ + XMEMCPY(ctx->devKey, key, keySz); + ctx->devKeySz = keySz; + ctx->devId = devId; +#endif + (void)heap; /* nothing on this path allocates */ + (void)devId; + return 0; } +int wc_Chacha_SetKey(ChaCha* ctx, const byte* key, word32 keySz) +{ + return wc_Chacha_SetKey_ex(ctx, key, keySz, NULL, INVALID_DEVID); +} + #if (!defined(USE_INTEL_CHACHA_SPEEDUP) && !defined(USE_ARM_CHACHA_SPEEDUP) && \ !defined(USE_RISCV_CHACHA_SPEEDUP)) || defined(WOLFSSL_ARM_CHACHA_NEED_C) /** diff --git a/wolfcrypt/src/chacha20_poly1305.c b/wolfcrypt/src/chacha20_poly1305.c index 5105137dcc..e53d547fa7 100644 --- a/wolfcrypt/src/chacha20_poly1305.c +++ b/wolfcrypt/src/chacha20_poly1305.c @@ -34,6 +34,10 @@ or Authenticated Encryption with Additional Data (AEAD) algorithm. #include #include +#ifdef WOLF_CRYPTO_CB + #include +#endif + #ifdef NO_INLINE #include #else @@ -442,6 +446,17 @@ WOLFSSL_API int wc_ChaCha20Poly1305_Encrypt_ex(ChaCha* chacha, Poly1305* poly, return BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB + /* devId comes from wc_Chacha_SetKey_ex(). This is the path the TLS + * record layer uses. Software runs if the device declines. */ + if (chacha->devId != INVALID_DEVID) { + ret = wc_CryptoCb_Chacha20Poly1305Encrypt(chacha->devId, chacha->devKey, + nonce, aad, aadSz, in, sz, out, tag); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + } +#endif + #ifdef WOLFSSL_CHACHA20_POLY1305_SHORT if (sz <= CHACHA20_POLY1305_SHORT_MAX) return chacha20_poly1305_encrypt_short(chacha, poly, out, in, sz, @@ -532,6 +547,16 @@ WOLFSSL_API int wc_ChaCha20Poly1305_Decrypt_ex(ChaCha* chacha, Poly1305* poly, return BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB + /* See the encrypt counterpart. */ + if (chacha->devId != INVALID_DEVID) { + ret = wc_CryptoCb_Chacha20Poly1305Decrypt(chacha->devId, chacha->devKey, + nonce, aad, aadSz, in, sz, tag, out); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + } +#endif + #ifdef WOLFSSL_CHACHA20_POLY1305_SHORT if (sz <= CHACHA20_POLY1305_SHORT_MAX) return chacha20_poly1305_decrypt_short(chacha, poly, out, in, sz, @@ -801,10 +826,10 @@ int wc_ChaCha20Poly1305_CheckTag( return ret; } -int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, +int wc_ChaCha20Poly1305_Init_ex(ChaChaPoly_Aead* aead, const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], - int isEncrypt) + int isEncrypt, void* heap, int devId) { int ret; byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE]; @@ -833,8 +858,10 @@ int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, aead->isEncrypt = isEncrypt ? 1 : 0; /* Initialize the ChaCha20 context (key and iv) */ - ret = wc_Chacha_SetKey(&aead->chacha, inKey, - CHACHA20_POLY1305_AEAD_KEYSIZE); + ret = wc_Chacha_SetKey_ex(&aead->chacha, inKey, + CHACHA20_POLY1305_AEAD_KEYSIZE, heap, devId); + (void)heap; + (void)devId; if (ret == 0) { ret = wc_Chacha_SetIV(&aead->chacha, inIV, CHACHA20_POLY1305_AEAD_INITIAL_COUNTER); @@ -870,6 +897,15 @@ int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, return ret; } +int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, + const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], + const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], + int isEncrypt) +{ + return wc_ChaCha20Poly1305_Init_ex(aead, inKey, inIV, isEncrypt, NULL, + INVALID_DEVID); +} + /* optional additional authentication data */ int wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead* aead, const byte* inAAD, word32 inAADLen) diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 8783a1684f..79da71c3b3 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -301,6 +301,8 @@ static const char* GetKdfTypeStr(int type) return "HKDF Extract"; case WC_KDF_TYPE_HKDF_EXPAND: return "HKDF Expand"; + case WC_KDF_TYPE_PBKDF2: + return "PBKDF2"; case WC_KDF_TYPE_TWOSTEP_CMAC: return "TWOSTEP_CMAC"; } @@ -2232,8 +2234,73 @@ int wc_CryptoCb_PqcSignatureCheckPrivKey(void* key, int type, } #endif /* HAVE_FALCON || WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */ +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) +int wc_CryptoCb_Chacha20Poly1305Encrypt(int devId, const byte* inKey, + const byte* inIV, const byte* inAAD, word32 inAADSz, const byte* in, + word32 inSz, byte* out, byte* outAuthTag) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_CIPHER); + + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + + cryptoInfo.algo_type = WC_ALGO_TYPE_CIPHER; + cryptoInfo.cipher.type = WC_CIPHER_CHACHA; + cryptoInfo.cipher.enc = 1; + cryptoInfo.cipher.chacha20_poly1305_enc.inKey = inKey; + cryptoInfo.cipher.chacha20_poly1305_enc.inIV = inIV; + cryptoInfo.cipher.chacha20_poly1305_enc.inAAD = inAAD; + cryptoInfo.cipher.chacha20_poly1305_enc.inAADSz = inAADSz; + cryptoInfo.cipher.chacha20_poly1305_enc.in = in; + cryptoInfo.cipher.chacha20_poly1305_enc.inSz = inSz; + cryptoInfo.cipher.chacha20_poly1305_enc.out = out; + cryptoInfo.cipher.chacha20_poly1305_enc.outAuthTag = outAuthTag; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} + +int wc_CryptoCb_Chacha20Poly1305Decrypt(int devId, const byte* inKey, + const byte* inIV, const byte* inAAD, word32 inAADSz, const byte* in, + word32 inSz, const byte* inAuthTag, byte* out) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_CIPHER); + + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + + cryptoInfo.algo_type = WC_ALGO_TYPE_CIPHER; + cryptoInfo.cipher.type = WC_CIPHER_CHACHA; + cryptoInfo.cipher.enc = 0; + cryptoInfo.cipher.chacha20_poly1305_dec.inKey = inKey; + cryptoInfo.cipher.chacha20_poly1305_dec.inIV = inIV; + cryptoInfo.cipher.chacha20_poly1305_dec.inAAD = inAAD; + cryptoInfo.cipher.chacha20_poly1305_dec.inAADSz = inAADSz; + cryptoInfo.cipher.chacha20_poly1305_dec.in = in; + cryptoInfo.cipher.chacha20_poly1305_dec.inSz = inSz; + cryptoInfo.cipher.chacha20_poly1305_dec.inAuthTag = inAuthTag; + cryptoInfo.cipher.chacha20_poly1305_dec.out = out; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} +#endif /* HAVE_CHACHA && HAVE_POLY1305 */ + #ifndef NO_AES #ifdef HAVE_AESGCM + int wc_CryptoCb_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, @@ -3589,6 +3656,39 @@ int wc_CryptoCb_DefaultDevID(void) return ret; } +#if (defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED)) +int wc_CryptoCb_Pbkdf2(byte* output, const byte* passwd, int pLen, + const byte* salt, int sLen, int iterations, int kLen, int hashType, + int devId) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + /* Find registered callback device */ + dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_KDF); + + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + + cryptoInfo.algo_type = WC_ALGO_TYPE_KDF; + cryptoInfo.kdf.type = WC_KDF_TYPE_PBKDF2; + cryptoInfo.kdf.pbkdf2.output = output; + cryptoInfo.kdf.pbkdf2.passwd = passwd; + cryptoInfo.kdf.pbkdf2.pLen = pLen; + cryptoInfo.kdf.pbkdf2.salt = salt; + cryptoInfo.kdf.pbkdf2.sLen = sLen; + cryptoInfo.kdf.pbkdf2.iterations = iterations; + cryptoInfo.kdf.pbkdf2.kLen = kLen; + cryptoInfo.kdf.pbkdf2.hashType = hashType; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} +#endif /* HAVE_PBKDF2 && !NO_HMAC && !NO_PWDBASED */ + #if defined(HAVE_HKDF) && !defined(NO_HMAC) int wc_CryptoCb_Hkdf(int hashType, const byte* inKey, word32 inKeySz, const byte* salt, word32 saltSz, const byte* info, diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 37b64a89ef..c56834efb6 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -2007,6 +2007,14 @@ int wc_ecc_set_curve(ecc_key* key, int keysize, int curve_id) return ECC_BAD_ARG_E; } +#ifdef WOLFSSL_SILABS_SE_TYPES + /* Every import and keygen path resolves the curve here, so this is where + * a key stops representing whatever wc_SilabsSe_EccUse*Key() bound to it. + * Leaving the binding set would keep the SE on the old resident key. */ + ForceZero(key->key_raw, sizeof(key->key_raw)); + key->silabsKeySet = 0; +#endif + /* handle custom case */ if (key->idx != ECC_CUSTOM_IDX) { int x; @@ -8621,6 +8629,13 @@ int wc_ecc_free(ecc_key* key) wc_MAXQ10XX_EccFree(key); #endif +#ifdef WOLFSSL_SILABS_SE_TYPES + /* key_raw holds the private scalar alongside the public point whenever the + * SE has been handed a plaintext key. Scrub it. */ + ForceZero(key->key_raw, sizeof(key->key_raw)); + key->silabsKeySet = 0; +#endif + #ifdef WOLFSSL_DHUK /* Scrub the DHUK derivation seed and wrapped scalar (both secret). */ ForceZero(key->dhuk_seed, sizeof(key->dhuk_seed)); diff --git a/wolfcrypt/src/include.am b/wolfcrypt/src/include.am index 53fa2ca0c4..acac556208 100644 --- a/wolfcrypt/src/include.am +++ b/wolfcrypt/src/include.am @@ -132,6 +132,14 @@ EXTRA_DIST += wolfcrypt/src/port/ti/ti-aes.c \ wolfcrypt/src/port/silabs/silabs_ecc.c \ wolfcrypt/src/port/silabs/silabs_hash.c \ wolfcrypt/src/port/silabs/silabs_random.c \ + wolfcrypt/src/port/silabs/silabs_cryptocb.c \ + wolfcrypt/src/port/silabs/silabs_cb_rng.c \ + wolfcrypt/src/port/silabs/silabs_cb_hash.c \ + wolfcrypt/src/port/silabs/silabs_cb_cipher.c \ + wolfcrypt/src/port/silabs/silabs_cb_cmac.c \ + wolfcrypt/src/port/silabs/silabs_cb_pk.c \ + wolfcrypt/src/port/silabs/silabs_cb_kdf.c \ + wolfcrypt/src/port/silabs/silabs_key.c \ wolfcrypt/src/port/silabs/README.md \ wolfcrypt/src/port/st/stm32.c \ wolfcrypt/src/port/st/README.md \ @@ -288,6 +296,21 @@ if BUILD_RTL8735B src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/realtek/rtl8735b.c endif +if BUILD_SILABS_CRYPTOCB +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_aes.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_ecc.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_hash.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_random.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_cryptocb.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_cb_rng.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_cb_hash.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_cb_cipher.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_cb_cmac.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_cb_pk.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_cb_kdf.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/silabs/silabs_key.c +endif + if BUILD_PSA src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/psa/psa.c src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/psa/psa_hash.c diff --git a/wolfcrypt/src/port/silabs/README.md b/wolfcrypt/src/port/silabs/README.md index aa1a97d298..03209471b3 100644 --- a/wolfcrypt/src/port/silabs/README.md +++ b/wolfcrypt/src/port/silabs/README.md @@ -7,7 +7,19 @@ Tested on ERF32 Gecko Series 2 device config 1 (Secure Element) * https://docs.silabs.com/mcu/latest/efr32mg21/group-SE * https://docs.silabs.com/gecko-platform/latest/service/api/group-sl-se-manager -## Building +There are two independent ports over the same SE Manager code: + +| Port | Macro | Shape | +|---|---|---| +| Direct | `WOLFSSL_SILABS_SE_ACCEL` | Replaces the software implementation of each supported algorithm at compile time. All or nothing per algorithm; no `devId`, no runtime fallback. | +| Crypto callback | `WOLFSSL_SILABS_CRYPTOCB` | Routes operations through the wolfCrypt crypto callback framework by `devId`. Software stays compiled in, so anything the SE cannot do falls back instead of failing. | + +They are mutually exclusive; defining both is a compile error. Both share the +`silabs_aes.c` / `silabs_ecc.c` / `silabs_hash.c` / `silabs_random.c` helpers, +and both pull the SE context members into `Aes`, `ecc_key` and `wc_Sha*` via the +internal `WOLFSSL_SILABS_SE_TYPES` umbrella. + +## Building the direct port To enable support define the following: @@ -15,9 +27,150 @@ To enable support define the following: #define WOLFSSL_SILABS_SE_ACCEL ``` +## Building the crypto callback port + +``` +#define WOLFSSL_SILABS_CRYPTOCB +``` + +That is all that is required: it turns on `WOLF_CRYPTO_CB`, registers the device +from `wolfCrypt_Init()` at `WOLFSSL_SILABS_DEVID` (default `0x5345`), and points +`WC_USE_DEVID` at it so the unmodified `wolfcrypt_test` and `benchmark` exercise +the hardware. Defining `WC_USE_DEVID` yourself overrides that. + +By default every supported engine is offloaded. To offload a subset, define one +or more of these instead, in which case only those are used: + +``` +WOLFSSL_SILABS_CRYPTOCB_TRNG +WOLFSSL_SILABS_CRYPTOCB_HASH +WOLFSSL_SILABS_CRYPTOCB_CIPHER +WOLFSSL_SILABS_CRYPTOCB_CMAC +WOLFSSL_SILABS_CRYPTOCB_ECC +WOLFSSL_SILABS_CRYPTOCB_KDF +``` + +### What is offloaded + +| Engine | Operations | Notes | +|---|---|---| +| TRNG | seed, and whole blocks with `WOLFSSL_SILABS_TRNG` | | +| Hash | SHA-1, SHA-224, SHA-256; SHA-384 and SHA-512 on Secure Vault High | SHA-512/224 and SHA-512/256 stay in software | +| Cipher | AES ECB/CBC/CTR/GCM/CCM | GCM tags shorter than 16 bytes fall back to software | +| Cipher | ChaCha20-Poly1305 | Secure Vault High only | +| CMAC | AES-CMAC, streaming and one-shot | | +| ECC | ECDSA sign/verify, ECDH, key generation | P-256 always; P-192 and, on Vault High, P-384 and P-521 | +| KDF | HKDF, PBKDF2 | Secure Vault High only | + +There is **no RSA engine**: the Series 2 High Security Engine has no RSA +hardware, so RSA always runs in software. + +There is **no HMAC engine**, and none is needed. wolfCrypt's HMAC gives its +inner and outer hash contexts the `Hmac`'s own `devId`, so every hash block of +an HMAC already runs on the SE through the hash engine. The SE's own HMAC +streaming state has no room to buffer a partial block, so a dedicated engine +would have to re-block the message for no gain. + +AES-CTR is offloaded only for whole-block requests made on a block boundary. +The SE tracks the keystream with an mbedTLS-style offset while wolfCrypt tracks +unused bytes in `aes->tmp`; rather than translate between the two mid-keystream, +partial-block requests fall back to software, which keeps both representations +consistent because the SE updates the counter in `aes->reg` the same way. + +When `WOLFSSL_ARMASM` is defined, AES-CTR is left entirely in software. The +Thumb2 assembly implementation keeps the keystream remainder on a different +contract than the C one, so a stream split between it and the SE does not line +up. Every other engine is unaffected by `WOLFSSL_ARMASM`. + +ChaCha20-Poly1305 is dispatched on the `devId` bound to the cipher object by +`wc_Chacha_SetKey_ex()` or `wc_ChaCha20Poly1305_Init_ex()`. That is the API the +TLS record layer uses, so a connection whose `devId` selects this port has its +ChaCha records offloaded. The legacy one-shot `wc_ChaCha20Poly1305_Encrypt()` +and `_Decrypt()` carry no key object and therefore no `devId`, and stay in +software. + +### TLS 1.3 + +The port is exercised under a real TLS 1.3 handshake, not only the wolfCrypt +known answer vectors. The EFR32xG25 example under `embedded/efr32xg25` in the +[wolfssl-examples](https://github.com/wolfSSL/wolfssl-examples) repository +builds an on-device test (`WOLFSSL_XG25_TLS13`) that runs a client and a server +against each other over an in-memory transport with the SE `devId` set on both, +once per TLS 1.3 cipher suite, followed by application data through the record +layer. `TLS13-AES128-GCM-SHA256`, `TLS13-AES256-GCM-SHA384` and +`TLS13-CHACHA20-POLY1305-SHA256` all pass on an EFR32FG25. + +That path matters because it drives the engines differently from the vector +tests: buffers arrive at arbitrary offsets rather than as aligned stack arrays, +and a single cipher object is reused across many chained records, so state that +is not carried correctly between calls fails rather than passing by luck. The +ChaCha suite is also the only one that reaches the ChaCha20-Poly1305 engine +through the record layer; instrumenting the dispatch during that run counts 28 +requests, all 28 handled by the Secure Element with no software fallback. + +Note that the SE is the only entropy source on the part. `wc_GenerateSeed()` is +wired to the SE TRNG for both ports, so a software DRBG can still be seeded when +an operation falls back; without that an `WC_RNG` built with `INVALID_DEVID` has +no seed at all and fails with `RNG_FAILURE_E`. + +### Secure Vault key management + +On a Secure Vault High part the SE can hold keys the application never sees. A +*wrapped* key is encrypted to a device-unique key: the SE uses it, the +application only ever handles the wrapped blob. A *built-in* key lives in an SE +slot and is named rather than supplied. Either kind binds to an ordinary `Aes` +or `ecc_key`, after which the normal wolfCrypt calls run on the SE against it: + +```c +byte wrapped[64]; +word32 wrappedSz = (word32)sizeof(wrapped); +Aes aes; + +wc_SilabsSe_AesGenerateWrappedKey(256, wrapped, &wrappedSz); +wc_AesInit(&aes, NULL, WOLFSSL_SILABS_DEVID); +wc_SilabsSe_AesUseWrappedKey(&aes, wrapped, wrappedSz, 256); +/* wc_AesGcmEncrypt() and friends now run on the SE with that key */ +``` + +The ECC equivalents are `wc_SilabsSe_EccGetWrappedKeySize()`, +`wc_SilabsSe_EccGenerateWrappedKey()` (which can also export the public point +as X||Y) and `wc_SilabsSe_EccUseWrappedKey()`. Built-in slots bind with +`wc_SilabsSe_AesUseBuiltInKey()` and `wc_SilabsSe_EccUseBuiltInKey()`, taking a +slot such as `SL_SE_KEY_SLOT_APPLICATION_ATTESTATION_KEY` or +`SL_SE_KEY_SLOT_APPLICATION_AES_128_KEY`. + +Two things to keep in mind: + +* The key descriptor **references** the caller's wrapped buffer rather than + copying it, so that buffer must outlive the `Aes` / `ecc_key` bound to it. +* Wrapped keys need Secure Vault High. The built-in slots do not. On a Vault + Mid part the wrapped-key functions compile out; define + `WOLFSSL_SILABS_NO_VAULT_KEYS` to drop their prototypes too. + +Generating a key into an `ecc_key` that is already bound to device-resident +material returns `BAD_FUNC_ARG` rather than silently discarding the binding; +use `wc_SilabsSe_EccGenerateWrappedKey()` for that. + +### Host compile test + +`./configure --enable-silabs-cryptocb` builds the port on a host with no +Simplicity SDK installed, by swapping the SE Manager headers for +`wolfcrypt/src/port/silabs/silabs_shim.h` (`WOLFSSL_SILABS_HOST_TEST`). Every +stub returns `SL_STATUS_NOT_SUPPORTED`, which the port maps to +`CRYPTOCB_UNAVAILABLE`, so `make check` passes entirely in software while +exercising each engine's decline-and-fall-back path. It is a build gate; +correctness is established on EFR32 silicon. + +The shim models a Secure Vault High Series 2 Config 5 part. Defining +`WOLFSSL_SILABS_HOST_TEST_VAULT_MID` models a Vault Mid part instead, which +compile-tests that every Vault-only path (SHA-384/512, P-384/P-521, +ChaCha20-Poly1305, the SE KDFs and wrapped keys) drops out cleanly. + ## Simplicity Studio Example -For the Silicon Labs Simplicity Studio example see [/IDE/SimplicityStudio/README.md](/IDE/SimplicityStudio/README.md). +The crypto callback port's worked example - a headless `slc-cli` project that runs `wolfcrypt_test`, the benchmark and an optional on-device TLS 1.3 test on an EFR32xG25 - lives in the [wolfssl-examples](https://github.com/wolfSSL/wolfssl-examples) repository under `embedded/efr32xg25`, alongside the other hardware-port examples. + +For the older direct port (`WOLFSSL_SILABS_SE_ACCEL`) see [/IDE/SimplicityStudio/README.md](/IDE/SimplicityStudio/README.md). ## Caveats @@ -43,6 +196,73 @@ If a different OS is used with multi-threading, additional mutex protection may See our [benchmarks](https://www.wolfssl.com/docs/benchmarks/) on the wolfSSL website. +### Crypto callback port, EFR32xG25 + +Measured with the `efr32xg25` example on an **EFR32FG25B222F1920IM56** (Secure Vault High, SE firmware 2.2.0), wolfSSL 5.9.2. Because `WOLFSSL_SILABS_CRYPTOCB` sets `WC_USE_DEVID`, the stock benchmark runs every algorithm twice and labels the rows `HW` and `SW`, so each software/Secure Element pair comes from a single run on one part. + +Both builds define the `--enable-harden` options (`ECC_TIMING_RESISTANT`, `WC_RSA_BLINDING`), so these are hardened numbers. They cost nothing measurable in this configuration, which routes P-256 through the already constant-time `WOLFSSL_SP_MATH_ALL`/`WOLFSSL_HAVE_SP_ECC` path: every ECC and RSA row moved by less than 0.3% when the options were added. + +The two software columns are two builds of that same project. **Software (C)** is the default: portable C for the symmetric and hash algorithms. **Software (Thumb2 asm)** additionally defines `WOLFSSL_ARMASM` (see the commented block in that example's `user_settings.h`), which brings in wolfSSL's Cortex-M Thumb2 assembly. Both builds already use `WOLFSSL_SP_ARM_CORTEX_M_ASM` for ECC and RSA, which is why the ECC rows are identical -- there is no plain-C ECC baseline here to beat. + +| Algorithm | Software (C) | Software (Thumb2 asm) | Secure Element | SE vs C | SE vs asm | +|---|---|---|---|---|---| +| RNG SHA-256 DRBG | 281 KiB/s | 408 KiB/s | **89 KiB/s** | **0.3x** | **0.2x** | +| AES-128-CBC-enc | 856 KiB/s | 986 KiB/s | **2.63 MiB/s** | **3.2x** | **2.7x** | +| AES-128-CBC-dec | 854 KiB/s | 985 KiB/s | **2.65 MiB/s** | **3.2x** | **2.8x** | +| AES-192-CBC-enc | 722 KiB/s | 826 KiB/s | **2.60 MiB/s** | **3.7x** | **3.2x** | +| AES-192-CBC-dec | 722 KiB/s | 820 KiB/s | **2.59 MiB/s** | **3.7x** | **3.2x** | +| AES-256-CBC-enc | 627 KiB/s | 710 KiB/s | **2.58 MiB/s** | **4.2x** | **3.7x** | +| AES-256-CBC-dec | 626 KiB/s | 712 KiB/s | **2.59 MiB/s** | **4.2x** | **3.7x** | +| AES-128-GCM-enc | 293 KiB/s | 629 KiB/s | **2.37 MiB/s** | **8.3x** | **3.9x** | +| AES-128-GCM-dec | 291 KiB/s | 626 KiB/s | **2.26 MiB/s** | **8.0x** | **3.7x** | +| AES-192-GCM-enc | 275 KiB/s | 558 KiB/s | **2.38 MiB/s** | **8.9x** | **4.4x** | +| AES-192-GCM-dec | 274 KiB/s | 556 KiB/s | **2.24 MiB/s** | **8.4x** | **4.1x** | +| AES-256-GCM-enc | 260 KiB/s | 502 KiB/s | **2.38 MiB/s** | **9.4x** | **4.8x** | +| AES-256-GCM-dec | 258 KiB/s | 501 KiB/s | **2.23 MiB/s** | **8.8x** | **4.6x** | +| AES-128-GCM-enc-no_AAD | 296 KiB/s | 632 KiB/s | **2.44 MiB/s** | **8.4x** | **3.9x** | +| AES-128-GCM-dec-no_AAD | 294 KiB/s | 630 KiB/s | **2.28 MiB/s** | **7.9x** | **3.7x** | +| AES-192-GCM-enc-no_AAD | 278 KiB/s | 560 KiB/s | **2.41 MiB/s** | **8.9x** | **4.4x** | +| AES-192-GCM-dec-no_AAD | 276 KiB/s | 559 KiB/s | **2.30 MiB/s** | **8.5x** | **4.2x** | +| AES-256-GCM-enc-no_AAD | 262 KiB/s | 505 KiB/s | **2.35 MiB/s** | **9.2x** | **4.8x** | +| AES-256-GCM-dec-no_AAD | 261 KiB/s | 503 KiB/s | **2.23 MiB/s** | **8.8x** | **4.5x** | +| AES-128-ECB-enc | 876 KiB/s | 992 KiB/s | **2.84 MiB/s** | **3.3x** | **2.9x** | +| AES-128-ECB-dec | 882 KiB/s | 988 KiB/s | **2.77 MiB/s** | **3.2x** | **2.9x** | +| AES-192-ECB-enc | 737 KiB/s | 824 KiB/s | **2.74 MiB/s** | **3.8x** | **3.4x** | +| AES-192-ECB-dec | 741 KiB/s | 833 KiB/s | **2.79 MiB/s** | **3.9x** | **3.4x** | +| AES-256-ECB-enc | 638 KiB/s | 711 KiB/s | **2.70 MiB/s** | **4.3x** | **3.9x** | +| AES-256-ECB-dec | 640 KiB/s | 710 KiB/s | **2.71 MiB/s** | **4.3x** | **3.9x** | +| AES-128-CTR | 852 KiB/s | 977 KiB/s | **2.63 MiB/s** | **3.2x** | n/a | +| AES-192-CTR | 720 KiB/s | 820 KiB/s | **2.62 MiB/s** | **3.7x** | n/a | +| AES-256-CTR | 625 KiB/s | 705 KiB/s | **2.63 MiB/s** | **4.3x** | n/a | +| AES-CCM-enc | 426 KiB/s | 460 KiB/s | **2.20 MiB/s** | **5.3x** | **4.9x** | +| AES-CCM-dec | 425 KiB/s | 459 KiB/s | **2.14 MiB/s** | **5.1x** | **4.8x** | +| AES-CCM-enc-no_AAD | 426 KiB/s | 460 KiB/s | **2.20 MiB/s** | **5.3x** | **4.9x** | +| AES-CCM-dec-no_AAD | 425 KiB/s | 459 KiB/s | **2.11 MiB/s** | **5.1x** | **4.7x** | +| SHA-1 | 3.91 MiB/s | 3.92 MiB/s | **3.63 MiB/s** | **0.9x** | **0.9x** | +| SHA-224 | 1.23 MiB/s | 1.99 MiB/s | **3.67 MiB/s** | **3.0x** | **1.8x** | +| SHA-256 | 1.23 MiB/s | 1.99 MiB/s | **3.67 MiB/s** | **3.0x** | **1.8x** | +| AES-128-CMAC | 793 KiB/s | 856 KiB/s | **2.45 MiB/s** | **3.2x** | **2.9x** | +| AES-256-CMAC | 593 KiB/s | 640 KiB/s | **2.40 MiB/s** | **4.1x** | **3.8x** | +| HMAC-SHA-1 | 3.87 MiB/s | 3.87 MiB/s | **3.01 MiB/s** | **0.8x** | **0.8x** | +| HMAC-SHA-224 | 1.22 MiB/s | 1.97 MiB/s | **3.04 MiB/s** | **2.5x** | **1.5x** | +| HMAC-SHA-256 | 1.22 MiB/s | 1.97 MiB/s | **3.04 MiB/s** | **2.5x** | **1.5x** | +| ECC 256 key gen | 125.4 ops/s | 125.3 ops/s | **180.6 ops/s** | **1.4x** | **1.4x** | +| ECDHE 256 agree | 65.4 ops/s | 65.4 ops/s | **179.8 ops/s** | **2.7x** | **2.7x** | +| ECDSA 256 sign | 67.8 ops/s | 67.8 ops/s | **173.4 ops/s** | **2.6x** | **2.6x** | +| ECDSA 256 verify | 41.9 ops/s | 41.9 ops/s | **163.8 ops/s** | **3.9x** | **3.9x** | + +Rows where the Secure Element is **slower** are reported as measured. SHA-1 and HMAC-SHA-1 are cheap enough on a 78 MHz Cortex-M33 that the SE mailbox round trip costs more than it saves at this message size. The **RNG** row is the widest gap and the least surprising: the `HW` column asks the SE for every byte, while the `SW` column is a SHA-256 DRBG seeded once from the same SE TRNG. Seeding a DRBG is the normal way to use a hardware entropy source, and it is what wolfSSL does by default -- the `HW` row measures raw TRNG throughput, not the speed of getting random data. Everything else gains, most of all **AES-GCM** at up to **9.4x**. + +The RNG row is also the only one that moves appreciably between runs. The DRBG reseeds from the SE TRNG at a fixed generate count, and one reseed is expensive at 89 KiB/s, so the software figure swings by roughly 15% depending on whether a reseed falls inside the one-second measurement window. + +**AES-CTR** shows `n/a` against the assembly build because the port declines CTR when `WOLFSSL_ARMASM` is defined (see above), so that build has no Secure Element CTR number to compare. + +**ChaCha20-Poly1305** has no paired row: the benchmark drives it through the one-shot API, which carries no `devId` and so always runs in software. The pre-keyed API the TLS record layer uses does carry one, and that path is offloaded. + +**RSA** and **DH** are omitted: Series 2 has no RSA hardware, so they measure identically in both columns. **SHA-384/512** likewise: the SE supports them on Secure Vault High, but the SE context does not survive the context copy that `wc_ShaXXXGetHash()` performs, so they are left to software until that is implemented. Measurement showed no throughput gain over software for either. + +### Direct port, EFR32MG21 + ``` RNG 2 MB took 1.004 seconds, 1.897 MB/s AES-128-CBC-enc 5 MB took 1.001 seconds, 4.902 MB/s diff --git a/wolfcrypt/src/port/silabs/silabs_aes.c b/wolfcrypt/src/port/silabs/silabs_aes.c index 10b6995514..edeeff64bb 100644 --- a/wolfcrypt/src/port/silabs/silabs_aes.c +++ b/wolfcrypt/src/port/silabs/silabs_aes.c @@ -28,7 +28,7 @@ #include -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include @@ -36,45 +36,34 @@ #include -int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, - const byte* iv, int dir) +/* Build a plaintext-key SE key descriptor for keylen bytes held at key. The + * descriptor points at the caller's buffer, so that buffer must outlive it. + * Shared by the direct port below and by the crypto callback port. */ +int silabs_aes_init_key_desc(silabs_aes_t* ctx, const byte* key, word32 keylen) { sl_se_command_context_t cc = SL_SE_COMMAND_CONTEXT_INIT; int ret = 0; - (void)dir; - if (aes == NULL || userKey == NULL || keylen > sizeof(aes->key)) { + if (ctx == NULL || key == NULL) { return BAD_FUNC_ARG; } - ret = sl_se_init(); - if (ret != SL_STATUS_OK) { - return WC_HW_E; - } - - XMEMSET(aes, 0, sizeof(*aes)); - - ret = wc_AesSetIV(aes, iv); - if (ret != 0) - return ret; - aes->rounds = keylen/4 + 6; - aes->ctx.cmd_ctx = cc; - - XMEMSET(&(aes->ctx.key), 0, sizeof(sl_se_key_descriptor_t)); + ctx->cmd_ctx = cc; + XMEMSET(&(ctx->key), 0, sizeof(sl_se_key_descriptor_t)); + ctx->key.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_PLAINTEXT; - aes->ctx.key.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_PLAINTEXT; - switch(keylen) { + switch (keylen) { case 128/8: - aes->ctx.key.type = SL_SE_KEY_TYPE_AES_128; + ctx->key.type = SL_SE_KEY_TYPE_AES_128; break; #ifdef WOLFSSL_AES_192 case 192/8: - aes->ctx.key.type = SL_SE_KEY_TYPE_AES_192; + ctx->key.type = SL_SE_KEY_TYPE_AES_192; break; #endif #ifdef WOLFSSL_AES_256 case 256/8: - aes->ctx.key.type = SL_SE_KEY_TYPE_AES_256; + ctx->key.type = SL_SE_KEY_TYPE_AES_256; break; #endif default: @@ -83,134 +72,160 @@ int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, } if (ret == 0) { - XMEMCPY(aes->key, userKey, keylen); - aes->ctx.key.storage.location.buffer.pointer = (void*)aes->key; - aes->ctx.key.storage.location.buffer.size = keylen; - aes->ctx.key.size = keylen; - /* Mark key installed so the shared aes.c mode guards accept this - * context. */ - aes->keyInstalled = 1; + ctx->key.storage.location.buffer.pointer = (void*)key; + ctx->key.storage.location.buffer.size = keylen; + ctx->key.size = keylen; } return ret; } -#ifdef HAVE_AES_ECB -int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) +/* Raw ECB. sz must already be a multiple of the block size; callers check. */ +/* Raw-status ECB: negative for an argument error, otherwise the SE status + * unchanged so an unsupported command can be told from a hardware failure. + * The callback port needs that to fall back; the wrapper below does not. */ +int silabs_aes_ecb_status(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir) { - sl_status_t status; if ((in == NULL) || (out == NULL) || (aes == NULL)) { return BAD_FUNC_ARG; } - if ((sz % WC_AES_BLOCK_SIZE) != 0) { - return BAD_LENGTH_E; - } - status = sl_se_aes_crypt_ecb( + return (int)sl_se_aes_crypt_ecb( &(aes->ctx.cmd_ctx), &(aes->ctx.key), - SL_SE_ENCRYPT, + dir, sz, in, out); +} + +int silabs_aes_ecb(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir) +{ + int status = silabs_aes_ecb_status(aes, out, in, sz, dir); + + if (status < 0) { + return status; + } return (status != SL_STATUS_OK) ? WC_HW_E : 0; } -int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) +/* Raw CBC. The working IV is aes->reg, updated in place by the SE. */ +/* Raw-status CBC; see silabs_aes_ecb_status() for the return convention. */ +int silabs_aes_cbc_status(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir) { - sl_status_t status; if ((in == NULL) || (out == NULL) || (aes == NULL)) { return BAD_FUNC_ARG; } - if ((sz % WC_AES_BLOCK_SIZE) != 0) { - return BAD_LENGTH_E; - } - status = sl_se_aes_crypt_ecb( + return (int)sl_se_aes_crypt_cbc( &(aes->ctx.cmd_ctx), &(aes->ctx.key), - SL_SE_DECRYPT, + dir, sz, + (uint8_t*)aes->reg, in, out); +} + +int silabs_aes_cbc(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir) +{ + int status = silabs_aes_cbc_status(aes, out, in, sz, dir); + + if (status < 0) { + return status; + } return (status != SL_STATUS_OK) ? WC_HW_E : 0; } -#endif /* HAVE_AES_ECB */ -#ifdef WOLFSSL_AES_DIRECT -int wc_AesEncrypt(Aes* aes, const byte* inBlock, byte* outBlock) + +#if defined(WOLFSSL_SILABS_SE_ACCEL) +/* Below this point the SE replaces the software implementation outright. The + * crypto callback port leaves the software versions in place and reaches the + * SE through the helpers above instead. */ + +int wc_AesSetKey(Aes* aes, const byte* userKey, word32 keylen, + const byte* iv, int dir) { - sl_status_t status; - if ((inBlock == NULL) || (outBlock == NULL) || (aes == NULL)) { + int ret = 0; + (void)dir; + + if (aes == NULL || userKey == NULL || keylen > sizeof(aes->key)) { return BAD_FUNC_ARG; } - status = sl_se_aes_crypt_ecb( - &(aes->ctx.cmd_ctx), - &(aes->ctx.key), - SL_SE_ENCRYPT, - WC_AES_BLOCK_SIZE, - inBlock, - outBlock); - return (status != SL_STATUS_OK) ? WC_HW_E : 0; + ret = sl_se_init(); + if (ret != SL_STATUS_OK) { + return WC_HW_E; + } + + XMEMSET(aes, 0, sizeof(*aes)); + + ret = wc_AesSetIV(aes, iv); + if (ret != 0) + return ret; + aes->rounds = keylen/4 + 6; + + XMEMCPY(aes->key, userKey, keylen); + ret = silabs_aes_init_key_desc(&(aes->ctx), (const byte*)aes->key, keylen); + if (ret == 0) { + /* Mark key installed so the shared aes.c mode guards accept this + * context. */ + aes->keyInstalled = 1; + } + + return ret; } -int wc_AesDecrypt(Aes* aes, const byte* inBlock, byte* outBlock) +#ifdef HAVE_AES_ECB +int wc_AesEcbEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - sl_status_t status; - if ((inBlock == NULL) || (outBlock == NULL) || (aes == NULL)) { - return BAD_FUNC_ARG; + if ((sz % WC_AES_BLOCK_SIZE) != 0) { + return BAD_LENGTH_E; } + return silabs_aes_ecb(aes, out, in, sz, SL_SE_ENCRYPT); +} - status = sl_se_aes_crypt_ecb( - &(aes->ctx.cmd_ctx), - &(aes->ctx.key), - SL_SE_DECRYPT, - WC_AES_BLOCK_SIZE, - inBlock, - outBlock); - return (status != SL_STATUS_OK) ? WC_HW_E : 0; +int wc_AesEcbDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) +{ + if ((sz % WC_AES_BLOCK_SIZE) != 0) { + return BAD_LENGTH_E; + } + return silabs_aes_ecb(aes, out, in, sz, SL_SE_DECRYPT); +} +#endif /* HAVE_AES_ECB */ + +#ifdef WOLFSSL_AES_DIRECT +int wc_AesEncrypt(Aes* aes, const byte* inBlock, byte* outBlock) +{ + return silabs_aes_ecb(aes, outBlock, inBlock, WC_AES_BLOCK_SIZE, + SL_SE_ENCRYPT); +} + +int wc_AesDecrypt(Aes* aes, const byte* inBlock, byte* outBlock) +{ + return silabs_aes_ecb(aes, outBlock, inBlock, WC_AES_BLOCK_SIZE, + SL_SE_DECRYPT); } #endif /* WOLFSSL_AES_DIRECT */ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - sl_status_t status; - if ((in == NULL) || (out == NULL) || (aes == NULL)) { - return BAD_FUNC_ARG; - } - - status = sl_se_aes_crypt_cbc( - &(aes->ctx.cmd_ctx), - &(aes->ctx.key), - SL_SE_ENCRYPT, - sz, - (uint8_t*)aes->reg, - in, - out); - return (status != SL_STATUS_OK) ? WC_HW_E : 0; + return silabs_aes_cbc(aes, out, in, sz, SL_SE_ENCRYPT); } int wc_AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz) { - sl_status_t status; - if ((in == NULL) || (out == NULL) || (aes == NULL)) { - return BAD_FUNC_ARG; - } - - status = sl_se_aes_crypt_cbc( - &(aes->ctx.cmd_ctx), - &(aes->ctx.key), - SL_SE_DECRYPT, - sz, - (uint8_t*)aes->reg, - in, - out); - return (status != SL_STATUS_OK) ? WC_HW_E : 0; + return silabs_aes_cbc(aes, out, in, sz, SL_SE_DECRYPT); } +#endif /* WOLFSSL_SILABS_SE_ACCEL */ + #ifdef HAVE_AESGCM -int wc_AesGcmEncrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, +int wc_AesGcmEncrypt_silabs_status (Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz) @@ -235,10 +250,23 @@ int wc_AesGcmEncrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, authTagSz, authTag); + return (int)status; +} + +int wc_AesGcmEncrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, + const byte* iv, word32 ivSz, + byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz) +{ + int status = wc_AesGcmEncrypt_silabs_status(aes, out, in, sz, iv, ivSz, authTag, authTagSz, authIn, authInSz); + + if (status < 0) { + return status; + } return (status != SL_STATUS_OK) ? AES_GCM_AUTH_E : 0; } -int wc_AesGcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, +int wc_AesGcmDecrypt_silabs_status (Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, const byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz) @@ -262,6 +290,19 @@ int wc_AesGcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, authTagSz, (byte*)authTag); + return (int)status; +} + +int wc_AesGcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, + const byte* iv, word32 ivSz, + const byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz) +{ + int status = wc_AesGcmDecrypt_silabs_status(aes, out, in, sz, iv, ivSz, authTag, authTagSz, authIn, authInSz); + + if (status < 0) { + return status; + } return (status != SL_STATUS_OK) ? AES_GCM_AUTH_E : 0; } @@ -269,7 +310,7 @@ int wc_AesGcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, #ifdef HAVE_AESCCM -int wc_AesCcmEncrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, +int wc_AesCcmEncrypt_silabs_status (Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz) @@ -307,10 +348,23 @@ int wc_AesCcmEncrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, authTagSz ); - return (status != SL_STATUS_OK) ? AES_GCM_AUTH_E : 0; + return (int)status; } -int wc_AesCcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, +int wc_AesCcmEncrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, + const byte* iv, word32 ivSz, + byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz) +{ + int status = wc_AesCcmEncrypt_silabs_status(aes, out, in, sz, iv, ivSz, authTag, authTagSz, authIn, authInSz); + + if (status < 0) { + return status; + } + return (status != SL_STATUS_OK) ? AES_CCM_AUTH_E : 0; +} + +int wc_AesCcmDecrypt_silabs_status (Aes* aes, byte* out, const byte* in, word32 sz, const byte* iv, word32 ivSz, const byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz) @@ -347,9 +401,22 @@ int wc_AesCcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, (byte*)authTag, authTagSz); - return (status != SL_STATUS_OK) ? AES_GCM_AUTH_E : 0; + return (int)status; +} + +int wc_AesCcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, + const byte* iv, word32 ivSz, + const byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz) +{ + int status = wc_AesCcmDecrypt_silabs_status(aes, out, in, sz, iv, ivSz, authTag, authTagSz, authIn, authInSz); + + if (status < 0) { + return status; + } + return (status != SL_STATUS_OK) ? AES_CCM_AUTH_E : 0; } #endif /* HAVE_AESCCM */ -#endif /* WOLFSSL_SILABS_SE_ACCEL */ +#endif /* WOLFSSL_SILABS_SE_TYPES */ diff --git a/wolfcrypt/src/port/silabs/silabs_cb_cipher.c b/wolfcrypt/src/port/silabs/silabs_cb_cipher.c new file mode 100644 index 0000000000..fba11e7528 --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_cb_cipher.c @@ -0,0 +1,469 @@ +/* silabs_cb_cipher.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 + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SILABS_CRYPTOCB) && defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) + +#include +#include + +#ifndef NO_AES +#include +#include +#endif + +/* silabs_hash.h pulls in em_device.h (or the host-test shim), which is what + * defines _SILICON_LABS_SECURITY_FEATURE for the Vault checks below. */ +#include + +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) +#include +#endif + +/* ChaCha20-Poly1305 is a Secure Vault High feature; on a Vault Mid part the SE + * has no ChaCha20 key type and the AEAD stays in software. */ +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && \ + defined(_SILICON_LABS_SECURITY_FEATURE) && \ + (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) + #define WOLFSSL_SILABS_CHACHA_POLY +#endif + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#ifndef NO_AES + +/* Point the SE key descriptor at aes->devKey, the raw key wc_AesSetKey keeps + * for offload alongside the expanded schedule. It lives as long as the Aes. */ +static int silabs_cipher_setkey(Aes* aes) +{ + if (aes == NULL) { + return BAD_FUNC_ARG; + } + /* A wrapped or built-in key was bound by wc_SilabsSe_AesUse*Key(); the + * descriptor already names it and there is no plaintext key to rebuild + * from. */ + if (aes->ctx.keySet) { + return 0; + } + if (aes->keylen <= 0 || (word32)aes->keylen > sizeof(aes->devKey)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + return silabs_aes_init_key_desc(&(aes->ctx), (const byte*)aes->devKey, + (word32)aes->keylen); +} + +#ifdef WOLFSSL_AES_COUNTER +/* AES-CTR. The SE tracks the keystream with an mbedTLS-style nc_off, wolfCrypt + * with aes->left over aes->tmp. Rather than translate mid-keystream, offload + * only whole blocks on a block boundary; the rest falls back to software, which + * stays consistent because the SE updates aes->reg the same way. */ +static int silabs_cipher_ctr(wc_CryptoInfo* info) +{ +#ifdef WOLFSSL_ARMASM + /* The Thumb2 assembly keeps the keystream remainder on a different + * contract than the C code, so a stream split between it and the SE does + * not line up (confirmed on an EFR32FG25). Leave CTR to software. */ + (void)info; + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); +#else + Aes* aes = info->cipher.aesctr.aes; + unsigned char streamBlock[SLI_SE_AES_CTR_NUM_BLOCKS_BUFFERED * + SL_SE_AES_BLOCK_SIZE]; + uint32_t ncOff = 0; + sl_status_t status; + int ret; + + if (aes == NULL || info->cipher.aesctr.out == NULL || + info->cipher.aesctr.in == NULL) { + return BAD_FUNC_ARG; + } + if (aes->left != 0 || + (info->cipher.aesctr.sz % WC_AES_BLOCK_SIZE) != 0) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + ret = silabs_cipher_setkey(aes); + if (ret != 0) { + return ret; + } + + XMEMSET(streamBlock, 0, sizeof(streamBlock)); + status = sl_se_aes_crypt_ctr( + &(aes->ctx.cmd_ctx), + &(aes->ctx.key), + info->cipher.aesctr.sz, + &ncOff, + (unsigned char*)aes->reg, + streamBlock, + info->cipher.aesctr.in, + info->cipher.aesctr.out); + + ForceZero(streamBlock, sizeof(streamBlock)); + + return silabs_cb_status((int)status); +#endif /* WOLFSSL_ARMASM */ +} +#endif /* WOLFSSL_AES_COUNTER */ + +#endif /* !NO_AES */ + +#ifdef WOLFSSL_SILABS_CHACHA_POLY +/* ChaCha20-Poly1305 AEAD. The one-shot API has no key object, so the key + * descriptor and command context are built on the stack per call. */ +static int silabs_cipher_chachapoly(wc_CryptoInfo* info) +{ + sl_se_command_context_t cmd = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_descriptor_t key; + sl_status_t status; + const byte* inKey; + int ret; + + inKey = info->cipher.enc ? info->cipher.chacha20_poly1305_enc.inKey + : info->cipher.chacha20_poly1305_dec.inKey; + if (inKey == NULL) { + return BAD_FUNC_ARG; + } + + XMEMSET(&key, 0, sizeof(key)); + key.type = SL_SE_KEY_TYPE_CHACHA20; + key.size = CHACHA20_POLY1305_AEAD_KEYSIZE; + key.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_PLAINTEXT; + key.storage.location.buffer.pointer = (void*)inKey; + key.storage.location.buffer.size = CHACHA20_POLY1305_AEAD_KEYSIZE; + + if (info->cipher.enc) { + status = sl_se_chacha20_poly1305_encrypt_and_tag( + &cmd, &key, + info->cipher.chacha20_poly1305_enc.inSz, + info->cipher.chacha20_poly1305_enc.inIV, + info->cipher.chacha20_poly1305_enc.inAAD, + info->cipher.chacha20_poly1305_enc.inAADSz, + info->cipher.chacha20_poly1305_enc.in, + info->cipher.chacha20_poly1305_enc.out, + info->cipher.chacha20_poly1305_enc.outAuthTag); + } + else { + status = sl_se_chacha20_poly1305_auth_decrypt( + &cmd, &key, + info->cipher.chacha20_poly1305_dec.inSz, + info->cipher.chacha20_poly1305_dec.inIV, + info->cipher.chacha20_poly1305_dec.inAAD, + info->cipher.chacha20_poly1305_dec.inAADSz, + info->cipher.chacha20_poly1305_dec.in, + info->cipher.chacha20_poly1305_dec.out, + info->cipher.chacha20_poly1305_dec.inAuthTag); + /* On a real failure clear the output, as wc_ChaCha20Poly1305_Decrypt + * does - its own ForceZero never runs for this path. A decline is + * excluded: the SE did not touch the buffer and AEAD may be in place, + * so clearing would destroy the ciphertext software still needs. */ + ret = silabs_cb_status((int)status); + if (ret != 0 && ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + if (info->cipher.chacha20_poly1305_dec.out != NULL && + info->cipher.chacha20_poly1305_dec.inSz > 0) { + ForceZero(info->cipher.chacha20_poly1305_dec.out, + info->cipher.chacha20_poly1305_dec.inSz); + } + if (status == SL_STATUS_INVALID_SIGNATURE) { + return MAC_CMP_FAILED_E; + } + } + return ret; + } + + return silabs_cb_status((int)status); +} +#endif /* WOLFSSL_SILABS_CHACHA_POLY */ + +/* Map a raw SE status for an AEAD. An authentication failure keeps its own + * error; an unsupported command still declines so software can run. */ +static int silabs_cipher_aead_status(int status, int authErr) +{ + if (status >= 0 && (sl_status_t)status == SL_STATUS_INVALID_SIGNATURE) { + return authErr; + } + return silabs_cb_status(status); +} + +/* Decrypt counterpart: on a real failure clear the plaintext, as the software + * AEADs do. A decline is excluded - the SE did not touch the buffer and AEAD + * may be in place, so clearing would destroy the ciphertext. */ +static int silabs_cipher_aead_dec_status(int status, int authErr, + byte* out, word32 sz) +{ + int ret = silabs_cipher_aead_status(status, authErr); + + if (ret != 0 && ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE) && + out != NULL && sz > 0) { + ForceZero(out, sz); + } + + return ret; +} + +/* The Aes a cipher request operates on, or NULL when the type carries none. */ +static Aes* silabs_cipher_aes(const wc_CryptoInfo* info) +{ + switch (info->cipher.type) { +#ifndef NO_AES +#if defined(HAVE_AES_ECB) || defined(WOLFSSL_AES_DIRECT) + case WC_CIPHER_AES_ECB: + return info->cipher.aesecb.aes; +#endif +#ifdef HAVE_AES_CBC + case WC_CIPHER_AES_CBC: + return info->cipher.aescbc.aes; +#endif +#ifdef WOLFSSL_AES_COUNTER + case WC_CIPHER_AES_CTR: + return info->cipher.aesctr.aes; +#endif +#ifdef HAVE_AESGCM + case WC_CIPHER_AES_GCM: + return info->cipher.enc ? info->cipher.aesgcm_enc.aes + : info->cipher.aesgcm_dec.aes; +#endif +#ifdef HAVE_AESCCM + case WC_CIPHER_AES_CCM: + return info->cipher.enc ? info->cipher.aesccm_enc.aes + : info->cipher.aesccm_dec.aes; +#endif +#endif /* !NO_AES */ + default: + return NULL; + } +} + +static int silabs_cipher_dispatch(wc_CryptoInfo* info) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + switch (info->cipher.type) { +#ifndef NO_AES +#if defined(HAVE_AES_ECB) || defined(WOLFSSL_AES_DIRECT) + case WC_CIPHER_AES_ECB: + if (info->cipher.aesecb.aes == NULL) { + return BAD_FUNC_ARG; + } + if ((info->cipher.aesecb.sz % WC_AES_BLOCK_SIZE) != 0) { + return BAD_LENGTH_E; + } + ret = silabs_cipher_setkey(info->cipher.aesecb.aes); + if (ret == 0) { + ret = silabs_aes_ecb_status(info->cipher.aesecb.aes, + info->cipher.aesecb.out, info->cipher.aesecb.in, + info->cipher.aesecb.sz, + info->cipher.enc ? SL_SE_ENCRYPT : SL_SE_DECRYPT); + ret = silabs_cb_status(ret); + } + break; +#endif +#ifdef HAVE_AES_CBC + case WC_CIPHER_AES_CBC: + if (info->cipher.aescbc.aes == NULL) { + return BAD_FUNC_ARG; + } + ret = silabs_cipher_setkey(info->cipher.aescbc.aes); + if (ret == 0) { + ret = silabs_aes_cbc_status(info->cipher.aescbc.aes, + info->cipher.aescbc.out, info->cipher.aescbc.in, + info->cipher.aescbc.sz, + info->cipher.enc ? SL_SE_ENCRYPT : SL_SE_DECRYPT); + ret = silabs_cb_status(ret); + } + break; +#endif +#ifdef WOLFSSL_AES_COUNTER + case WC_CIPHER_AES_CTR: + ret = silabs_cipher_ctr(info); + break; +#endif +#ifdef HAVE_AESGCM + case WC_CIPHER_AES_GCM: + if (info->cipher.enc) { + /* The SE computes only full-length GCM tags. */ + if (info->cipher.aesgcm_enc.authTagSz < + WC_AES_BLOCK_SIZE) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + /* GMAC authenticates AAD with no plaintext, so wolfCrypt passes a + * zero length and NULL in/out. The SE helper requires both, so + * leave that case to software. */ + if (info->cipher.aesgcm_enc.sz == 0 || + info->cipher.aesgcm_enc.in == NULL || + info->cipher.aesgcm_enc.out == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + /* The SE accepts only a 96-bit GCM IV. Decline anything else + * rather than let the helper report it as an auth failure. */ + if (info->cipher.aesgcm_enc.ivSz != GCM_NONCE_MID_SZ) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + ret = silabs_cipher_setkey(info->cipher.aesgcm_enc.aes); + if (ret == 0) { + ret = wc_AesGcmEncrypt_silabs_status( + info->cipher.aesgcm_enc.aes, + 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); + ret = silabs_cipher_aead_status(ret, AES_GCM_AUTH_E); + } + } + else { + if (info->cipher.aesgcm_dec.authTagSz < + WC_AES_BLOCK_SIZE) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (info->cipher.aesgcm_dec.sz == 0 || + info->cipher.aesgcm_dec.in == NULL || + info->cipher.aesgcm_dec.out == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (info->cipher.aesgcm_dec.ivSz != GCM_NONCE_MID_SZ) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + ret = silabs_cipher_setkey(info->cipher.aesgcm_dec.aes); + if (ret == 0) { + ret = wc_AesGcmDecrypt_silabs_status( + info->cipher.aesgcm_dec.aes, + 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); + ret = silabs_cipher_aead_dec_status(ret, + AES_GCM_AUTH_E, info->cipher.aesgcm_dec.out, + info->cipher.aesgcm_dec.sz); + } + } + break; +#endif +#ifdef HAVE_AESCCM + case WC_CIPHER_AES_CCM: + if (info->cipher.enc) { + if (info->cipher.aesccm_enc.sz == 0 || + info->cipher.aesccm_enc.in == NULL || + info->cipher.aesccm_enc.out == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + ret = silabs_cipher_setkey(info->cipher.aesccm_enc.aes); + if (ret == 0) { + ret = wc_AesCcmEncrypt_silabs_status( + info->cipher.aesccm_enc.aes, + 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); + ret = silabs_cipher_aead_status(ret, AES_CCM_AUTH_E); + } + } + else { + if (info->cipher.aesccm_dec.sz == 0 || + info->cipher.aesccm_dec.in == NULL || + info->cipher.aesccm_dec.out == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + ret = silabs_cipher_setkey(info->cipher.aesccm_dec.aes); + if (ret == 0) { + ret = wc_AesCcmDecrypt_silabs_status( + info->cipher.aesccm_dec.aes, + 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); + ret = silabs_cipher_aead_dec_status(ret, + AES_CCM_AUTH_E, info->cipher.aesccm_dec.out, + info->cipher.aesccm_dec.sz); + } + } + break; +#endif +#endif /* !NO_AES */ +#ifdef WOLFSSL_SILABS_CHACHA_POLY + case WC_CIPHER_CHACHA: + ret = silabs_cipher_chachapoly(info); + break; +#endif + default: + break; + } + + return ret; +} + +/* WC_ALGO_TYPE_CIPHER. With a wrapped or built-in key the Aes holds no key + * material, so declining would run software AES on an unset schedule and + * silently produce wrong ciphertext. Fail outright instead. */ +int wc_SilabsCipher(wc_CryptoInfo* info) +{ + int ret; + Aes* aes; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_cipher_dispatch(info); + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + aes = silabs_cipher_aes(info); + if (aes != NULL && aes->ctx.keySet) { + return WC_HW_E; + } + } + + return ret; +} + +#endif /* WOLFSSL_SILABS_CRYPTOCB && WOLFSSL_SILABS_CRYPTOCB_CIPHER */ diff --git a/wolfcrypt/src/port/silabs/silabs_cb_cmac.c b/wolfcrypt/src/port/silabs/silabs_cb_cmac.c new file mode 100644 index 0000000000..a75761e5b7 --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_cb_cmac.c @@ -0,0 +1,284 @@ +/* silabs_cb_cmac.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 + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SILABS_CRYPTOCB) && \ + defined(WOLFSSL_SILABS_CRYPTOCB_CMAC) && defined(WOLFSSL_CMAC) && \ + !defined(NO_AES) + +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +/* Per-Cmac SE state, hung off Cmac.devCtx and released by the free callback. + * The SE multipart context buffers a partial block itself, so wolfCrypt can + * update with any length. */ +typedef struct { + sl_se_command_context_t cmd_ctx; + sl_se_cmac_multipart_context_t cmac_ctx; + silabs_aes_t keyDesc; + byte key[AES_MAX_KEY_SIZE / WOLFSSL_BIT_SIZE]; + word32 keySz; +} silabs_cmac_ctx_t; + +static silabs_cmac_ctx_t* silabs_cmac_ctx(Cmac* cmac) +{ + return (cmac == NULL) ? NULL : (silabs_cmac_ctx_t*)cmac->devCtx; +} + +static void silabs_cmac_free(Cmac* cmac) +{ + silabs_cmac_ctx_t* c = silabs_cmac_ctx(cmac); + + if (c != NULL) { + ForceZero(c, sizeof(*c)); + XFREE(c, cmac->aes.heap, DYNAMIC_TYPE_CMAC); + cmac->devCtx = NULL; + } +} + +/* Take a copy of the key: the SE descriptor references the buffer for the + * lifetime of the multipart operation, and the caller's key may not live + * that long. */ +static int silabs_cmac_start(Cmac* cmac, const byte* key, word32 keySz) +{ + silabs_cmac_ctx_t* c; + sl_status_t status; + int ret; + + if (cmac == NULL || key == NULL) { + return BAD_FUNC_ARG; + } + if (keySz != AES_128_KEY_SIZE && keySz != AES_192_KEY_SIZE && + keySz != AES_256_KEY_SIZE) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + silabs_cmac_free(cmac); + + c = (silabs_cmac_ctx_t*)XMALLOC(sizeof(silabs_cmac_ctx_t), cmac->aes.heap, + DYNAMIC_TYPE_CMAC); + if (c == NULL) { + return MEMORY_E; + } + XMEMSET(c, 0, sizeof(*c)); + XMEMCPY(c->key, key, keySz); + c->keySz = keySz; + + /* Only keyDesc.key is consumed here. The helper is shared with the cipher + * path and also seeds keyDesc.cmd_ctx, but the CMAC multipart calls all + * take c->cmd_ctx, so that context is intentionally left unused. */ + ret = silabs_aes_init_key_desc(&(c->keyDesc), c->key, keySz); + if (ret != 0) { + ForceZero(c, sizeof(*c)); + XFREE(c, cmac->aes.heap, DYNAMIC_TYPE_CMAC); + return ret; + } + + status = sl_se_cmac_multipart_starts(&(c->cmac_ctx), &(c->cmd_ctx), + &(c->keyDesc.key)); + ret = silabs_cb_status((int)status); + if (ret != 0) { + ForceZero(c, sizeof(*c)); + XFREE(c, cmac->aes.heap, DYNAMIC_TYPE_CMAC); + return ret; + } + + cmac->devCtx = c; + + return 0; +} + +/* One-shot: key, message and output in a single call. + * + * wc_AesCmacGenerate_ex() dispatches here BEFORE wc_InitCmac_ex() runs, so + * cmac->devCtx and cmac->aes.heap are still uninitialised caller storage. Keep + * all state on the stack and never touch the Cmac. Declining part-way is safe: + * software redoes the operation from the key and message. */ +static int silabs_cmac_oneshot(wc_CryptoInfo* info) +{ + silabs_cmac_ctx_t c; + byte tag[WC_AES_BLOCK_SIZE]; + sl_status_t status; + int ret; + + if (info->cmac.keySz != AES_128_KEY_SIZE && + info->cmac.keySz != AES_192_KEY_SIZE && + info->cmac.keySz != AES_256_KEY_SIZE) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (*info->cmac.outSz < WC_CMAC_TAG_MIN_SZ || + *info->cmac.outSz > WC_CMAC_TAG_MAX_SZ) { + return BUFFER_E; + } + + XMEMSET(&c, 0, sizeof(c)); + XMEMCPY(c.key, info->cmac.key, info->cmac.keySz); + c.keySz = info->cmac.keySz; + + ret = silabs_aes_init_key_desc(&(c.keyDesc), c.key, c.keySz); + if (ret == 0) { + status = sl_se_cmac_multipart_starts(&(c.cmac_ctx), &(c.cmd_ctx), + &(c.keyDesc.key)); + ret = silabs_cb_status((int)status); + } + if (ret == 0 && info->cmac.in != NULL) { + status = sl_se_cmac_multipart_update(&(c.cmac_ctx), &(c.cmd_ctx), + &(c.keyDesc.key), info->cmac.in, info->cmac.inSz); + ret = silabs_cb_status((int)status); + } + if (ret == 0) { + status = sl_se_cmac_multipart_finish(&(c.cmac_ctx), &(c.cmd_ctx), + &(c.keyDesc.key), tag); + ret = silabs_cb_status((int)status); + } + if (ret == 0) { + XMEMCPY(info->cmac.out, tag, *info->cmac.outSz); + } + + ForceZero(tag, sizeof(tag)); + ForceZero(&c, sizeof(c)); + + return ret; +} + +/* WC_ALGO_TYPE_CMAC and the CMAC arm of WC_ALGO_TYPE_FREE. wolfCrypt drives + * this with key set (start), in set (update), out set (final), or all three + * for the one-shot. */ +int wc_SilabsCmac(wc_CryptoInfo* info) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + silabs_cmac_ctx_t* c; + sl_status_t status; + Cmac* cmac; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type == WC_ALGO_TYPE_FREE) { + silabs_cmac_free((Cmac*)info->free.obj); + /* Return unavailable so wolfSSL still runs its own cleanup. */ + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + if (info->algo_type != WC_ALGO_TYPE_CMAC) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + cmac = info->cmac.cmac; + if (cmac == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + /* The SE does AES-CMAC only. */ + if (info->cmac.type != WC_CMAC_AES) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + /* Dispatch happens before wc_AesCmacGenerate_ex() validates its + * pointer/length pairs, so check them now - a NULL buffer with a positive + * length would otherwise pass as an empty message. Checking before any SE + * state exists also avoids leaving a device context behind. */ + if (info->cmac.in == NULL && info->cmac.inSz > 0) { + return BAD_FUNC_ARG; + } + if (info->cmac.out == NULL && info->cmac.outSz != NULL && + *info->cmac.outSz > 0) { + return BAD_FUNC_ARG; + } + + /* A one-shot arrives with key and output together, before the Cmac has + * been initialised. Handle it without touching the object at all. */ + if (info->cmac.key != NULL && info->cmac.out != NULL && + info->cmac.outSz != NULL) { + return silabs_cmac_oneshot(info); + } + + if (info->cmac.key != NULL) { + ret = silabs_cmac_start(cmac, info->cmac.key, info->cmac.keySz); + if (ret != 0) { + return ret; + } + } + + c = silabs_cmac_ctx(cmac); + if (c == NULL) { + /* No SE state: the key was set before this device took over, so let + * software finish the operation. */ + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + /* From here the SE holds the key and message so far, while the software + * AES/CMAC state was never set up, so a later failure must be terminal + * rather than a decline the core would try to resume. */ + if (info->cmac.in != NULL) { + status = sl_se_cmac_multipart_update(&(c->cmac_ctx), &(c->cmd_ctx), + &(c->keyDesc.key), info->cmac.in, info->cmac.inSz); + ret = silabs_cb_status((int)status); + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ret = WC_HW_E; + } + if (ret != 0) { + return ret; + } + } + + if (info->cmac.out != NULL) { + /* The SE writes a full block, but wolfCrypt allows a truncated tag, + * so finalize into a local block and copy out what was asked for. */ + byte tag[WC_AES_BLOCK_SIZE]; + + if (info->cmac.outSz == NULL || + *info->cmac.outSz < WC_CMAC_TAG_MIN_SZ || + *info->cmac.outSz > WC_CMAC_TAG_MAX_SZ) { + return BUFFER_E; + } + status = sl_se_cmac_multipart_finish(&(c->cmac_ctx), &(c->cmd_ctx), + &(c->keyDesc.key), tag); + ret = silabs_cb_status((int)status); + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ret = WC_HW_E; /* see the note above the update */ + } + if (ret == 0) { + XMEMCPY(info->cmac.out, tag, *info->cmac.outSz); + } + ForceZero(tag, sizeof(tag)); + silabs_cmac_free(cmac); + } + + return ret; +} + +#endif /* WOLFSSL_SILABS_CRYPTOCB && WOLFSSL_SILABS_CRYPTOCB_CMAC && + * WOLFSSL_CMAC && !NO_AES */ diff --git a/wolfcrypt/src/port/silabs/silabs_cb_hash.c b/wolfcrypt/src/port/silabs/silabs_cb_hash.c new file mode 100644 index 0000000000..0dd5643f35 --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_cb_hash.c @@ -0,0 +1,178 @@ +/* silabs_cb_hash.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 + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SILABS_CRYPTOCB) && defined(WOLFSSL_SILABS_CRYPTOCB_HASH) + +#include +#include +#include +#include + +/* Resolve a hash request to the SE context embedded in the caller's wolfCrypt + * hash object, and to the digest length. Returns CRYPTOCB_UNAVAILABLE for + * anything the SE cannot do, so software runs. + * + * SHA-384/512 are always left to software. The SE supports them on Vault High, + * but wc_ShaXXXGetHash() copies the context and finalizes the copy, which the + * SE context does not survive (the SDK-v3 fixups in wc_Sha512Copy() are gated + * on the direct port's macros). Offloading needs copy support first, and an + * EFR32FG25B showed no gain over software. SHA-1/224/256 do offload. */ +static int silabs_hash_resolve(wc_CryptoInfo* info, wc_silabs_sha_t** ctx, + int* digestSz) +{ + void* obj = NULL; + + *ctx = NULL; + *digestSz = 0; + + switch (info->hash.type) { +#ifndef NO_SHA + case WC_HASH_TYPE_SHA: + obj = (void*)info->hash.sha1; + if (obj != NULL) + *ctx = &((wc_Sha*)obj)->silabsCtx; + *digestSz = WC_SHA_DIGEST_SIZE; + break; +#endif +#ifdef WOLFSSL_SHA224 + case WC_HASH_TYPE_SHA224: + obj = (void*)info->hash.sha224; + if (obj != NULL) + *ctx = &((wc_Sha224*)obj)->silabsCtx; + *digestSz = WC_SHA224_DIGEST_SIZE; + break; +#endif +#ifndef NO_SHA256 + case WC_HASH_TYPE_SHA256: + obj = (void*)info->hash.sha256; + if (obj != NULL) + *ctx = &((wc_Sha256*)obj)->silabsCtx; + *digestSz = WC_SHA256_DIGEST_SIZE; + break; +#endif + default: + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + if (*ctx == NULL) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + return 0; +} + +/* Start the SE context on first use. wolfCrypt has no crypto callback hook on + * wc_InitShaXXX, and that call zeroes the object, so "started" is clear on a + * freshly initialized context. */ +static int silabs_hash_start(wc_silabs_sha_t* ctx, int hashType) +{ + int ret = 0; + + if (!ctx->started) { + ret = silabs_cb_status( + wc_silabs_se_hash_init_status(ctx, (enum wc_HashType)hashType)); + if (ret == 0) { + ctx->started = 1; + } + } + + return ret; +} + +/* WC_ALGO_TYPE_HASH. digest == NULL means update, digest != NULL means final. + * A single call may carry both. */ +int wc_SilabsHash(wc_CryptoInfo* info) +{ + int ret; + int digestSz = 0; + wc_silabs_sha_t* ctx = NULL; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + /* A copy needs no SE work: the multipart context is embedded in the + * wolfCrypt object and holds no pointers back into it, so the XMEMCPY that + * runs on a decline suffices. (The SDK v3 context does hold self pointers, + * which sha256.c and friends fix up after the copy.) */ + if (info->algo_type == WC_ALGO_TYPE_COPY) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + if (info->algo_type != WC_ALGO_TYPE_HASH) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + ret = silabs_hash_resolve(info, &ctx, &digestSz); + if (ret != 0) { + return ret; + } + + /* The SE Manager already serializes the mailbox: every sl_se_* command + * goes through sli_se_execute_and_wait(), which takes its own lock. This + * mutex guards something else - the start/update/final sequence below runs + * several commands against one streaming context, and interleaving two of + * those would corrupt it. The one-shot engines (cipher, pk, kdf, cmac) + * issue a single command and so need no lock of their own. */ + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + + /* Declining is only safe before the SE has taken any of the message: after + * that the software context has not seen the consumed input, so a fallback + * would hash the wrong data. Failures must then be terminal. */ + ret = silabs_hash_start(ctx, info->hash.type); + + if (ret == 0 && info->hash.in != NULL) { + ret = silabs_cb_status( + wc_silabs_se_hash_update_status(ctx, info->hash.in, + info->hash.inSz)); + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ret = WC_HW_E; + } + } + + if (ret == 0 && info->hash.digest != NULL) { + ret = silabs_cb_status( + wc_silabs_se_hash_final_status(ctx, info->hash.digest, + (word32)digestSz)); + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + ret = WC_HW_E; + } + /* wolfCrypt leaves a finalized hash ready for reuse. The software + * final re-inits; here clearing the flag restarts the SE context on + * the next update. */ + ctx->started = 0; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +#endif /* WOLFSSL_SILABS_CRYPTOCB && WOLFSSL_SILABS_CRYPTOCB_HASH */ diff --git a/wolfcrypt/src/port/silabs/silabs_cb_kdf.c b/wolfcrypt/src/port/silabs/silabs_cb_kdf.c new file mode 100644 index 0000000000..11e4eb42e2 --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_cb_kdf.c @@ -0,0 +1,182 @@ +/* silabs_cb_kdf.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 + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SILABS_CRYPTOCB) && defined(WOLFSSL_SILABS_CRYPTOCB_KDF) + +#include +#include +/* for the SE types and _SILICON_LABS_SECURITY_FEATURE */ +#include + +/* The SE key derivation commands are a Secure Vault High feature. On a Vault + * Mid part they do not exist and both KDFs stay in software (where HKDF still + * benefits from the offloaded HMAC/SHA path). */ +#if defined(_SILICON_LABS_SECURITY_FEATURE) && \ + (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) + #define WOLFSSL_SILABS_KDF_HW +#endif + +#ifdef WOLFSSL_SILABS_KDF_HW + +#ifndef WOLFSSL_SILABS_HOST_TEST + #include +#endif + +/* Describe a plaintext symmetric key held in a caller buffer. */ +static void silabs_kdf_plain_key(sl_se_key_descriptor_t* desc, const void* buf, + word32 sz) +{ + XMEMSET(desc, 0, sizeof(*desc)); + desc->type = SL_SE_KEY_TYPE_SYMMETRIC; + desc->size = sz; + desc->storage.method = SL_SE_KEY_STORAGE_EXTERNAL_PLAINTEXT; + desc->storage.location.buffer.pointer = (void*)buf; + desc->storage.location.buffer.size = sz; +} + +#if defined(HAVE_HKDF) && !defined(NO_HMAC) +static int silabs_kdf_hkdf(wc_CryptoInfo* info) +{ + sl_se_command_context_t cmd = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_descriptor_t inKey; + sl_se_key_descriptor_t outKey; + sl_status_t status; + int seHash; + + if (info->kdf.hkdf.out == NULL) { + return BAD_FUNC_ARG; + } + /* wc_HKDF accepts zero-length keying material with a NULL pointer, which + * an SE key descriptor cannot express - decline rather than reject a call + * the public API allows. NULL with a non-zero length is a real error. */ + if (info->kdf.hkdf.inKey == NULL) { + if (info->kdf.hkdf.inKeySz != 0) { + return BAD_FUNC_ARG; + } + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + seHash = silabs_cb_hash_type(info->kdf.hkdf.hashType, NULL); + if (seHash == SL_SE_HASH_NONE) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + silabs_kdf_plain_key(&inKey, info->kdf.hkdf.inKey, info->kdf.hkdf.inKeySz); + silabs_kdf_plain_key(&outKey, info->kdf.hkdf.out, info->kdf.hkdf.outSz); + + status = sl_se_derive_key_hkdf(&cmd, &inKey, + (sl_se_hash_type_t)seHash, + info->kdf.hkdf.salt, info->kdf.hkdf.saltSz, + info->kdf.hkdf.info, info->kdf.hkdf.infoSz, + &outKey); + + return silabs_cb_status((int)status); +} +#endif /* HAVE_HKDF && !NO_HMAC */ + +#if defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED) +static int silabs_kdf_pbkdf2(wc_CryptoInfo* info) +{ + sl_se_command_context_t cmd = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_descriptor_t inKey; + sl_se_key_descriptor_t outKey; + sl_status_t status; + int seHash; + + if (info->kdf.pbkdf2.output == NULL) { + return BAD_FUNC_ARG; + } + if (info->kdf.pbkdf2.passwd == NULL && info->kdf.pbkdf2.pLen != 0) { + return BAD_FUNC_ARG; + } + /* kLen == 0 is not an error for the software API, only for this engine. */ + if (info->kdf.pbkdf2.pLen < 0 || info->kdf.pbkdf2.sLen < 0 || + info->kdf.pbkdf2.kLen < 0 || info->kdf.pbkdf2.iterations <= 0) { + return BAD_FUNC_ARG; + } + /* An empty password or a zero-length result are both accepted by + * wc_PBKDF2_ex but cannot be handed to the SE; decline so software keeps + * the existing contract. */ + if (info->kdf.pbkdf2.passwd == NULL || info->kdf.pbkdf2.kLen == 0) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + seHash = silabs_cb_hash_type(info->kdf.pbkdf2.hashType, NULL); + if (seHash == SL_SE_HASH_NONE) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + + silabs_kdf_plain_key(&inKey, info->kdf.pbkdf2.passwd, + (word32)info->kdf.pbkdf2.pLen); + silabs_kdf_plain_key(&outKey, info->kdf.pbkdf2.output, + (word32)info->kdf.pbkdf2.kLen); + + status = sl_se_derive_key_pbkdf2(&cmd, &inKey, + (sl_se_pbkdf2_prf_type_t)seHash, + info->kdf.pbkdf2.salt, (size_t)info->kdf.pbkdf2.sLen, + (uint32_t)info->kdf.pbkdf2.iterations, + &outKey); + + return silabs_cb_status((int)status); +} +#endif /* HAVE_PBKDF2 && !NO_HMAC && !NO_PWDBASED */ + +#endif /* WOLFSSL_SILABS_KDF_HW */ + +/* WC_ALGO_TYPE_KDF. */ +int wc_SilabsKdf(wc_CryptoInfo* info) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + if (info == NULL) { + return BAD_FUNC_ARG; + } + +#ifdef WOLFSSL_SILABS_KDF_HW + switch (info->kdf.type) { +#if defined(HAVE_HKDF) && !defined(NO_HMAC) + case WC_KDF_TYPE_HKDF: + /* The SE command is the full extract-then-expand HKDF; the separate + * extract and expand steps have no SE equivalent and stay in + * software. */ + ret = silabs_kdf_hkdf(info); + break; +#endif +#if defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED) + case WC_KDF_TYPE_PBKDF2: + ret = silabs_kdf_pbkdf2(info); + break; +#endif + default: + break; + } +#endif /* WOLFSSL_SILABS_KDF_HW */ + + return ret; +} + +#endif /* WOLFSSL_SILABS_CRYPTOCB && WOLFSSL_SILABS_CRYPTOCB_KDF */ diff --git a/wolfcrypt/src/port/silabs/silabs_cb_pk.c b/wolfcrypt/src/port/silabs/silabs_cb_pk.c new file mode 100644 index 0000000000..f6f10171d7 --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_cb_pk.c @@ -0,0 +1,405 @@ +/* silabs_cb_pk.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 + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SILABS_CRYPTOCB) && \ + defined(WOLFSSL_SILABS_CRYPTOCB_ECC) && defined(HAVE_ECC) + +#include +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +/* Raw r||s pair at the curve size, and the verify scratch that adds the two + * decode halves alongside it. Both exceed the stack budget for a small target, + * so WOLFSSL_SMALL_STACK moves them to the heap. */ +#define SILABS_ECC_RAW_SZ (ECC_MAX_CRYPTO_HW_SIZE * 2) +#define SILABS_ECC_VERIFY_SCRATCH_SZ (ECC_MAX_CRYPTO_HW_SIZE * 4) + + +/* Curves the SE supports. P-384 and P-521 need Secure Vault High; on a Vault + * Mid part those key types do not exist and the curves stay in software. */ +static int silabs_pk_curve_ok(const ecc_key* key) +{ + if (key == NULL || key->dp == NULL) { + return 0; + } + + /* ECC_MAX_CRYPTO_HW_SIZE sizes key_raw and the r||s scratch below, but an + * #elif chain can pick a smaller value for another backend built alongside + * this one (ATECC, TA100). A wider curve would overrun that buffer, so + * decline and let software, which sizes from the curve, handle it. */ + if (key->dp->size > ECC_MAX_CRYPTO_HW_SIZE) { + return 0; + } + + switch (key->dp->id) { +#ifdef SL_SE_KEY_TYPE_ECC_P192 + case ECC_SECP192R1: + return 1; +#endif + case ECC_SECP256R1: + return 1; +#ifdef SL_SE_KEY_TYPE_ECC_P384 + case ECC_SECP384R1: + return 1; +#endif +#ifdef SL_SE_KEY_TYPE_ECC_P521 + case ECC_SECP521R1: + return 1; +#endif + default: + break; + } + + return 0; +} + +/* The SE takes the digest as-is, assuming it is exactly the curve size, and + * does not apply the ECDSA truncation rules. Hand those to software. */ +static int silabs_pk_digest_ok(const ecc_key* key, word32 hashLen) +{ + return (key != NULL) && (key->dp != NULL) && + (hashLen == (word32)key->dp->size); +} + +/* ecc.c rejects an all-zero digest unless WC_ALLOW_ECC_ZERO_HASH is set, but + * the callback runs first, so apply the same policy here. */ +#ifndef WC_ALLOW_ECC_ZERO_HASH +static int silabs_pk_hash_is_zero(const byte* hash, word32 hashLen) +{ + byte acc = 0; + word32 i; + + if (hash == NULL) { + return 0; + } + for (i = 0; i < hashLen; i++) { + acc |= hash[i]; + } + + return (acc == 0); +} +#endif + +/* The SE works on the flat X||Y||D buffer in the key. The direct port fills it + * from the wc_ecc_import_* hooks; the callback port leaves those in software, + * so refresh it here from the key's mp_ints before each operation. */ +static int silabs_pk_load(ecc_key* key, int pub, int priv) +{ + int ret; + int savedType; + + if (key == NULL || key->dp == NULL) { + return BAD_FUNC_ARG; + } + /* A wrapped or built-in key was bound by wc_SilabsSe_EccUse*Key(); the + * descriptor already names it, and key_raw holds no private scalar to + * import. */ + if (key->silabsKeySet) { + return 0; + } + + /* silabs_ecc_import() rewrites key->type from the pub/priv flags. Here it + * only refreshes the SE buffer, so preserve the type: downgrading a key + * that holds a private scalar would break a later private-key export. */ + savedType = key->type; + ret = silabs_ecc_import(key, (word32)key->dp->size, pub, priv); + key->type = savedType; + + return ret; +} + +/* The ecc_key a PK request operates on, or NULL when the type carries none. */ +static ecc_key* silabs_pk_key(const wc_CryptoInfo* info) +{ + switch (info->pk.type) { +#ifdef HAVE_ECC_DHE + case WC_PK_TYPE_EC_KEYGEN: + return info->pk.eckg.key; + case WC_PK_TYPE_ECDH: + return info->pk.ecdh.private_key; +#endif +#ifdef HAVE_ECC_SIGN + case WC_PK_TYPE_ECDSA_SIGN: + return info->pk.eccsign.key; +#endif +#ifdef HAVE_ECC_VERIFY + case WC_PK_TYPE_ECDSA_VERIFY: + return info->pk.eccverify.key; +#endif + default: + return NULL; + } +} + +static int silabs_pk_dispatch(wc_CryptoInfo* info) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + switch (info->pk.type) { +#ifdef HAVE_ECC_DHE + case WC_PK_TYPE_EC_KEYGEN: + if (info->pk.eckg.key == NULL) { + return BAD_FUNC_ARG; + } + /* wc_ecc_make_key_ex sets the curve before dispatching; without it + * there is nothing to tell the SE which key to make. */ + if (!silabs_pk_curve_ok(info->pk.eckg.key)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + /* Generating into a key already bound to device-resident material + * would silently discard that binding. Use + * wc_SilabsSe_EccGenerateWrappedKey() for that instead. */ + if (info->pk.eckg.key->silabsKeySet) { + return BAD_FUNC_ARG; + } + /* Use the curve's field size, not the requested one: + * wc_ecc_make_key_ex() rounds up to the next curve (40 selects P-384), + * so eckg.size would read the SE's key back at the wrong stride. */ + ret = silabs_cb_status(silabs_ecc_make_key_status(info->pk.eckg.key, + info->pk.eckg.key->dp->size)); + break; + + case WC_PK_TYPE_ECDH: + if (info->pk.ecdh.private_key == NULL || + info->pk.ecdh.public_key == NULL) { + return BAD_FUNC_ARG; + } + /* Both objects are imported into fixed-size SE buffers below, so the + * public key's curve has to be checked too, not just the private + * one. */ + if (!silabs_pk_curve_ok(info->pk.ecdh.private_key) || + !silabs_pk_curve_ok(info->pk.ecdh.public_key)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + /* wc_ecc_shared_secret() dispatches here before its own checks, so + * repeat them to return the documented ECC_BAD_ARG_E. */ + if (info->pk.ecdh.private_key->type != ECC_PRIVATEKEY && + info->pk.ecdh.private_key->type != ECC_PRIVATEKEY_ONLY) { + return ECC_BAD_ARG_E; + } + if (info->pk.ecdh.private_key->dp->id != + info->pk.ecdh.public_key->dp->id) { + return ECC_BAD_ARG_E; + } + ret = silabs_pk_load(info->pk.ecdh.private_key, 1, 1); + if (ret == 0) { + ret = silabs_pk_load(info->pk.ecdh.public_key, 1, 0); + } + if (ret == 0) { + ret = silabs_cb_status(silabs_ecc_shared_secret_status( + info->pk.ecdh.private_key, info->pk.ecdh.public_key, + info->pk.ecdh.out, info->pk.ecdh.outlen)); + } + break; +#endif /* HAVE_ECC_DHE */ + +#ifdef HAVE_ECC_SIGN + case WC_PK_TYPE_ECDSA_SIGN: + if (info->pk.eccsign.key == NULL) { + return BAD_FUNC_ARG; + } + if (!silabs_pk_curve_ok(info->pk.eccsign.key)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (!silabs_pk_digest_ok(info->pk.eccsign.key, + info->pk.eccsign.inlen)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + #ifndef WC_ALLOW_ECC_ZERO_HASH + if (silabs_pk_hash_is_zero(info->pk.eccsign.in, + info->pk.eccsign.inlen)) { + return ECC_BAD_ARG_E; + } + #endif + /* A key bound by wc_SilabsSe_EccUse*Key() holds no software scalar by + * design - the SE signs with the bound descriptor - so it is a valid + * source. Any other key must carry a real scalar, or the direct port's + * silabs_ecc_sign_hash() would silently sign with the SE attestation + * key instead of the one the caller supplied. */ + if (!info->pk.eccsign.key->silabsKeySet && + (info->pk.eccsign.key->type != ECC_PRIVATEKEY || + mp_unsigned_bin_size( + wc_ecc_key_get_priv(info->pk.eccsign.key)) == 0)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + ret = silabs_pk_load(info->pk.eccsign.key, 1, 1); + if (ret == 0) { + /* The SE emits a raw r||s pair. wc_ecc_sign_hash() hands the + * callback the buffer for the DER encoded signature, so convert + * here; under the direct port ecc.c does this after the helper. */ +#ifdef WOLFSSL_SMALL_STACK + byte* raw = NULL; +#else + byte raw[SILABS_ECC_RAW_SZ]; +#endif + word32 rawLen = SILABS_ECC_RAW_SZ; + word32 keySz = (word32)info->pk.eccsign.key->dp->size; + +#ifdef WOLFSSL_SMALL_STACK + raw = (byte*)XMALLOC(SILABS_ECC_RAW_SZ, + info->pk.eccsign.key->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (raw == NULL) { + ret = MEMORY_E; + } + if (ret == 0) +#endif + { + ret = silabs_cb_status(silabs_ecc_sign_hash_status( + info->pk.eccsign.in, info->pk.eccsign.inlen, raw, &rawLen, + info->pk.eccsign.key)); + if (ret == 0) { + ret = wc_ecc_rs_raw_to_sig(raw, keySz, raw + keySz, keySz, + info->pk.eccsign.out, info->pk.eccsign.outlen); + } + ForceZero(raw, SILABS_ECC_RAW_SZ); + } +#ifdef WOLFSSL_SMALL_STACK + XFREE(raw, info->pk.eccsign.key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + } + break; +#endif /* HAVE_ECC_SIGN */ + +#ifdef HAVE_ECC_VERIFY + case WC_PK_TYPE_ECDSA_VERIFY: + if (info->pk.eccverify.key == NULL) { + return BAD_FUNC_ARG; + } + if (!silabs_pk_curve_ok(info->pk.eccverify.key)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + if (!silabs_pk_digest_ok(info->pk.eccverify.key, + info->pk.eccverify.hashlen)) { + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + #ifndef WC_ALLOW_ECC_ZERO_HASH + if (silabs_pk_hash_is_zero(info->pk.eccverify.hash, + info->pk.eccverify.hashlen)) { + if (info->pk.eccverify.res != NULL) { + *info->pk.eccverify.res = 0; + } + return ECC_BAD_ARG_E; + } + #endif + ret = silabs_pk_load(info->pk.eccverify.key, 1, 0); + if (ret == 0) { + /* wolfCrypt passes a DER encoded signature; the SE wants a raw + * r||s pair of exactly the curve size, left padded. */ +#ifdef WOLFSSL_SMALL_STACK + byte* raw = NULL; +#else + byte raw[SILABS_ECC_VERIFY_SCRATCH_SZ]; +#endif + byte* rBuf; + byte* sBuf; + word32 keySz = (word32)info->pk.eccverify.key->dp->size; + word32 rLen = ECC_MAX_CRYPTO_HW_SIZE; + word32 sLen = ECC_MAX_CRYPTO_HW_SIZE; + +#ifdef WOLFSSL_SMALL_STACK + raw = (byte*)XMALLOC(SILABS_ECC_VERIFY_SCRATCH_SZ, + info->pk.eccverify.key->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (raw == NULL) { + ret = MEMORY_E; + } + if (ret == 0) +#endif + { + /* One allocation, carved into r||s and the two decode + * halves, so there is a single free on the way out. */ + rBuf = raw + SILABS_ECC_RAW_SZ; + sBuf = rBuf + ECC_MAX_CRYPTO_HW_SIZE; + + /* The decoder strips DER's leading zeros, but the SE wants + * each value at exactly the curve size, so left pad both. */ + ret = wc_ecc_sig_to_rs(info->pk.eccverify.sig, + info->pk.eccverify.siglen, rBuf, &rLen, sBuf, &sLen); + if (ret == 0 && (rLen > keySz || sLen > keySz)) { + ret = ASN_PARSE_E; + } + if (ret == 0) { + XMEMSET(raw, 0, SILABS_ECC_RAW_SZ); + XMEMCPY(raw + (keySz - rLen), rBuf, rLen); + XMEMCPY(raw + keySz + (keySz - sLen), sBuf, sLen); + ret = silabs_cb_status(silabs_ecc_verify_hash_status( + raw, keySz * 2, info->pk.eccverify.hash, + info->pk.eccverify.hashlen, info->pk.eccverify.res, + info->pk.eccverify.key)); + } + ForceZero(raw, SILABS_ECC_VERIFY_SCRATCH_SZ); + } +#ifdef WOLFSSL_SMALL_STACK + XFREE(raw, info->pk.eccverify.key->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + } + break; +#endif /* HAVE_ECC_VERIFY */ + + default: + break; + } + + return ret; +} + +/* WC_ALGO_TYPE_PK. ECC only - Series 2 has no RSA hardware, so RSA always + * declines to software. With a wrapped or built-in key the ecc_key holds no + * private scalar, so declining would run software ECC with no key behind it. + * Fail outright instead. */ +int wc_SilabsPk(wc_CryptoInfo* info) +{ + int ret; + ecc_key* key; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_pk_dispatch(info); + if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + key = silabs_pk_key(info); + if (key != NULL && key->silabsKeySet) { + return WC_HW_E; + } + } + + return ret; +} + +#endif /* WOLFSSL_SILABS_CRYPTOCB && WOLFSSL_SILABS_CRYPTOCB_ECC && HAVE_ECC */ diff --git a/wolfcrypt/src/port/silabs/silabs_cb_rng.c b/wolfcrypt/src/port/silabs/silabs_cb_rng.c new file mode 100644 index 0000000000..4465d151af --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_cb_rng.c @@ -0,0 +1,86 @@ +/* silabs_cb_rng.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 + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SILABS_CRYPTOCB) && defined(WOLFSSL_SILABS_CRYPTOCB_TRNG) + +#include +#include +#include +/* for the SE types; silabs_hash.h pulls in em_device.h or the host shim */ +#include + +#ifndef WOLFSSL_SILABS_HOST_TEST + #include +#endif + +/* Ask the SE for random bytes. Unlike silabs_GenerateRand(), which flattens + * every status to WC_HW_E, this maps through silabs_cb_status so an + * unsupported command declines to software while a real TRNG failure still + * returns WC_HW_E instead of silently downgrading the entropy source. */ +static int silabs_rng_bytes(byte* out, word32 sz) +{ + sl_se_command_context_t cmd = SL_SE_COMMAND_CONTEXT_INIT; + sl_status_t status; + + status = sl_se_get_random(&cmd, out, sz); + + return silabs_cb_status((int)status); +} + +/* WC_ALGO_TYPE_RNG and WC_ALGO_TYPE_SEED. The SE TRNG is a NIST SP800-90B + * entropy source; wolfCrypt treats it as the seed for its own DRBG unless the + * application asks for raw output. */ +int wc_SilabsRng(wc_CryptoInfo* info) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type == WC_ALGO_TYPE_SEED) { + if (info->seed.seed == NULL) { + return BAD_FUNC_ARG; + } + ret = silabs_rng_bytes(info->seed.seed, info->seed.sz); + } +#ifdef WOLFSSL_SILABS_TRNG + /* Only claim whole random blocks when the application opted in to the + * TRNG feeding every byte; otherwise the wolfCrypt DRBG stays in charge + * and just gets seeded from the SE above. */ + else if (info->algo_type == WC_ALGO_TYPE_RNG) { + if (info->rng.out == NULL) { + return BAD_FUNC_ARG; + } + ret = silabs_rng_bytes(info->rng.out, info->rng.sz); + } +#endif + + return ret; +} + +#endif /* WOLFSSL_SILABS_CRYPTOCB && WOLFSSL_SILABS_CRYPTOCB_TRNG */ diff --git a/wolfcrypt/src/port/silabs/silabs_cryptocb.c b/wolfcrypt/src/port/silabs/silabs_cryptocb.c new file mode 100644 index 0000000000..c98dd5dac1 --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_cryptocb.c @@ -0,0 +1,199 @@ +/* silabs_cryptocb.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 + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_SILABS_CRYPTOCB + +#include +#include +#include +#include +#include + +#ifndef WOLF_CRYPTO_CB + #error "WOLFSSL_SILABS_CRYPTOCB requires WOLF_CRYPTO_CB" +#endif + +/* Map an SE Manager status to a wolfCrypt error. SL_STATUS_NOT_SUPPORTED and + * SL_STATUS_INVALID_PARAMETER become CRYPTOCB_UNAVAILABLE so that a request the + * SE cannot service falls back to software instead of failing the caller. */ +int silabs_cb_status(int slStatus) +{ + /* The raw-status helpers return a negative wolfCrypt error for argument + * problems rather than an SE status; pass those straight through so they + * are not flattened into WC_HW_E. */ + if (slStatus < 0) { + return slStatus; + } + + switch ((sl_status_t)slStatus) { + case SL_STATUS_OK: + return 0; + case SL_STATUS_NOT_SUPPORTED: + case SL_STATUS_INVALID_PARAMETER: + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + default: + return WC_HW_E; + } +} + +/* Map a wolfCrypt hash type to the SE hash type and its digest length. + * Returns SL_SE_HASH_NONE when the SE cannot do that hash. */ +int silabs_cb_hash_type(int wcHashType, int* digestSz) +{ + int sz = 0; + int se = SL_SE_HASH_NONE; + + switch (wcHashType) { +#ifndef NO_SHA + case WC_HASH_TYPE_SHA: + se = SL_SE_HASH_SHA1; sz = WC_SHA_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SHA224 + case WC_HASH_TYPE_SHA224: + se = SL_SE_HASH_SHA224; sz = WC_SHA224_DIGEST_SIZE; break; +#endif +#ifndef NO_SHA256 + case WC_HASH_TYPE_SHA256: + se = SL_SE_HASH_SHA256; sz = WC_SHA256_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SILABS_SE_SHA384 + case WC_HASH_TYPE_SHA384: + se = SL_SE_HASH_SHA384; sz = WC_SHA384_DIGEST_SIZE; break; +#endif +#ifdef WOLFSSL_SILABS_SE_SHA512 + case WC_HASH_TYPE_SHA512: + se = SL_SE_HASH_SHA512; sz = WC_SHA512_DIGEST_SIZE; break; +#endif + default: + break; + } + + if (digestSz != NULL) { + *digestSz = sz; + } + + return se; +} + +/* Route a context free (WC_ALGO_TYPE_FREE) to the engine that owns the object. + * Only CMAC allocates, so it is the only case here. */ +static int wc_SilabsFree(wc_CryptoInfo* info) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + switch (info->free.algo) { +#if defined(WOLFSSL_SILABS_CRYPTOCB_CMAC) && defined(WOLFSSL_CMAC) && \ + !defined(NO_AES) + case WC_ALGO_TYPE_CMAC: + ret = wc_SilabsCmac(info); + break; +#endif + default: + break; + } + + return ret; +} + +/* Crypto callback dispatcher. Each engine handler runs the full operation and + * returns the wolfCrypt result: 0 when the SE handled it, CRYPTOCB_UNAVAILABLE + * to fall back to software, or a negative error. */ +static int wc_SilabsCryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + (void)devId; + (void)ctx; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + switch (info->algo_type) { + case WC_ALGO_TYPE_HASH: +#ifdef WOLFSSL_SILABS_CRYPTOCB_HASH + ret = wc_SilabsHash(info); +#endif + break; + case WC_ALGO_TYPE_SEED: + case WC_ALGO_TYPE_RNG: +#ifdef WOLFSSL_SILABS_CRYPTOCB_TRNG + ret = wc_SilabsRng(info); +#endif + break; + case WC_ALGO_TYPE_CIPHER: +#ifdef WOLFSSL_SILABS_CRYPTOCB_CIPHER + ret = wc_SilabsCipher(info); +#endif + break; + case WC_ALGO_TYPE_CMAC: +#if defined(WOLFSSL_SILABS_CRYPTOCB_CMAC) && defined(WOLFSSL_CMAC) && \ + !defined(NO_AES) + ret = wc_SilabsCmac(info); +#endif + break; + case WC_ALGO_TYPE_PK: +#if defined(WOLFSSL_SILABS_CRYPTOCB_ECC) && defined(HAVE_ECC) + ret = wc_SilabsPk(info); +#endif + break; + case WC_ALGO_TYPE_KDF: +#ifdef WOLFSSL_SILABS_CRYPTOCB_KDF + ret = wc_SilabsKdf(info); +#endif + break; +#ifdef WOLF_CRYPTO_CB_COPY + case WC_ALGO_TYPE_COPY: + #ifdef WOLFSSL_SILABS_CRYPTOCB_HASH + if (info->copy.algo == WC_ALGO_TYPE_HASH) { + ret = wc_SilabsHash(info); + } + #endif + break; +#endif + case WC_ALGO_TYPE_FREE: + ret = wc_SilabsFree(info); + break; + default: + break; + } + + return ret; +} + +int wc_SilabsCryptoCb_RegisterDevice(int devId) +{ + return wc_CryptoCb_RegisterDevice(devId, wc_SilabsCryptoDevCb, NULL); +} + +int wc_SilabsCryptoCb_UnRegisterDevice(int devId) +{ + wc_CryptoCb_UnRegisterDevice(devId); + return 0; +} + +#endif /* WOLFSSL_SILABS_CRYPTOCB */ diff --git a/wolfcrypt/src/port/silabs/silabs_ecc.c b/wolfcrypt/src/port/silabs/silabs_ecc.c index 0bead146f9..18937a6786 100644 --- a/wolfcrypt/src/port/silabs/silabs_ecc.c +++ b/wolfcrypt/src/port/silabs/silabs_ecc.c @@ -26,12 +26,22 @@ #include -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include #include #include -#include "sl_se_manager_internal_keys.h" + +/* for ForceZero() */ +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif +#ifndef WOLFSSL_SILABS_HOST_TEST + #include "sl_se_manager_internal_keys.h" +#endif #if (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) static sl_se_key_descriptor_t private_device_key = @@ -58,7 +68,7 @@ static sl_se_key_type_t silabs_map_key_type(ecc_curve_id curve_id) { sl_se_key_type_t res = SILABS_UNSUPPORTED_KEY_TYPE; - switch (curve_id) { + switch ((int)curve_id) { case ECC_SECP192R1: res = SL_SE_KEY_TYPE_ECC_P192; break; @@ -98,7 +108,10 @@ static sl_se_key_type_t silabs_map_key_type(ecc_curve_id curve_id) return res; } -int silabs_ecc_sign_hash(const byte* in, word32 inlen, byte* out, +/* Raw-status form: negative is a wolfCrypt error, otherwise the SE status + * unchanged so the callback port can decline rather than hard-fail. The + * wrapper below keeps the direct port's original WC_HW_E contract. */ +int silabs_ecc_sign_hash_status(const byte* in, word32 inlen, byte* out, word32 *outlen, ecc_key* key) { sl_status_t sl_stat; @@ -117,10 +130,19 @@ int silabs_ecc_sign_hash(const byte* in, word32 inlen, byte* out, } #if (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) - /* if signing and not private key provided then use vault key */ - if (key->type != ECC_PRIVATEKEY || - mp_unsigned_bin_size(wc_ecc_key_get_priv(key)) == 0) { - slkey = &private_device_key; + /* A wrapped or built-in key bound by wc_SilabsSe_EccUse*Key() has no + * software private scalar by design: key->key already names the key to + * sign with, so use it exactly as bound. */ + if (!key->silabsKeySet) { +#ifdef WOLFSSL_SILABS_SE_ACCEL + /* Legacy direct-port behavior: with no private scalar, sign with the + * SE attestation key. The callback port declines instead rather than + * silently sign with a different key than the caller supplied. */ + if (key->type != ECC_PRIVATEKEY || + mp_unsigned_bin_size(wc_ecc_key_get_priv(key)) == 0) { + slkey = &private_device_key; + } +#endif } #endif @@ -144,12 +166,26 @@ int silabs_ecc_sign_hash(const byte* in, word32 inlen, byte* out, *outlen = siglen; return 0; } - return WC_HW_E; + return (int)sl_stat; +} + +int silabs_ecc_sign_hash(const byte* in, word32 inlen, byte* out, + word32 *outlen, ecc_key* key) +{ + int status = silabs_ecc_sign_hash_status(in, inlen, out, outlen, key); + + if (status < 0) { + return status; + } + return (status != SL_STATUS_OK) ? WC_HW_E : 0; } #ifdef HAVE_ECC_VERIFY -int silabs_ecc_verify_hash(const byte* sig, word32 siglen, +/* Raw-status form; see silabs_ecc_sign_hash_status(). A rejected signature is + * not a failure of the operation: *stat is set to 0 and SL_STATUS_OK returned, + * so the caller reports "did not verify" rather than an error. */ +int silabs_ecc_verify_hash_status(const byte* sig, word32 siglen, const byte* hash, word32 hashlen, int* stat, ecc_key* key) { @@ -172,16 +208,31 @@ int silabs_ecc_verify_hash(const byte* sig, word32 siglen, } if (sl_stat == SL_STATUS_OK) { *stat = 1; - } else if (sl_stat == SL_STATUS_INVALID_SIGNATURE) { + return (int)SL_STATUS_OK; + } + if (sl_stat == SL_STATUS_INVALID_SIGNATURE) { *stat = 0; - } else { - return WC_HW_E; + return (int)SL_STATUS_OK; } - return 0; + return (int)sl_stat; +} + +int silabs_ecc_verify_hash(const byte* sig, word32 siglen, + const byte* hash, word32 hashlen, + int* stat, ecc_key* key) +{ + int status = silabs_ecc_verify_hash_status(sig, siglen, hash, hashlen, + stat, key); + + if (status < 0) { + return status; + } + return (status != SL_STATUS_OK) ? WC_HW_E : 0; } #endif -int silabs_ecc_make_key(ecc_key* key, int keysize) +/* Raw-status form; see silabs_ecc_sign_hash_status(). */ +int silabs_ecc_make_key_status(ecc_key* key, int keysize) { sl_status_t sl_stat; @@ -223,9 +274,25 @@ int silabs_ecc_make_key(ecc_key* key, int keysize) (ecc_blind_k_rng(key, NULL) != 0)) { return WC_HW_E; } + /* The SE returns an affine point. Software consumers of key->pubkey + * (the crypto callback port falls back to them) need z set, or the + * point reads as the point at infinity. */ + if (mp_set(key->pubkey.z, 1) != MP_OKAY) { + return WC_HW_E; + } } - return (sl_stat == SL_STATUS_OK) ? 0 : WC_HW_E; + return (int)sl_stat; +} + +int silabs_ecc_make_key(ecc_key* key, int keysize) +{ + int status = silabs_ecc_make_key_status(key, keysize); + + if (status < 0) { + return status; + } + return (status != SL_STATUS_OK) ? WC_HW_E : 0; } int silabs_ecc_import(ecc_key* key, word32 keysize, int pub, int priv) @@ -283,7 +350,8 @@ int silabs_ecc_import(ecc_key* key, word32 keysize, int pub, int priv) return err; } -int silabs_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, +/* Raw-status form; see silabs_ecc_sign_hash_status(). */ +int silabs_ecc_shared_secret_status(ecc_key* private_key, ecc_key* public_key, byte* out, word32* outlen) { sl_se_command_context_t cmd; @@ -335,7 +403,18 @@ int silabs_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, } ForceZero(fullpoint, sizeof(fullpoint)); - return (sl_stat == SL_STATUS_OK) ? 0 : WC_HW_E; + return (int)sl_stat; +} + +int silabs_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, + byte* out, word32* outlen) +{ + int status = silabs_ecc_shared_secret_status(private_key, public_key, out, outlen); + + if (status < 0) { + return status; + } + return (status != SL_STATUS_OK) ? WC_HW_E : 0; } int silabs_ecc_export_public(ecc_key* key, sl_se_key_descriptor_t* seKey) @@ -405,4 +484,4 @@ int silabs_ecc_load_vault(ecc_key* key) } #endif -#endif /* WOLFSSL_SILABS_SE_ACCEL */ +#endif /* WOLFSSL_SILABS_SE_TYPES */ diff --git a/wolfcrypt/src/port/silabs/silabs_hash.c b/wolfcrypt/src/port/silabs/silabs_hash.c index 1510bd2385..ccdb3ffd2e 100644 --- a/wolfcrypt/src/port/silabs/silabs_hash.c +++ b/wolfcrypt/src/port/silabs/silabs_hash.c @@ -28,7 +28,7 @@ #include -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include #include @@ -38,7 +38,7 @@ static sl_se_hash_type_t wc_silabs_gethashtype(enum wc_HashType type) { /* set init state */ - switch (type) { + switch ((int)type) { case WC_HASH_TYPE_SHA: return SL_SE_HASH_SHA1; break; @@ -47,11 +47,11 @@ static sl_se_hash_type_t wc_silabs_gethashtype(enum wc_HashType type) break; case WC_HASH_TYPE_SHA256: return SL_SE_HASH_SHA256; -#ifdef WOLFSSL_SILABS_SHA384 +#ifdef WOLFSSL_SILABS_SE_SHA384 case WC_HASH_TYPE_SHA384: return SL_SE_HASH_SHA384; #endif -#ifdef WOLFSSL_SILABS_SHA512 +#ifdef WOLFSSL_SILABS_SE_SHA512 case WC_HASH_TYPE_SHA512: return SL_SE_HASH_SHA512; #endif @@ -61,14 +61,15 @@ static sl_se_hash_type_t wc_silabs_gethashtype(enum wc_HashType type) return SL_SE_HASH_NONE; } -int wc_silabs_se_hash_init (wc_silabs_sha_t* sha, enum wc_HashType type) +/* The _status variants hand back the raw SE Manager status so an unimplemented + * command (which the callback port turns into a software fallback) can be told + * from a hardware failure. The plain wrappers still return 0 or WC_HW_E. */ +int wc_silabs_se_hash_init_status(wc_silabs_sha_t* sha, enum wc_HashType type) { - int ret = 0; - sl_status_t rr; sl_se_hash_type_t ht = wc_silabs_gethashtype(type); if (ht == SL_SE_HASH_NONE) { - return NOT_COMPILED_IN; + return (int)SL_STATUS_NOT_SUPPORTED; } /* set sizes and state */ @@ -76,53 +77,66 @@ int wc_silabs_se_hash_init (wc_silabs_sha_t* sha, enum wc_HashType type) /* set init state */ #ifdef WOLFSSL_SILABS_SE_ACCEL_3 - rr = sl_se_hash_starts(&sha->hash_ctx, &sha->cmd_ctx, ht, + return (int)sl_se_hash_starts(&sha->hash_ctx, &sha->cmd_ctx, ht, &sha->hash_type_ctx); #else - rr = sl_se_hash_multipart_starts(&sha->hash_type_ctx, &sha->cmd_ctx, ht); + return (int)sl_se_hash_multipart_starts(&sha->hash_type_ctx, &sha->cmd_ctx, + ht); #endif - if (rr != SL_STATUS_OK) { - ret = WC_HW_E; - } - - return ret; } -int wc_silabs_se_hash_update(wc_silabs_sha_t* sha, const byte* data, +int wc_silabs_se_hash_update_status(wc_silabs_sha_t* sha, const byte* data, word32 len) { - int ret = 0; - sl_status_t status; - #ifdef WOLFSSL_SILABS_SE_ACCEL_3 - status = sl_se_hash_update(&sha->hash_ctx, data, len); + return (int)sl_se_hash_update(&sha->hash_ctx, data, len); #else - status = sl_se_hash_multipart_update(&sha->hash_type_ctx, &sha->cmd_ctx, + return (int)sl_se_hash_multipart_update(&sha->hash_type_ctx, &sha->cmd_ctx, data, len); #endif - if (status != SL_STATUS_OK) { - ret = WC_HW_E; - } - return ret; } -int wc_silabs_se_hash_final(wc_silabs_sha_t* sha, byte* hash, word32 len) +int wc_silabs_se_hash_final_status(wc_silabs_sha_t* sha, byte* hash, word32 len) { - int ret = 0; - sl_status_t status; - #ifdef WOLFSSL_SILABS_SE_ACCEL_3 - status = sl_se_hash_finish(&sha->hash_ctx, hash, len); + return (int)sl_se_hash_finish(&sha->hash_ctx, hash, len); #else - status = sl_se_hash_multipart_finish(&sha->hash_type_ctx, &sha->cmd_ctx, + return (int)sl_se_hash_multipart_finish(&sha->hash_type_ctx, &sha->cmd_ctx, hash, len); #endif - if (status != SL_STATUS_OK) { - ret = WC_HW_E; +} + +int wc_silabs_se_hash_init (wc_silabs_sha_t* sha, enum wc_HashType type) +{ + int status = wc_silabs_se_hash_init_status(sha, type); + + if ((sl_status_t)status == SL_STATUS_NOT_SUPPORTED) { + return NOT_COMPILED_IN; } - return ret; + + return ((sl_status_t)status == SL_STATUS_OK) ? 0 : WC_HW_E; } +int wc_silabs_se_hash_update(wc_silabs_sha_t* sha, const byte* data, + word32 len) +{ + int status = wc_silabs_se_hash_update_status(sha, data, len); + + return ((sl_status_t)status == SL_STATUS_OK) ? 0 : WC_HW_E; +} + +int wc_silabs_se_hash_final(wc_silabs_sha_t* sha, byte* hash, word32 len) +{ + int status = wc_silabs_se_hash_final_status(sha, hash, len); + + return ((sl_status_t)status == SL_STATUS_OK) ? 0 : WC_HW_E; +} + + +#if defined(WOLFSSL_SILABS_SE_ACCEL) +/* Below this point the SE replaces the software hash implementations outright. + * The crypto callback port leaves them in place and drives the SE through the + * wc_silabs_se_hash_* helpers above instead. */ static int wc_HashUpdate_ex(wc_silabs_sha_t* sha, const byte* data, word32 len) { @@ -311,3 +325,5 @@ int wc_Sha512Final(wc_Sha512* sha, byte* hash) #endif /* WOLFSSL_SILABS_SHA512 */ #endif /* WOLFSSL_SILABS_SE_ACCEL */ + +#endif /* WOLFSSL_SILABS_SE_TYPES */ diff --git a/wolfcrypt/src/port/silabs/silabs_key.c b/wolfcrypt/src/port/silabs/silabs_key.c new file mode 100644 index 0000000000..39e0e3441f --- /dev/null +++ b/wolfcrypt/src/port/silabs/silabs_key.c @@ -0,0 +1,518 @@ +/* silabs_key.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 + */ + +/* Secure Vault key management for the Silicon Labs crypto callback port: + * wrapped keys (encrypted to a device-unique key, usable by the SE but never + * readable by the application) and the SE built-in key slots. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_SILABS_CRYPTOCB + +#include +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif +#include +/* pulls em_device.h or the host shim, so the Vault feature macro is visible */ +#include + +#if !defined(NO_AES) && defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) +#include +#include +#endif + +#if defined(HAVE_ECC) && defined(WOLFSSL_SILABS_CRYPTOCB_ECC) +#include +#include +#endif + +#ifndef WOLFSSL_SILABS_HOST_TEST + #include +#endif + +/* Wrapped keys are a Secure Vault High feature. On a Vault Mid part the SE has + * no wrapped-key storage; the built-in slots are still reachable. */ +#if defined(_SILICON_LABS_SECURITY_FEATURE) && \ + (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) + #define WOLFSSL_SILABS_WRAPPED_KEYS +#endif + +/* Map a key-bit count to the SE AES key type. */ +#if !defined(NO_AES) && defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) +static int silabs_key_aes_type(int keyBits, sl_se_key_type_t* type) +{ + switch (keyBits) { + case 128: + *type = SL_SE_KEY_TYPE_AES_128; + break; +#ifdef WOLFSSL_AES_192 + case 192: + *type = SL_SE_KEY_TYPE_AES_192; + break; +#endif +#ifdef WOLFSSL_AES_256 + case 256: + *type = SL_SE_KEY_TYPE_AES_256; + break; +#endif + default: + return BAD_FUNC_ARG; + } + + return 0; +} +#endif /* !NO_AES && WOLFSSL_SILABS_CRYPTOCB_CIPHER */ + +#if defined(HAVE_ECC) && defined(WOLFSSL_SILABS_CRYPTOCB_ECC) +/* Map a wolfCrypt curve id to the SE ECC key type. Mirrors the curves the + * callback port offloads. */ +static int silabs_key_ecc_type(int curveId, sl_se_key_type_t* type, + word32* keySz) +{ + int sz; + + switch (curveId) { +#ifdef SL_SE_KEY_TYPE_ECC_P192 + case ECC_SECP192R1: + *type = SL_SE_KEY_TYPE_ECC_P192; + break; +#endif + case ECC_SECP256R1: + *type = SL_SE_KEY_TYPE_ECC_P256; + break; +#ifdef SL_SE_KEY_TYPE_ECC_P384 + case ECC_SECP384R1: + *type = SL_SE_KEY_TYPE_ECC_P384; + break; +#endif +#ifdef SL_SE_KEY_TYPE_ECC_P521 + case ECC_SECP521R1: + *type = SL_SE_KEY_TYPE_ECC_P521; + break; +#endif + default: + return BAD_FUNC_ARG; + } + + /* Let wolfCrypt supply the coordinate size rather than restating it. */ + sz = wc_ecc_get_curve_size_from_id(curveId); + if (sz <= 0) { + return BAD_FUNC_ARG; + } + *keySz = (word32)sz; + + return 0; +} +#endif /* HAVE_ECC && WOLFSSL_SILABS_CRYPTOCB_ECC */ + +#if !defined(NO_AES) && defined(WOLFSSL_SILABS_WRAPPED_KEYS) && \ + defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) + +int wc_SilabsSe_AesGetWrappedKeySize(int keyBits, word32* outSz) +{ + sl_se_key_descriptor_t desc; + sl_se_key_type_t type; + uint32_t sz = 0; + int ret; + + if (outSz == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_key_aes_type(keyBits, &type); + if (ret != 0) { + return ret; + } + + XMEMSET(&desc, 0, sizeof(desc)); + desc.type = type; + desc.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE; + desc.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED; + + ret = silabs_cb_status((int)sl_se_get_storage_size(&desc, &sz)); + if (ret == 0) { + *outSz = (word32)sz; + } + + return ret; +} + +int wc_SilabsSe_AesGenerateWrappedKey(int keyBits, byte* out, word32* outSz) +{ + sl_se_command_context_t cmd = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_descriptor_t desc; + sl_se_key_type_t type; + word32 need = 0; + int ret; + + if (out == NULL || outSz == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_SilabsSe_AesGetWrappedKeySize(keyBits, &need); + if (ret != 0) { + return ret; + } + if (*outSz < need) { + *outSz = need; + return BUFFER_E; + } + + ret = silabs_key_aes_type(keyBits, &type); + if (ret != 0) { + return ret; + } + + XMEMSET(&desc, 0, sizeof(desc)); + desc.type = type; + desc.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE; + desc.size = (uint32_t)(keyBits / 8); + desc.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED; + desc.storage.location.buffer.pointer = out; + desc.storage.location.buffer.size = need; + + ret = silabs_cb_status((int)sl_se_generate_key(&cmd, &desc)); + if (ret == 0) { + *outSz = need; + } + + return ret; +} + +int wc_SilabsSe_AesUseWrappedKey(Aes* aes, const byte* wrapped, + word32 wrappedSz, int keyBits) +{ + sl_se_command_context_t cc = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_type_t type; + word32 need = 0; + int ret; + + if (aes == NULL || wrapped == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_SilabsSe_AesGetWrappedKeySize(keyBits, &need); + if (ret != 0) { + return ret; + } + if (wrappedSz != need) { + return BAD_LENGTH_E; + } + + ret = silabs_key_aes_type(keyBits, &type); + if (ret != 0) { + return ret; + } + + aes->ctx.cmd_ctx = cc; + XMEMSET(&(aes->ctx.key), 0, sizeof(sl_se_key_descriptor_t)); + aes->ctx.key.type = type; + aes->ctx.key.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE; + aes->ctx.key.size = (uint32_t)(keyBits / 8); + aes->ctx.key.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED; + /* The descriptor references the caller's blob, which must outlive the + * Aes. Nothing is copied: that is the point of a wrapped key. */ + aes->ctx.key.storage.location.buffer.pointer = (uint8_t*)wrapped; + aes->ctx.key.storage.location.buffer.size = wrappedSz; + /* Scrub any plaintext key this object was carrying. The Secure Element + * holds the key from here on, and leaving software material behind would + * let a path that never consults ctx.keySet encrypt with the old key. */ + ForceZero(aes->devKey, sizeof(aes->devKey)); + ForceZero(aes->key, sizeof(aes->key)); + /* The schedule is gone, so the object must not claim to be keyed: an + * object keyed by wc_AesSetKey() before this call would otherwise pass + * WC_AES_KEY_IS_SET() with an all-zero schedule. wc_AesInit_Id() and + * wc_AesInit_Label() leave the flag clear for the same reason - a device + * named key has no software key, and a software path must fail rather + * than encrypt with zeros. Offload is unaffected, because every dispatch + * runs before that guard. */ + aes->keyInstalled = 0; + aes->ctx.keySet = 1; + + /* wolfCrypt sizes its own state from keylen even when it never sees the + * key material. */ + aes->keylen = keyBits / 8; + aes->rounds = (word32)(aes->keylen / 4 + 6); + + return 0; +} + +#endif /* !NO_AES && WOLFSSL_SILABS_WRAPPED_KEYS */ + +#if !defined(NO_AES) && defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) +int wc_SilabsSe_AesUseBuiltInKey(Aes* aes, int slot, int keyBits) +{ + sl_se_command_context_t cc = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_type_t type; + int ret; + + if (aes == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_key_aes_type(keyBits, &type); + if (ret != 0) { + return ret; + } + + aes->ctx.cmd_ctx = cc; + XMEMSET(&(aes->ctx.key), 0, sizeof(sl_se_key_descriptor_t)); + aes->ctx.key.type = type; + aes->ctx.key.flags = SL_SE_KEY_FLAG_NON_EXPORTABLE; + aes->ctx.key.size = (uint32_t)(keyBits / 8); + aes->ctx.key.storage.method = SL_SE_KEY_STORAGE_INTERNAL_IMMUTABLE; + aes->ctx.key.storage.location.slot = (uint32_t)slot; + /* Scrub any plaintext key this object was carrying. The Secure Element + * holds the key from here on, and leaving software material behind would + * let a path that never consults ctx.keySet encrypt with the old key. */ + ForceZero(aes->devKey, sizeof(aes->devKey)); + ForceZero(aes->key, sizeof(aes->key)); + /* The schedule is gone, so the object must not claim to be keyed: an + * object keyed by wc_AesSetKey() before this call would otherwise pass + * WC_AES_KEY_IS_SET() with an all-zero schedule. wc_AesInit_Id() and + * wc_AesInit_Label() leave the flag clear for the same reason - a device + * named key has no software key, and a software path must fail rather + * than encrypt with zeros. Offload is unaffected, because every dispatch + * runs before that guard. */ + aes->keyInstalled = 0; + aes->ctx.keySet = 1; + + aes->keylen = keyBits / 8; + aes->rounds = (word32)(aes->keylen / 4 + 6); + + return 0; +} +#endif /* !NO_AES && WOLFSSL_SILABS_CRYPTOCB_CIPHER */ + +#if defined(HAVE_ECC) && defined(WOLFSSL_SILABS_CRYPTOCB_ECC) + +#ifdef WOLFSSL_SILABS_WRAPPED_KEYS +int wc_SilabsSe_EccGetWrappedKeySize(int curveId, word32* outSz) +{ + sl_se_key_descriptor_t desc; + sl_se_key_type_t type; + word32 keySz = 0; + uint32_t sz = 0; + int ret; + + if (outSz == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_key_ecc_type(curveId, &type, &keySz); + if (ret != 0) { + return ret; + } + + XMEMSET(&desc, 0, sizeof(desc)); + desc.type = type; + desc.flags = SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PRIVATE_KEY | + SL_SE_KEY_FLAG_NON_EXPORTABLE; + desc.size = keySz; + desc.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED; + + ret = silabs_cb_status((int)sl_se_get_storage_size(&desc, &sz)); + if (ret == 0) { + *outSz = (word32)sz; + } + + return ret; +} + +int wc_SilabsSe_EccGenerateWrappedKey(int curveId, byte* wrapped, + word32* wrappedSz, byte* pubOut, word32* pubOutSz) +{ + sl_se_command_context_t cmd = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_descriptor_t desc; + sl_se_key_descriptor_t pubDesc; + sl_se_key_type_t type; + word32 keySz = 0; + word32 need = 0; + int ret; + + if (wrapped == NULL || wrappedSz == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_key_ecc_type(curveId, &type, &keySz); + if (ret != 0) { + return ret; + } + ret = wc_SilabsSe_EccGetWrappedKeySize(curveId, &need); + if (ret != 0) { + return ret; + } + if (*wrappedSz < need) { + *wrappedSz = need; + return BUFFER_E; + } + /* The public point is X||Y, two coordinates of the curve size. */ + if (pubOut != NULL && (pubOutSz == NULL || *pubOutSz < (keySz * 2))) { + if (pubOutSz != NULL) { + *pubOutSz = keySz * 2; + } + return BUFFER_E; + } + + XMEMSET(&desc, 0, sizeof(desc)); + desc.type = type; + desc.flags = SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PRIVATE_KEY | + SL_SE_KEY_FLAG_NON_EXPORTABLE; + desc.size = keySz; + desc.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED; + desc.storage.location.buffer.pointer = wrapped; + desc.storage.location.buffer.size = need; + + ret = silabs_cb_status((int)sl_se_generate_key(&cmd, &desc)); + if (ret != 0) { + return ret; + } + *wrappedSz = need; + + if (pubOut != NULL) { + XMEMSET(&pubDesc, 0, sizeof(pubDesc)); + pubDesc.type = type; + pubDesc.flags = SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PUBLIC_KEY; + pubDesc.size = keySz; + pubDesc.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_PLAINTEXT; + pubDesc.storage.location.buffer.pointer = pubOut; + pubDesc.storage.location.buffer.size = keySz * 2; + + ret = silabs_cb_status( + (int)sl_se_export_public_key(&cmd, &desc, &pubDesc)); + if (ret == 0) { + *pubOutSz = keySz * 2; + } + } + + return ret; +} + +int wc_SilabsSe_EccUseWrappedKey(ecc_key* key, const byte* wrapped, + word32 wrappedSz, int curveId) +{ + sl_se_command_context_t cc = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_type_t type; + word32 keySz = 0; + word32 need = 0; + int ret; + + if (key == NULL || wrapped == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_key_ecc_type(curveId, &type, &keySz); + if (ret != 0) { + return ret; + } + ret = wc_SilabsSe_EccGetWrappedKeySize(curveId, &need); + if (ret != 0) { + return ret; + } + if (wrappedSz != need) { + return BAD_LENGTH_E; + } + + /* Give wolfCrypt the curve so sizes and signature encoding are right, + * even though it never sees the private scalar. */ + ret = wc_ecc_set_curve(key, (int)keySz, curveId); + if (ret != 0) { + return ret; + } + key->type = ECC_PRIVATEKEY; + + key->cmd_ctx = cc; + XMEMSET(&(key->key), 0, sizeof(sl_se_key_descriptor_t)); + key->key.type = type; + key->key.flags = SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PRIVATE_KEY | + SL_SE_KEY_FLAG_NON_EXPORTABLE; + key->key.size = keySz; + key->key.storage.method = SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED; + key->key.storage.location.buffer.pointer = (uint8_t*)wrapped; + key->key.storage.location.buffer.size = wrappedSz; + /* Drop any software key material - the SE holds this key now. The object + * stays marked ECC_PRIVATEKEY, so wc_ecc_export_private_only() still + * accepts it; without this scrub a caller could export the scalar held + * *before* the bind from a key that now claims to be SE-resident. + * ecc_forcezero_k() also clears the blinding state, which would be + * enough to reconstruct it. */ + ForceZero(key->key_raw, sizeof(key->key_raw)); + ecc_forcezero_k(key); + key->silabsKeySet = 1; + + return 0; +} +#endif /* WOLFSSL_SILABS_WRAPPED_KEYS */ + +int wc_SilabsSe_EccUseBuiltInKey(ecc_key* key, int slot, int curveId) +{ + sl_se_command_context_t cc = SL_SE_COMMAND_CONTEXT_INIT; + sl_se_key_type_t type; + word32 keySz = 0; + int ret; + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + ret = silabs_key_ecc_type(curveId, &type, &keySz); + if (ret != 0) { + return ret; + } + + ret = wc_ecc_set_curve(key, (int)keySz, curveId); + if (ret != 0) { + return ret; + } + key->type = ECC_PRIVATEKEY; + + key->cmd_ctx = cc; + XMEMSET(&(key->key), 0, sizeof(sl_se_key_descriptor_t)); + key->key.type = type; + key->key.flags = SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PRIVATE_KEY | + SL_SE_KEY_FLAG_NON_EXPORTABLE; + key->key.size = keySz; + key->key.storage.method = SL_SE_KEY_STORAGE_INTERNAL_IMMUTABLE; + key->key.storage.location.slot = (uint32_t)slot; + /* Drop any software key material - the SE holds this key now. The object + * stays marked ECC_PRIVATEKEY, so wc_ecc_export_private_only() still + * accepts it; without this scrub a caller could export the scalar held + * *before* the bind from a key that now claims to be SE-resident. + * ecc_forcezero_k() also clears the blinding state, which would be + * enough to reconstruct it. */ + ForceZero(key->key_raw, sizeof(key->key_raw)); + ecc_forcezero_k(key); + key->silabsKeySet = 1; + + return 0; +} + +#endif /* HAVE_ECC && WOLFSSL_SILABS_CRYPTOCB_ECC */ + +#endif /* WOLFSSL_SILABS_CRYPTOCB */ diff --git a/wolfcrypt/src/port/silabs/silabs_random.c b/wolfcrypt/src/port/silabs/silabs_random.c index d0fa63df86..b08af08225 100644 --- a/wolfcrypt/src/port/silabs/silabs_random.c +++ b/wolfcrypt/src/port/silabs/silabs_random.c @@ -27,11 +27,16 @@ #include -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) -#include -#include +#ifdef WOLFSSL_SILABS_HOST_TEST + #include +#else + #include + #include +#endif +#include #include int silabs_GenerateRand(byte* output, word32 sz) @@ -49,4 +54,4 @@ int silabs_GenerateRand(byte* output, word32 sz) return (status != SL_STATUS_OK) ? WC_HW_E : 0; } -#endif /* WOLFSSL_SILABS_SE_ACCEL */ +#endif /* WOLFSSL_SILABS_SE_TYPES */ diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index 1c2c4acbc4..c683af856f 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -37,6 +37,9 @@ #include #include #include +#ifdef WOLF_CRYPTO_CB + #include +#endif #ifdef NO_INLINE #include @@ -255,6 +258,16 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, return BAD_FUNC_ARG; } +#ifdef WOLF_CRYPTO_CB + if (devId != INVALID_DEVID) { + ret = wc_CryptoCb_Pbkdf2(output, passwd, pLen, salt, sLen, iterations, + kLen, hashType, devId); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + } +#endif + hashT = wc_HashTypeConvert(hashType); hLen = wc_HashGetDigestSize(hashT); if (hLen < 0) diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index fff02bad25..db31e328e8 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -232,7 +232,7 @@ This library contains implementation for the random number generator. #endif #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include #endif @@ -4942,7 +4942,14 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) */ #define USE_TEST_GENSEED -#elif defined(WOLFSSL_SILABS_SE_ACCEL) +#elif defined(WOLFSSL_SILABS_SE_TYPES) && !defined(WOLFSSL_SILABS_HOST_TEST) + /* Both Silicon Labs ports seed from the SE TRNG. Gated on SE_TYPES, not + * SE_ACCEL, so the crypto callback port seeds too: the SE is the only + * entropy source on the part, so otherwise an RNG built with INVALID_DEVID + * fails with RNG_FAILURE_E and the software fallback path is unusable. + * + * The host compile test is excluded - its shim declines every command, so + * that build must fall through to the host's own entropy source. */ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) { (void)os; diff --git a/wolfcrypt/src/sha.c b/wolfcrypt/src/sha.c index d65766313e..f7cf0a59ed 100644 --- a/wolfcrypt/src/sha.c +++ b/wolfcrypt/src/sha.c @@ -155,6 +155,7 @@ return BAD_FUNC_ARG; } + WC_SILABS_CLEAR_HASH_CTX(sha); (void)devId; (void)heap; @@ -210,6 +211,7 @@ return BAD_FUNC_ARG; } + WC_SILABS_CLEAR_HASH_CTX(sha); (void)devId; (void)heap; @@ -387,6 +389,7 @@ if (sha == NULL) { return BAD_FUNC_ARG; } + WC_SILABS_CLEAR_HASH_CTX(sha); (void)devId; return se050_hash_init(&sha->se050Ctx, heap); @@ -579,6 +582,7 @@ int wc_InitSha_ex(wc_Sha* sha, void* heap, int devId) return BAD_FUNC_ARG; } + WC_SILABS_CLEAR_HASH_CTX(sha); sha->heap = heap; #ifdef WOLF_CRYPTO_CB sha->devId = devId; @@ -1251,7 +1255,7 @@ int wc_ShaCopy(wc_Sha* src, wc_Sha* dst) wc_ShaFree(dst); XMEMCPY(dst, src, sizeof(wc_Sha)); -#if defined(WOLFSSL_SILABS_SE_ACCEL) && defined(WOLFSSL_SILABS_SE_ACCEL_3) +#if defined(WOLFSSL_SILABS_SE_TYPES) && defined(WOLFSSL_SILABS_SE_ACCEL_3) dst->silabsCtx.hash_ctx.cmd_ctx = &dst->silabsCtx.cmd_ctx; dst->silabsCtx.hash_ctx.hash_type_ctx = &dst->silabsCtx.hash_type_ctx; #endif diff --git a/wolfcrypt/src/sha256.c b/wolfcrypt/src/sha256.c index 14c1f99caa..843b67a3de 100644 --- a/wolfcrypt/src/sha256.c +++ b/wolfcrypt/src/sha256.c @@ -793,6 +793,7 @@ static int InitSha256(wc_Sha256* sha256) if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); sha256->heap = heap; #ifdef WOLF_CRYPTO_CB sha256->devId = devId; @@ -939,6 +940,7 @@ static int InitSha256(wc_Sha256* sha256) if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); (void)devId; (void)heap; @@ -1002,6 +1004,7 @@ static int InitSha256(wc_Sha256* sha256) if (sha256 == NULL) { return BAD_FUNC_ARG; } + WC_SILABS_CLEAR_HASH_CTX(sha256); (void)devId; return se050_hash_init(&sha256->se050Ctx, heap); @@ -1083,6 +1086,7 @@ static int InitSha256(wc_Sha256* sha256) if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); sha256->heap = heap; ret = InitSha256(sha256); @@ -1141,6 +1145,7 @@ static int InitSha256(wc_Sha256* sha256) return BAD_FUNC_ARG; } + WC_SILABS_CLEAR_HASH_CTX(sha256); #if defined(WOLFSSL_USE_ESP32_CRYPT_HASH_HW) && \ !defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA256) /* We know this is a fresh, uninitialized item, so set to INIT */ @@ -1242,6 +1247,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); ret = InitSha256(sha256); if (ret != 0) return ret; @@ -1379,6 +1385,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); ret = InitSha256(sha256); if (ret != 0) return ret; @@ -1486,6 +1493,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); ret = InitSha256(sha256); if (ret != 0) return ret; @@ -1552,6 +1560,7 @@ int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId) if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); ret = InitSha256(sha256); if (ret != 0) return ret; @@ -1605,6 +1614,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, int ret; if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); ret = InitSha256(sha256); if (ret != 0) return ret; @@ -1621,6 +1631,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, int ret = 0; if (sha256 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha256); ret = InitSha256(sha256); if (ret != 0) return ret; @@ -2553,6 +2564,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, { if (sha224 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha224); (void)devId; (void)heap; @@ -2604,6 +2616,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, if (sha224 == NULL) { return BAD_FUNC_ARG; } + WC_SILABS_CLEAR_HASH_CTX(sha224); (void)devId; return se050_hash_init(&sha224->se050Ctx, heap); @@ -2662,6 +2675,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, int ret; if (sha224 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha224); ret = InitSha256((wc_Sha256*)sha224); if (ret != 0) return ret; @@ -2762,6 +2776,7 @@ static WC_INLINE int Transform_Sha256_Len(wc_Sha256* sha256, const byte* data, if (sha224 == NULL) return BAD_FUNC_ARG; + WC_SILABS_CLEAR_HASH_CTX(sha224); sha224->heap = heap; #ifdef WOLFSSL_SMALL_STACK_CACHE sha224->W = NULL; @@ -3274,7 +3289,7 @@ int wc_Sha224_Grow(wc_Sha224* sha224, const byte* in, int inSz) } #endif - #if defined(WOLFSSL_SILABS_SE_ACCEL) && defined(WOLFSSL_SILABS_SE_ACCEL_3) + #if defined(WOLFSSL_SILABS_SE_TYPES) && defined(WOLFSSL_SILABS_SE_ACCEL_3) dst->silabsCtx.hash_ctx.cmd_ctx = &dst->silabsCtx.cmd_ctx; dst->silabsCtx.hash_ctx.hash_type_ctx = &dst->silabsCtx.hash_type_ctx; #endif @@ -3428,7 +3443,7 @@ int wc_Sha256Copy(wc_Sha256* src, wc_Sha256* dst) } #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) && defined(WOLFSSL_SILABS_SE_ACCEL_3) +#if defined(WOLFSSL_SILABS_SE_TYPES) && defined(WOLFSSL_SILABS_SE_ACCEL_3) dst->silabsCtx.hash_ctx.cmd_ctx = &dst->silabsCtx.cmd_ctx; dst->silabsCtx.hash_ctx.hash_type_ctx = &dst->silabsCtx.hash_type_ctx; #endif diff --git a/wolfcrypt/src/sha512.c b/wolfcrypt/src/sha512.c index 108af7ed2f..14de60ea2d 100644 --- a/wolfcrypt/src/sha512.c +++ b/wolfcrypt/src/sha512.c @@ -3210,7 +3210,7 @@ int wc_Sha512Copy(wc_Sha512* src, wc_Sha512* dst) } #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) && defined(WOLFSSL_SILABS_SE_ACCEL_3) && \ +#if defined(WOLFSSL_SILABS_SE_TYPES) && defined(WOLFSSL_SILABS_SE_ACCEL_3) && \ defined(WOLFSSL_SILABS_SHA512) dst->silabsCtx.hash_ctx.cmd_ctx = &dst->silabsCtx.cmd_ctx; dst->silabsCtx.hash_ctx.hash_type_ctx = &dst->silabsCtx.hash_type_ctx; @@ -3652,7 +3652,7 @@ int wc_Sha384Copy(wc_Sha384* src, wc_Sha384* dst) } #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) && defined(WOLFSSL_SILABS_SE_ACCEL_3) && \ +#if defined(WOLFSSL_SILABS_SE_TYPES) && defined(WOLFSSL_SILABS_SE_ACCEL_3) && \ defined(WOLFSSL_SILABS_SHA384) dst->silabsCtx.hash_ctx.cmd_ctx = &dst->silabsCtx.cmd_ctx; dst->silabsCtx.hash_ctx.hash_type_ctx = &dst->silabsCtx.hash_type_ctx; diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 4c6fbe7bdf..6beff23742 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -160,6 +160,10 @@ Threading/Mutex options: #include #endif +#if defined(WOLFSSL_SILABS_CRYPTOCB) + #include +#endif + #if (defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER)) \ && !defined(WOLFCRYPT_ONLY) #include @@ -581,6 +585,15 @@ int wolfCrypt_Init(void) WOLFCRYPT_INIT_RAISE_BAD_STATE(); } #endif + + /* Register the Silicon Labs Secure Element device so wolfCrypt operations + * route to the SE. sl_se_init() runs further down in this function. */ + #if defined(WOLFSSL_SILABS_CRYPTOCB) && defined(WOLF_CRYPTO_CB) + ret = wc_SilabsCryptoCb_RegisterDevice(WOLFSSL_SILABS_DEVID); + if (ret != 0) { + WOLFCRYPT_INIT_RAISE_BAD_STATE(); + } + #endif #if defined(MAX3266X_RTC) ret = wc_MXC_RTC_Init(); if (ret != 0) { @@ -636,11 +649,11 @@ int wolfCrypt_Init(void) } #endif - #ifdef WOLFSSL_SILABS_SE_ACCEL + #ifdef WOLFSSL_SILABS_SE_TYPES /* init handles if it is already initialized */ ret = sl_se_init(); if (ret != 0) { - WOLFSSL_MSG("SILABS_SE_ACCEL init failed"); + WOLFSSL_MSG("SiLabs SE Manager init failed"); WOLFCRYPT_INIT_RAISE_BAD_STATE(); } #endif @@ -871,7 +884,17 @@ int wolfCrypt_Cleanup(void) #if defined(WOLFSSL_CRYPTOCELL) cc310_Free(); #endif - #ifdef WOLFSSL_SILABS_SE_ACCEL + /* Unregister before sl_se_deinit(). wc_CryptoCb_Cleanup() further down + * would also clear the device, but it runs after the SE is torn down, so + * the unregister command would reach the callback with no SE behind it. */ + #if defined(WOLFSSL_SILABS_CRYPTOCB) && defined(WOLF_CRYPTO_CB) + { + int ret2 = wc_SilabsCryptoCb_UnRegisterDevice(WOLFSSL_SILABS_DEVID); + if (ret == 0) + ret = ret2; + } + #endif + #ifdef WOLFSSL_SILABS_SE_TYPES { int ret2 = sl_se_deinit(); if (ret == 0) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 33a25729dc..b7a4d42688 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -52,6 +52,13 @@ #ifdef NO_INLINE #include + /* With NO_INLINE the misc.c implementations live in the library and are + * WOLFSSL_LOCAL, so they do not link from here. Use the exported wrapper + * for the one this file needs. WOLFSSL_NO_FORCE_ZERO means the user + * supplies ForceZero() with external linkage, so call it directly. */ + #ifndef WOLFSSL_NO_FORCE_ZERO + #define ForceZero wc_ForceZero + #endif #else #define WOLFSSL_MISC_INCLUDED #include @@ -474,6 +481,10 @@ static const byte const_byte_array[] = "A+Gd\0\0\0"; #endif #ifdef WOLF_CRYPTO_CB #include +#ifdef WOLFSSL_SILABS_CRYPTOCB + /* For WOLFSSL_SILABS_WRAPPED_KEYS_API and the wc_SilabsSe_* prototypes. */ + #include +#endif #ifdef HAVE_INTEL_QA_SYNC #include #endif @@ -81419,6 +81430,14 @@ typedef struct { int hkdfPendArm; /* pend the next this-many HKDF callback calls */ int hkdfPendCount; /* pends issued; test asserts non-zero */ #endif +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) + int chachaPolyEncCount; /* ChaCha20-Poly1305 encrypt cb invocations */ + int chachaPolyDecCount; /* ChaCha20-Poly1305 decrypt cb invocations */ +#endif +#if (defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED)) + int pbkdf2Count; /* PBKDF2 callback invocations */ + int pbkdf2Decline; /* when set, decline so software fallback runs */ +#endif } myCryptoDevCtx; #ifdef WOLF_CRYPTO_CB_ONLY_RSA @@ -83978,6 +83997,53 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } #endif /* !NO_DES3 */ #endif /* !NO_AES || !NO_DES3 */ +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) + if (info->cipher.type == WC_CIPHER_CHACHA) { + ChaCha chacha; + Poly1305 poly; + + /* The one-shot AEAD carries no key object and so no devId to + * blank out. Forward through the _ex entry points, which have no + * callback hook, instead of recursing into the one-shot. */ + if (info->cipher.enc) { + ret = wc_Chacha_SetKey(&chacha, + info->cipher.chacha20_poly1305_enc.inKey, + CHACHA20_POLY1305_AEAD_KEYSIZE); + if (ret == 0) { + ret = wc_ChaCha20Poly1305_Encrypt_ex(&chacha, &poly, + info->cipher.chacha20_poly1305_enc.out, + info->cipher.chacha20_poly1305_enc.in, + info->cipher.chacha20_poly1305_enc.inSz, + info->cipher.chacha20_poly1305_enc.inIV, + info->cipher.chacha20_poly1305_enc.outAuthTag, + info->cipher.chacha20_poly1305_enc.inAAD, + info->cipher.chacha20_poly1305_enc.inAADSz); + } + if (ret == 0) + myCtx->chachaPolyEncCount++; + } + else { + ret = wc_Chacha_SetKey(&chacha, + info->cipher.chacha20_poly1305_dec.inKey, + CHACHA20_POLY1305_AEAD_KEYSIZE); + if (ret == 0) { + ret = wc_ChaCha20Poly1305_Decrypt_ex(&chacha, &poly, + info->cipher.chacha20_poly1305_dec.out, + info->cipher.chacha20_poly1305_dec.in, + info->cipher.chacha20_poly1305_dec.inSz, + info->cipher.chacha20_poly1305_dec.inIV, + info->cipher.chacha20_poly1305_dec.inAuthTag, + info->cipher.chacha20_poly1305_dec.inAAD, + info->cipher.chacha20_poly1305_dec.inAADSz); + } + if (ret == 0) + myCtx->chachaPolyDecCount++; + } + + ForceZero(&chacha, sizeof(chacha)); + ForceZero(&poly, sizeof(poly)); + } +#endif /* HAVE_CHACHA && HAVE_POLY1305 */ } #if !defined(NO_SHA) || !defined(NO_SHA256) || \ defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512) @@ -85194,6 +85260,24 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) NULL, INVALID_DEVID); } #endif /* HAVE_CMAC_KDF */ + #if (defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED)) + if (info->kdf.type == WC_KDF_TYPE_PBKDF2) { + if (myCtx->pbkdf2Decline) { + /* Exercise the decline path: wc_PBKDF2_ex must fall through to + * its own software implementation and still be correct. */ + return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + } + /* Redirect to software implementation for testing. Passing + * INVALID_DEVID keeps wc_PBKDF2_ex from dispatching back here. */ + ret = wc_PBKDF2_ex(info->kdf.pbkdf2.output, + info->kdf.pbkdf2.passwd, info->kdf.pbkdf2.pLen, + info->kdf.pbkdf2.salt, info->kdf.pbkdf2.sLen, + info->kdf.pbkdf2.iterations, info->kdf.pbkdf2.kLen, + info->kdf.pbkdf2.hashType, NULL, INVALID_DEVID); + if (ret == 0) + myCtx->pbkdf2Count++; + } + #endif /* HAVE_PBKDF2 && !NO_HMAC && !NO_PWDBASED */ } #if defined(WOLFSSL_SHE) && !defined(NO_AES) else if (info->algo_type == WC_ALGO_TYPE_SHE) { @@ -85602,6 +85686,14 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) myCtx.hkdfPendArm = 0; myCtx.hkdfPendCount = 0; #endif +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) + myCtx.chachaPolyEncCount = 0; + myCtx.chachaPolyDecCount = 0; +#endif +#if (defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED)) + myCtx.pbkdf2Count = 0; + myCtx.pbkdf2Decline = 0; +#endif /* set devId to something other than INVALID_DEVID */ devId = 1; @@ -85628,6 +85720,284 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) ret = rsa_onlycb_test(&myCtx); PRIVATE_KEY_LOCK(); #endif +#if defined(WOLFSSL_SILABS_CRYPTOCB) && \ + defined(WOLFSSL_SILABS_WRAPPED_KEYS_API) && \ + defined(WOLFSSL_SILABS_CRYPTOCB_ECC) && defined(HAVE_ECC) + /* Binding a resident key over an ecc_key that held a software scalar must + * not leave it behind: the object stays typed ECC_PRIVATEKEY, so + * wc_ecc_export_private_only() would hand back the old secret. */ + if (ret == 0) { + ecc_key vaultEcc; + WC_RNG vaultRng; + byte priv[MAX_ECC_BYTES]; + word32 privSz = (word32)sizeof(priv); + byte wrapped[256]; + int haveKey = 0; + int haveRng = 0; + + if (wc_InitRng_ex(&vaultRng, HEAP_HINT, devId) != 0) + ret = WC_TEST_RET_ENC_NC; + else + haveRng = 1; + if (ret == 0) { + if (wc_ecc_init_ex(&vaultEcc, HEAP_HINT, devId) != 0) + ret = WC_TEST_RET_ENC_NC; + else + haveKey = 1; + } + /* A real software P-256 private key first. */ + if (ret == 0 && wc_ecc_make_key(&vaultRng, 32, &vaultEcc) != 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && + wc_ecc_export_private_only(&vaultEcc, priv, &privSz) != 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && privSz == 0) + ret = WC_TEST_RET_ENC_NC; + + /* Check both outcomes: a host shim declines the bind, real silicon + * accepts it. + * succeeded -> the old scalar must be gone + * declined -> the object must be untouched, catching a scrub that + * runs before the bind is known to succeed */ + if (ret == 0) { + byte after[MAX_ECC_BYTES]; + word32 afterSz = (word32)sizeof(after); + int bindRet; + int exportRet; + + XMEMSET(wrapped, 0, sizeof(wrapped)); + XMEMSET(after, 0, sizeof(after)); + bindRet = wc_SilabsSe_EccUseWrappedKey(&vaultEcc, wrapped, + sizeof(wrapped), ECC_SECP256R1); + exportRet = wc_ecc_export_private_only(&vaultEcc, after, &afterSz); + + if (bindRet == 0) { + /* Either the export refuses, or it yields nothing resembling + * the old scalar. Handing back the original is the failure. */ + if (exportRet == 0 && afterSz == privSz && + XMEMCMP(after, priv, privSz) == 0) { + ret = WC_TEST_RET_ENC_NC; + } + } + else { + /* A rejected bind must leave the key exactly as it was. */ + if (exportRet != 0 || afterSz != privSz || + XMEMCMP(after, priv, privSz) != 0) { + ret = WC_TEST_RET_ENC_NC; + } + } + ForceZero(after, sizeof(after)); + } + + if (haveKey) + wc_ecc_free(&vaultEcc); + if (haveRng) + wc_FreeRng(&vaultRng); + ForceZero(priv, sizeof(priv)); + } +#endif + +#if defined(WOLFSSL_SILABS_CRYPTOCB) && \ + defined(WOLFSSL_SILABS_WRAPPED_KEYS_API) && \ + defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) && !defined(NO_AES) + /* Argument handling of the Secure Vault key APIs. These run before the SE + * is consulted, so they are meaningful on a host build; the behavioural + * side needs real silicon and is covered on device. */ + if (ret == 0) { + Aes vaultAes; + word32 wrappedSz = 0; + byte blob[64]; + + /* NULL out-size, and key sizes the SE has no type for. */ + if (wc_SilabsSe_AesGetWrappedKeySize(256, NULL) != + WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && wc_SilabsSe_AesGetWrappedKeySize(0, &wrappedSz) == 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && wc_SilabsSe_AesGetWrappedKeySize(64, &wrappedSz) == 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && wc_SilabsSe_AesGetWrappedKeySize(255, &wrappedSz) == 0) + ret = WC_TEST_RET_ENC_NC; + + /* Binding rejects NULL arguments and an implausible blob length + * before it touches the Aes, so the object stays usable. */ + if (ret == 0 && wc_AesInit(&vaultAes, HEAP_HINT, devId) != 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0) { + if (wc_SilabsSe_AesUseWrappedKey(NULL, blob, sizeof(blob), 256) + != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && + wc_SilabsSe_AesUseWrappedKey(&vaultAes, NULL, sizeof(blob), + 256) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && + wc_SilabsSe_AesUseWrappedKey(&vaultAes, blob, 1, 256) == 0) + ret = WC_TEST_RET_ENC_NC; + /* A rejected bind must not have marked the object resident. */ + if (ret == 0 && vaultAes.ctx.keySet != 0) + ret = WC_TEST_RET_ENC_NC; + wc_AesFree(&vaultAes); + } + } +#endif + +#ifndef NO_SHA256 + /* Hash objects are initialised field by field, not by zeroing, so a port + * hanging lazy-init state off the object must clear it. Start from dirty + * storage: an unreset sentinel makes the device skip its own init. */ + if (ret == 0) { + WOLFSSL_SMALL_STACK_STATIC const byte abc[] = { 0x61, 0x62, 0x63 }; + WOLFSSL_SMALL_STACK_STATIC const byte abcHash[] = { + 0xBA,0x78,0x16,0xBF,0x8F,0x01,0xCF,0xEA, + 0x41,0x41,0x40,0xDE,0x5D,0xAE,0x22,0x23, + 0xB0,0x03,0x61,0xA3,0x96,0x17,0x7A,0x9C, + 0xB4,0x10,0xFF,0x61,0xF2,0x00,0x15,0xAD + }; + wc_Sha256 dirty; + byte digest[WC_SHA256_DIGEST_SIZE]; + + XMEMSET(&dirty, 0xA5, sizeof(dirty)); + if (wc_InitSha256_ex(&dirty, HEAP_HINT, devId) != 0) + ret = WC_TEST_RET_ENC_NC; +#ifdef WOLFSSL_SILABS_CRYPTOCB + /* Assert the sentinel directly: on a host build the shim declines + * every command so the flag is never set, and only real silicon would + * surface this through a wrong digest. */ + if (ret == 0 && dirty.silabsCtx.started != 0) + ret = WC_TEST_RET_ENC_NC; +#endif + if (ret == 0 && wc_Sha256Update(&dirty, abc, (word32)sizeof(abc)) != 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && wc_Sha256Final(&dirty, digest) != 0) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0 && XMEMCMP(digest, abcHash, sizeof(digest)) != 0) + ret = WC_TEST_RET_ENC_NC; + wc_Sha256Free(&dirty); + } +#endif + +/* pbkdf2_test() itself is only compiled when SHA-256 is available, and the + * decline sub-test below derives with WC_SHA256, so match that guard. */ +#if (defined(HAVE_PBKDF2) && !defined(NO_SHA256) && !defined(NO_HMAC) && \ + !defined(NO_PWDBASED)) + if (ret == 0) + ret = pbkdf2_test(); + /* Confirm the derivation actually crossed the callback boundary. */ + if (ret == 0 && myCtx.pbkdf2Count == 0) + ret = WC_TEST_RET_ENC_NC; + /* And that declining hands the work back to software: derive the same key + * with the device declining and with no device, and require a match. */ + if (ret == 0) { + WOLFSSL_SMALL_STACK_STATIC const byte pwd[] = "passwordPASSWORD"; + WOLFSSL_SMALL_STACK_STATIC const byte salt[] = "saltSALTsaltSALT"; + byte viaCb[24]; + byte viaSw[24]; + int cbRet; + int swRet; + + myCtx.pbkdf2Decline = 1; + cbRet = wc_PBKDF2_ex(viaCb, pwd, (int)XSTRLEN((const char*)pwd), + salt, (int)XSTRLEN((const char*)salt), 128, (int)sizeof(viaCb), + WC_SHA256, NULL, devId); + myCtx.pbkdf2Decline = 0; + + swRet = wc_PBKDF2_ex(viaSw, pwd, (int)XSTRLEN((const char*)pwd), + salt, (int)XSTRLEN((const char*)salt), 128, (int)sizeof(viaSw), + WC_SHA256, NULL, INVALID_DEVID); + + if (cbRet != 0 || swRet != 0) + ret = WC_TEST_RET_ENC_NC; + else if (XMEMCMP(viaCb, viaSw, sizeof(viaCb)) != 0) + ret = WC_TEST_RET_ENC_NC; + } +#endif +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) + if (ret == 0) + ret = chacha20_poly1305_aead_test(); + /* AEAD decrypt must fail closed through the callback, not just in + * software. Corrupt each authenticated input in turn and require + * MAC_CMP_FAILED_E every time, with no plaintext left behind. */ + if (ret == 0) { + WOLFSSL_SMALL_STACK_STATIC const byte cpKey[CHACHA20_POLY1305_AEAD_KEYSIZE] = { + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97, + 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f + }; + WOLFSSL_SMALL_STACK_STATIC const byte cpIV[CHACHA20_POLY1305_AEAD_IV_SIZE] = { + 0x07,0x00,0x00,0x00,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47 + }; + WOLFSSL_SMALL_STACK_STATIC const byte cpAAD[] = { + 0x50,0x51,0x52,0x53,0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7 + }; + byte cpPlain[16]; + byte cpCipher[16]; + byte cpOut[16]; + byte cpTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE]; + ChaCha cpChacha; + Poly1305 cpPoly; + int i; + + /* Bind the ChaCha context to this test's device, which is what routes + * the AEAD through the callback - the legacy one-shot carries no devId + * and deliberately stays in software. */ + XMEMSET(&cpChacha, 0, sizeof(cpChacha)); + XMEMSET(&cpPoly, 0, sizeof(cpPoly)); + XMEMSET(cpPlain, 0xA5, sizeof(cpPlain)); + ret = wc_Chacha_SetKey_ex(&cpChacha, cpKey, sizeof(cpKey), HEAP_HINT, + devId); + if (ret == 0) { + ret = wc_ChaCha20Poly1305_Encrypt_ex(&cpChacha, &cpPoly, cpCipher, + cpPlain, sizeof(cpPlain), cpIV, cpTag, cpAAD, sizeof(cpAAD)); + } + /* The encrypt above must have crossed the callback boundary. */ + if (ret == 0 && myCtx.chachaPolyEncCount == 0) + ret = WC_TEST_RET_ENC_NC; + + /* i = 0 tamper the tag, 1 the ciphertext, 2 the AAD */ + for (i = 0; ret == 0 && i < 3; i++) { + byte badAAD[sizeof(cpAAD)]; + byte badCipher[sizeof(cpCipher)]; + byte badTag[sizeof(cpTag)]; + int decRet; + + XMEMCPY(badAAD, cpAAD, sizeof(badAAD)); + XMEMCPY(badCipher, cpCipher, sizeof(badCipher)); + XMEMCPY(badTag, cpTag, sizeof(badTag)); + if (i == 0) + badTag[0] ^= 0x01; + else if (i == 1) + badCipher[0] ^= 0x01; + else + badAAD[0] ^= 0x01; + + XMEMSET(cpOut, 0x5A, sizeof(cpOut)); + decRet = wc_Chacha_SetKey_ex(&cpChacha, cpKey, sizeof(cpKey), + HEAP_HINT, devId); + if (decRet == 0) { + decRet = wc_ChaCha20Poly1305_Decrypt_ex(&cpChacha, &cpPoly, + cpOut, badCipher, sizeof(badCipher), cpIV, badTag, + badAAD, sizeof(badAAD)); + } + if (decRet != WC_NO_ERR_TRACE(MAC_CMP_FAILED_E)) { + ret = WC_TEST_RET_ENC_NC; + } + else { + /* The whole buffer must be zeroed, not merely different from + * the plaintext: leaving the sentinel untouched, or clearing + * only part of it, is still a leak of unauthenticated data. */ + word32 z; + for (z = 0; z < (word32)sizeof(cpOut); z++) { + if (cpOut[z] != 0) { + ret = WC_TEST_RET_ENC_NC; + break; + } + } + } + } + } +#endif #if defined(HAVE_ECC) PRIVATE_KEY_UNLOCK(); if (ret == 0) diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index 455f30088b..e5125a16b9 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -154,7 +154,7 @@ WOLFSSL_LOCAL void WC_ARG_NOT_NULL(1) GHASH(Gcm* gcm, const byte* a, #include #endif -#ifdef WOLFSSL_SILABS_SE_ACCEL +#ifdef WOLFSSL_SILABS_SE_TYPES #include #endif @@ -478,7 +478,7 @@ struct Aes { #if defined(WOLFSSL_IMXRT_DCP) dcp_handle_t handle; #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) silabs_aes_t ctx; #endif #ifdef WOLFSSL_MAXQ10XX_CRYPTO diff --git a/wolfssl/wolfcrypt/chacha.h b/wolfssl/wolfcrypt/chacha.h index 1591b5e7c7..ea344d5c64 100644 --- a/wolfssl/wolfcrypt/chacha.h +++ b/wolfssl/wolfcrypt/chacha.h @@ -99,6 +99,13 @@ typedef struct ChaCha { ALIGN8 word32 over[CHACHA_CHUNK_WORDS]; #endif WC_BITFIELD keySet:1; /* set to 1 once a key is set */ +#ifdef WOLF_CRYPTO_CB + /* X[] holds expanded state, but a device is handed a plaintext key, so + * keep a copy as Aes.devKey does. Set by wc_Chacha_SetKey_ex(). */ + byte devKey[CHACHA_MAX_KEY_SZ]; + word32 devKeySz; + int devId; +#endif } ChaCha; /** @@ -111,6 +118,11 @@ WOLFSSL_API int wc_Chacha_Process(ChaCha* ctx, byte* cipher, const byte* plain, word32 msglen); WOLFSSL_API int wc_Chacha_SetKey(ChaCha* ctx, const byte* key, word32 keySz); +/* As wc_Chacha_SetKey(), binding the context to a crypto callback device so + * wc_ChaCha20Poly1305_Encrypt_ex()/_Decrypt_ex() can offload it. Pass + * INVALID_DEVID for software; heap is unused. */ +WOLFSSL_API int wc_Chacha_SetKey_ex(ChaCha* ctx, const byte* key, word32 keySz, + void* heap, int devId); #ifdef HAVE_XCHACHA WOLFSSL_LOCAL void wc_Chacha_purge_current_block(ChaCha* ctx); diff --git a/wolfssl/wolfcrypt/chacha20_poly1305.h b/wolfssl/wolfcrypt/chacha20_poly1305.h index 051430f4b9..44757c3d01 100644 --- a/wolfssl/wolfcrypt/chacha20_poly1305.h +++ b/wolfssl/wolfcrypt/chacha20_poly1305.h @@ -127,6 +127,18 @@ int wc_ChaCha20Poly1305_CheckTag( /* Implementation of AEAD, which includes support for adding data, then final calculation of authentication tag */ +/* As wc_ChaCha20Poly1305_Init(), binding the embedded ChaCha to a crypto + * callback device. Pass INVALID_DEVID for software; heap is unused. + * + * Note the streaming calls below (UpdateAad/Update/Final) always run in + * software: they build the tag incrementally, which the one-shot AEAD callback + * cannot express. The devId reaches the embedded ChaCha, so offload happens + * only through wc_ChaCha20Poly1305_Encrypt_ex()/_Decrypt_ex(). */ +WOLFSSL_API int wc_ChaCha20Poly1305_Init_ex(ChaChaPoly_Aead* aead, + const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], + const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], + int isEncrypt, void* heap, int devId); + WOLFSSL_API int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE], const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE], diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index 9c32ecdf23..866807ab7d 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -598,7 +598,8 @@ typedef struct wc_CryptoInfo { }; #endif } pk; -#if !defined(NO_AES) || !defined(NO_DES3) +#if !defined(NO_AES) || !defined(NO_DES3) || \ + (defined(HAVE_CHACHA) && defined(HAVE_POLY1305)) struct { int type; /* enum wc_CipherType */ int enc; @@ -681,12 +682,34 @@ typedef struct wc_CryptoInfo { int pad; /* 1 = RFC 5649 padded, 0 = RFC 3394 */ } aeskeywrap; #endif + #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) + struct { /* ChaCha20-Poly1305 AEAD one-shot */ + const byte* inKey; /* CHACHA20_POLY1305_AEAD_KEYSIZE */ + const byte* inIV; /* CHACHA20_POLY1305_AEAD_IV_SIZE */ + const byte* inAAD; /* optional additional data */ + const byte* in; /* plaintext */ + byte* out; /* ciphertext */ + byte* outAuthTag;/* CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE */ + word32 inAADSz; + word32 inSz; + } chacha20_poly1305_enc; + struct { + const byte* inKey; + const byte* inIV; + const byte* inAAD; + const byte* in; /* ciphertext */ + const byte* inAuthTag; /* tag to verify */ + byte* out; /* plaintext */ + word32 inAADSz; + word32 inSz; + } chacha20_poly1305_dec; + #endif /* HAVE_CHACHA && HAVE_POLY1305 */ void* ctx; #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES }; #endif } cipher; -#endif /* !NO_AES || !NO_DES3 */ +#endif /* !NO_AES || !NO_DES3 || (HAVE_CHACHA && HAVE_POLY1305) */ #if !defined(NO_SHA) || !defined(NO_SHA256) || \ defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA3) struct { @@ -931,7 +954,8 @@ typedef struct wc_CryptoInfo { } op; } keystore; #endif /* WOLF_CRYPTO_CB_KEYSTORE */ -#if defined(HAVE_HKDF) || defined(HAVE_CMAC_KDF) +#if defined(HAVE_HKDF) || defined(HAVE_CMAC_KDF) || \ + (defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED)) struct { int type; /* enum wc_KdfType */ #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES @@ -979,12 +1003,24 @@ typedef struct wc_CryptoInfo { word32 outSz; /* Desired size of out key material. */ } twostep_cmac; #endif /* HAVE_CMAC_KDf */ + #if (defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED)) + struct { /* PBKDF2 (PKCS#5 v2.0) */ + byte* output; /* derived key out, kLen bytes */ + const byte* passwd; + const byte* salt; + int pLen; + int sLen; + int iterations; + int kLen; + int hashType; /* enum wc_HashType */ + } pbkdf2; + #endif /* HAVE_PBKDF2 && !NO_HMAC && !NO_PWDBASED */ /* Future KDF type structures here */ #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES }; #endif } kdf; -#endif /* HAVE_HKDF || HAVE_CMAC_KDF */ +#endif /* HAVE_HKDF || HAVE_CMAC_KDF || (HAVE_PBKDF2 && !NO_HMAC) */ #ifdef HAVE_ANONYMOUS_INLINE_AGGREGATES }; #endif @@ -1201,6 +1237,20 @@ WOLFSSL_LOCAL int wc_CryptoCb_PqcSignatureCheckPrivKey(void* key, int type, const byte* pubKey, word32 pubKeySz); #endif /* HAVE_FALCON || WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */ +#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) +/* ChaCha20-Poly1305 AEAD, dispatched on the devId bound by + * wc_Chacha_SetKey_ex() or wc_ChaCha20Poly1305_Init_ex(). The legacy one-shot + * wc_ChaCha20Poly1305_Encrypt()/_Decrypt() carry no devId and are not routed + * here - there would be nothing to select a device on. */ +WOLFSSL_LOCAL int wc_CryptoCb_Chacha20Poly1305Encrypt(int devId, + const byte* inKey, const byte* inIV, const byte* inAAD, word32 inAADSz, + const byte* in, word32 inSz, byte* out, byte* outAuthTag); + +WOLFSSL_LOCAL int wc_CryptoCb_Chacha20Poly1305Decrypt(int devId, + const byte* inKey, const byte* inIV, const byte* inAAD, word32 inAADSz, + const byte* in, word32 inSz, const byte* inAuthTag, byte* out); +#endif /* HAVE_CHACHA && HAVE_POLY1305 */ + #ifndef NO_AES #ifdef HAVE_AESGCM WOLFSSL_LOCAL int wc_CryptoCb_AesGcmEncrypt(Aes* aes, byte* out, @@ -1327,6 +1377,12 @@ WOLFSSL_LOCAL int wc_CryptoCb_Hkdf_Expand(int hashType, const byte* inKey, byte* out, word32 outSz, int devId); #endif /* HAVE_HKDF && !NO_HMAC */ +#if (defined(HAVE_PBKDF2) && !defined(NO_HMAC) && !defined(NO_PWDBASED)) +WOLFSSL_LOCAL int wc_CryptoCb_Pbkdf2(byte* output, const byte* passwd, int pLen, + const byte* salt, int sLen, int iterations, int kLen, int hashType, + int devId); +#endif /* HAVE_PBKDF2 && !NO_HMAC && !NO_PWDBASED */ + #if defined(HAVE_CMAC_KDF) WOLFSSL_LOCAL int wc_CryptoCb_Kdf_TwostepCmac(const byte * salt, word32 saltSz, const byte* z, word32 zSz, diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index d3785827f4..0e4a5c3273 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -55,7 +55,7 @@ #include #endif -#ifdef WOLFSSL_SILABS_SE_ACCEL +#ifdef WOLFSSL_SILABS_SE_TYPES #include #endif @@ -186,7 +186,7 @@ enum { ECC_MAX_CRYPTO_HW_PUBKEY_SIZE = (ATECC_KEY_SIZE*2), #elif defined(PLUTON_CRYPTO_ECC) ECC_MAX_CRYPTO_HW_SIZE = 32, -#elif defined(WOLFSSL_SILABS_SE_ACCEL) +#elif defined(WOLFSSL_SILABS_SE_TYPES) #if defined(_SILICON_LABS_SECURITY_FEATURE) && \ (_SILICON_LABS_SECURITY_FEATURE == \ _SILICON_LABS_SECURITY_FEATURE_VAULT) && \ @@ -571,7 +571,7 @@ struct ecc_key { void* devCtx; int devId; #endif -#ifdef WOLFSSL_SILABS_SE_ACCEL +#ifdef WOLFSSL_SILABS_SE_TYPES sl_se_command_context_t cmd_ctx; sl_se_key_descriptor_t key; /* Used for SiLabs "plaintext" with public X, public Y, and @@ -579,6 +579,10 @@ struct ecc_key { * offset `keysize`, and offset `2 * keysize`. */ byte key_raw[3 * ECC_MAX_CRYPTO_HW_SIZE]; + /* Non-zero once wc_SilabsSe_EccUseWrappedKey() or + * wc_SilabsSe_EccUseBuiltInKey() has bound a device-resident key, so the + * crypto callback port does not overwrite the descriptor from key_raw. */ + byte silabsKeySet; #endif #ifdef WOLFSSL_MAXQ10XX_CRYPTO maxq_ecc_t maxq_ctx; diff --git a/wolfssl/wolfcrypt/hash.h b/wolfssl/wolfcrypt/hash.h index 9195bfe0e2..7fb43929df 100644 --- a/wolfssl/wolfcrypt/hash.h +++ b/wolfssl/wolfcrypt/hash.h @@ -56,6 +56,19 @@ #include #endif +/* Clear the Silicon Labs SE context carried alongside the software members of + * wc_Sha and wc_Sha256. That port uses its "started" flag as a lazy-init + * sentinel (wolfCrypt has no callback hook on wc_InitShaXXX), and the init + * paths set fields individually rather than zeroing, so a fresh object whose + * storage held a non-zero byte would skip SE init and hand an uninitialised + * context to update or final. Call it AFTER the NULL check, never before. */ +#ifdef WOLFSSL_SILABS_CRYPTOCB + #define WC_SILABS_CLEAR_HASH_CTX(h) \ + XMEMSET(&(h)->silabsCtx, 0, sizeof((h)->silabsCtx)) +#else + #define WC_SILABS_CLEAR_HASH_CTX(h) WC_DO_NOTHING +#endif + #ifdef __cplusplus extern "C" { diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index 777a961937..5685013716 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -125,10 +125,6 @@ noinst_HEADERS+= \ wolfssl/wolfcrypt/port/caam/caam_driver.h \ wolfssl/wolfcrypt/port/caam/caam_error.h \ wolfssl/wolfcrypt/port/caam/caam_qnx.h \ - wolfssl/wolfcrypt/port/silabs/silabs_aes.h \ - wolfssl/wolfcrypt/port/silabs/silabs_ecc.h \ - wolfssl/wolfcrypt/port/silabs/silabs_hash.h \ - wolfssl/wolfcrypt/port/silabs/silabs_random.h \ wolfssl/wolfcrypt/port/st/stm32.h \ wolfssl/wolfcrypt/port/st/stsafe.h \ wolfssl/wolfcrypt/port/realtek/rtl8735b.h \ @@ -152,6 +148,17 @@ noinst_HEADERS+= \ wolfssl/wolfcrypt/libwolfssl_sources_asm.h +# Installed unconditionally: settings.h includes silabs_settings.h whenever +# WOLFSSL_SILABS_CRYPTOCB is set, so a consumer of an installed tree needs it +# (and, through silabs_cryptocb.h, the host shim) to preprocess wolfssl/ssl.h. +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/silabs/silabs_aes.h +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/silabs/silabs_ecc.h +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/silabs/silabs_hash.h +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/silabs/silabs_random.h +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/silabs/silabs_settings.h +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/silabs/silabs_cryptocb.h +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/silabs/silabs_shim.h + if BUILD_CRYPTOAUTHLIB nobase_include_HEADERS+= wolfssl/wolfcrypt/port/atmel/atmel.h endif diff --git a/wolfssl/wolfcrypt/port/silabs/silabs_aes.h b/wolfssl/wolfcrypt/port/silabs/silabs_aes.h index 7febb7645a..e6de372134 100644 --- a/wolfssl/wolfcrypt/port/silabs/silabs_aes.h +++ b/wolfssl/wolfcrypt/port/silabs/silabs_aes.h @@ -22,23 +22,49 @@ #ifndef _SILABS_AES_H_ #define _SILABS_AES_H_ -#include +#include -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include -#include -#include +#ifdef WOLFSSL_SILABS_HOST_TEST + #include +#else + #include + + #include + #include +#endif typedef struct { sl_se_command_context_t cmd_ctx; sl_se_key_descriptor_t key; + /* Non-zero once wc_SilabsSe_AesUseWrappedKey() or ...UseBuiltInKey() has + * bound a resident key, after which the callback port leaves the descriptor + * alone instead of rebuilding it from a plaintext key. */ + byte keySet; } silabs_aes_t; typedef struct Aes Aes; -#ifdef WOLFSSL_AES_DIRECT +/* Shared SE helpers, used by both the direct port (WOLFSSL_SILABS_SE_ACCEL) + * and the crypto callback port. */ +int silabs_aes_init_key_desc(silabs_aes_t* ctx, const byte* key, word32 keylen); +int silabs_aes_ecb(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir); +int silabs_aes_cbc(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir); + +/* Raw-status forms of the above: negative is a wolfCrypt argument error, + * anything else is the SE status unchanged, so an unsupported command can be + * told from a hardware failure. The callback port uses these. */ +int silabs_aes_ecb_status(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir); +int silabs_aes_cbc_status(Aes* aes, byte* out, const byte* in, word32 sz, + sl_se_cipher_operation_t dir); + +#if defined(WOLFSSL_SILABS_SE_ACCEL) && defined(WOLFSSL_AES_DIRECT) int wc_AesEncrypt(Aes* aes, const byte* inBlock, byte* outBlock); int wc_AesDecrypt(Aes* aes, const byte* inBlock, byte* outBlock); #endif @@ -53,6 +79,15 @@ int wc_AesGcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, const byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz); +int wc_AesGcmEncrypt_silabs_status (Aes* aes, byte* out, const byte* in, + word32 sz, const byte* iv, word32 ivSz, + byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz); +int wc_AesGcmDecrypt_silabs_status (Aes* aes, byte* out, const byte* in, + word32 sz, const byte* iv, word32 ivSz, + const byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz); + #endif /* HAVE_AESGCM */ #ifdef HAVE_AESCCM @@ -65,8 +100,17 @@ int wc_AesCcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, const byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz); +int wc_AesCcmEncrypt_silabs_status (Aes* aes, byte* out, const byte* in, + word32 sz, const byte* iv, word32 ivSz, + byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz); +int wc_AesCcmDecrypt_silabs_status (Aes* aes, byte* out, const byte* in, + word32 sz, const byte* iv, word32 ivSz, + const byte* authTag, word32 authTagSz, + const byte* authIn, word32 authInSz); + #endif /* HAVE_AESCCM */ -#endif /* defined(WOLFSSL_SILABS_SE_ACCEL) */ +#endif /* defined(WOLFSSL_SILABS_SE_TYPES) */ #endif /* _SILABS_AES_H_ */ diff --git a/wolfssl/wolfcrypt/port/silabs/silabs_cryptocb.h b/wolfssl/wolfcrypt/port/silabs/silabs_cryptocb.h new file mode 100644 index 0000000000..9230718aa2 --- /dev/null +++ b/wolfssl/wolfcrypt/port/silabs/silabs_cryptocb.h @@ -0,0 +1,149 @@ +/* silabs_cryptocb.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 + */ + +#ifndef _SILABS_CRYPTOCB_H_ +#define _SILABS_CRYPTOCB_H_ + +#include + +#ifdef WOLFSSL_SILABS_CRYPTOCB + +#include +#include +#ifndef NO_AES + #include +#endif +#ifdef HAVE_ECC + #include +#endif + +/* Pulled in for _SILICON_LABS_SECURITY_FEATURE, which gates the wrapped-key + * API below. The sibling silabs headers include it the same way. */ +#ifdef WOLFSSL_SILABS_HOST_TEST + #include +#else + #include +#endif + +/* Declare the wrapped-key API on exactly the parts that compile it (Vault + * High), so a Vault Mid build fails at the call site with an undeclared + * function rather than at link time. WOLFSSL_SILABS_NO_VAULT_KEYS suppresses + * the API on a Vault High part. */ +#if defined(_SILICON_LABS_SECURITY_FEATURE) && \ + (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) \ + && !defined(WOLFSSL_SILABS_NO_VAULT_KEYS) + #define WOLFSSL_SILABS_WRAPPED_KEYS_API +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Register / unregister the Secure Element device. wolfCrypt_Init registers it + * at WOLFSSL_SILABS_DEVID automatically; call these only for a different device + * id or an early teardown. 0 on success, negative wolfCrypt error otherwise. */ +WOLFSSL_API int wc_SilabsCryptoCb_RegisterDevice(int devId); +WOLFSSL_API int wc_SilabsCryptoCb_UnRegisterDevice(int devId); + +/* ---- Secure Vault key management ---------------------------------------- + * + * A wrapped key is encrypted to a device-unique key - the SE can use it, the + * application never sees the material. A built-in key lives in an SE slot and + * is named rather than supplied. Both bind to an ordinary Aes or ecc_key, + * after which the normal wolfCrypt calls run on the SE against that key. + * + * The descriptor references the caller's wrapped buffer without copying, so + * that buffer must outlive the bound Aes / ecc_key. Wrapped keys need Vault + * High; built-in slots do not. 0 on success, negative wolfCrypt error. */ + +/* Bound only to be used by the cipher engine; without it a bind would erase + * the software key and then dispatch would decline, leaving nothing usable. */ +#if !defined(NO_AES) && defined(WOLFSSL_SILABS_WRAPPED_KEYS_API) && \ + defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) +/* Size of the wrapped blob for an AES key of keyBits bits. */ +WOLFSSL_API int wc_SilabsSe_AesGetWrappedKeySize(int keyBits, word32* outSz); +/* Generate an AES key inside the SE, returning only its wrapped form. */ +WOLFSSL_API int wc_SilabsSe_AesGenerateWrappedKey(int keyBits, byte* out, + word32* outSz); +/* Bind a wrapped AES key to an Aes. */ +WOLFSSL_API int wc_SilabsSe_AesUseWrappedKey(Aes* aes, const byte* wrapped, + word32 wrappedSz, int keyBits); +#endif +#if !defined(NO_AES) && defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) +/* Bind an SE built-in AES slot (for example + * SL_SE_KEY_SLOT_APPLICATION_AES_128_KEY) to an Aes. */ +WOLFSSL_API int wc_SilabsSe_AesUseBuiltInKey(Aes* aes, int slot, int keyBits); +#endif + +/* As above: these are only usable when the ECC engine is compiled in. */ +#if defined(HAVE_ECC) && defined(WOLFSSL_SILABS_CRYPTOCB_ECC) +#if defined(WOLFSSL_SILABS_WRAPPED_KEYS_API) +WOLFSSL_API int wc_SilabsSe_EccGetWrappedKeySize(int curveId, word32* outSz); +/* Generate an ECC key inside the SE. wrapped receives the private key in + * wrapped form; pubOut, when not NULL, receives the public point as X||Y. */ +WOLFSSL_API int wc_SilabsSe_EccGenerateWrappedKey(int curveId, byte* wrapped, + word32* wrappedSz, byte* pubOut, word32* pubOutSz); +WOLFSSL_API int wc_SilabsSe_EccUseWrappedKey(ecc_key* key, const byte* wrapped, + word32 wrappedSz, int curveId); +#endif +/* Bind an SE built-in ECC slot (for example + * SL_SE_KEY_SLOT_APPLICATION_ATTESTATION_KEY) to an ecc_key. */ +WOLFSSL_API int wc_SilabsSe_EccUseBuiltInKey(ecc_key* key, int slot, + int curveId); +#endif /* HAVE_ECC && WOLFSSL_SILABS_CRYPTOCB_ECC */ + +/* Engine entry points. Each runs the whole operation and returns 0 when the SE + * handled it, CRYPTOCB_UNAVAILABLE to fall back to software, or a negative + * wolfCrypt error. */ +#ifdef WOLFSSL_SILABS_CRYPTOCB_TRNG +WOLFSSL_LOCAL int wc_SilabsRng(wc_CryptoInfo* info); +#endif +#ifdef WOLFSSL_SILABS_CRYPTOCB_HASH +WOLFSSL_LOCAL int wc_SilabsHash(wc_CryptoInfo* info); +#endif +#ifdef WOLFSSL_SILABS_CRYPTOCB_CIPHER +WOLFSSL_LOCAL int wc_SilabsCipher(wc_CryptoInfo* info); +#endif +#if defined(WOLFSSL_SILABS_CRYPTOCB_CMAC) && defined(WOLFSSL_CMAC) && \ + !defined(NO_AES) +WOLFSSL_LOCAL int wc_SilabsCmac(wc_CryptoInfo* info); +#endif +#if defined(WOLFSSL_SILABS_CRYPTOCB_ECC) && defined(HAVE_ECC) +WOLFSSL_LOCAL int wc_SilabsPk(wc_CryptoInfo* info); +#endif +#ifdef WOLFSSL_SILABS_CRYPTOCB_KDF +WOLFSSL_LOCAL int wc_SilabsKdf(wc_CryptoInfo* info); +#endif + +/* Map an SE status to a wolfCrypt error. */ +WOLFSSL_LOCAL int silabs_cb_status(int slStatus); + +/* Map a wolfCrypt hash type to an SE hash type, or SL_SE_HASH_NONE when the + * SE cannot do it. */ +WOLFSSL_LOCAL int silabs_cb_hash_type(int wcHashType, int* digestSz); + +#ifdef __cplusplus + } +#endif + +#endif /* WOLFSSL_SILABS_CRYPTOCB */ + +#endif /* _SILABS_CRYPTOCB_H_ */ diff --git a/wolfssl/wolfcrypt/port/silabs/silabs_ecc.h b/wolfssl/wolfcrypt/port/silabs/silabs_ecc.h index f3c09fa233..317781b96e 100644 --- a/wolfssl/wolfcrypt/port/silabs/silabs_ecc.h +++ b/wolfssl/wolfcrypt/port/silabs/silabs_ecc.h @@ -23,39 +23,56 @@ #ifndef _SILABS_ECC_H_ #define _SILABS_ECC_H_ +#include -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include -#include -#include -#include -#include +#ifdef WOLFSSL_SILABS_HOST_TEST + #include +#else + #include + #include + #include + #include +#endif typedef struct ecc_key ecc_key; int silabs_ecc_sign_hash (const byte* in, word32 inlen, byte* out, word32 *outlen, ecc_key* key); +/* Raw-status form: negative is a wolfCrypt error, otherwise the SE status + * unchanged, so the crypto callback port can map an unsupported command to + * CRYPTOCB_UNAVAILABLE and fall back instead of hard-failing. */ +int silabs_ecc_sign_hash_status (const byte* in, word32 inlen, + byte* out, word32 *outlen, + ecc_key* key); int silabs_ecc_verify_hash (const byte* sig, word32 siglen, const byte* hash, word32 hashlen, int* stat, ecc_key* key); +int silabs_ecc_verify_hash_status (const byte* sig, word32 siglen, + const byte* hash, word32 hashlen, + int* stat, ecc_key* key); int silabs_ecc_make_key(ecc_key* key, int keysize); +int silabs_ecc_make_key_status(ecc_key* key, int keysize); int silabs_ecc_import(ecc_key* key, word32 keysize, int pub, int priv); int silabs_ecc_export_public(ecc_key* key, sl_se_key_descriptor_t* seKey); int silabs_ecc_shared_secret(ecc_key* private_key, ecc_key* public_key, byte* out, word32* outlen); +int silabs_ecc_shared_secret_status(ecc_key* private_key, ecc_key* public_key, + byte* out, word32* outlen); #if (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) int silabs_ecc_load_vault(ecc_key* key); #endif -#endif /* WOLFSSL_SILABS_SE_ACCEL */ +#endif /* WOLFSSL_SILABS_SE_TYPES */ #endif /* _SILABS_ECC_H_ */ diff --git a/wolfssl/wolfcrypt/port/silabs/silabs_hash.h b/wolfssl/wolfcrypt/port/silabs/silabs_hash.h index 4632080be9..0aa48684b2 100644 --- a/wolfssl/wolfcrypt/port/silabs/silabs_hash.h +++ b/wolfssl/wolfcrypt/port/silabs/silabs_hash.h @@ -24,14 +24,18 @@ #include -#if defined(WOLFSSL_SILABS_SE_ACCEL) - -#include +#if defined(WOLFSSL_SILABS_SE_TYPES) #include -#include -#include +#ifdef WOLFSSL_SILABS_HOST_TEST + #include +#else + #include + + #include + #include +#endif /* workaround to detect older Gecko SDK version 3 */ #if !defined(WOLFSSL_SILABS_SE_ACCEL_3) && !defined(SL_SE_PRF_HMAC_SHA1) @@ -39,12 +43,25 @@ #define WOLFSSL_SILABS_SE_ACCEL_3 #endif -/* Enable SHA2-2384 and SHA2-512 if HW supports and enabled */ +/* SHA-384 and SHA-512 need Secure Vault High. WOLFSSL_SILABS_SE_SHA384/512 + * mean "the SE can do it" and gate the SE context and the callback port. */ #if (_SILICON_LABS_SECURITY_FEATURE == _SILICON_LABS_SECURITY_FEATURE_VAULT) #ifdef WOLFSSL_SHA384 - #define WOLFSSL_SILABS_SHA384 + #define WOLFSSL_SILABS_SE_SHA384 #endif #ifdef WOLFSSL_SHA512 + #define WOLFSSL_SILABS_SE_SHA512 + #endif +#endif + +/* WOLFSSL_SILABS_SHA384/512 additionally mean "replace the software + * implementation", which is only true for the direct port. The callback port + * keeps software compiled in so an unsupported request can fall back. */ +#ifdef WOLFSSL_SILABS_SE_ACCEL + #ifdef WOLFSSL_SILABS_SE_SHA384 + #define WOLFSSL_SILABS_SHA384 + #endif + #ifdef WOLFSSL_SILABS_SE_SHA512 #define WOLFSSL_SILABS_SHA512 #endif #endif @@ -58,13 +75,17 @@ typedef struct { sl_se_sha1_streaming_context_t sha1_ctx; sl_se_sha224_streaming_context_t sha224_ctx; sl_se_sha256_streaming_context_t sha256_ctx; - #ifdef WOLFSSL_SILABS_SHA384 + #ifdef WOLFSSL_SILABS_SE_SHA384 sl_se_sha384_streaming_context_t sha384_ctx; #endif - #ifdef WOLFSSL_SILABS_SHA512 + #ifdef WOLFSSL_SILABS_SE_SHA512 sl_se_sha512_streaming_context_t sha512_ctx; #endif } hash_type_ctx; + /* Set once the SE context has been started. The crypto callback port has + * no hook on wc_InitShaXXX, so it starts the SE lazily on the first + * update or final; wc_InitShaXXX zeroes the struct, clearing this. */ + byte started; } wc_silabs_sha_t; #else /* Gecko SDK v4 or later uses "multipart" interface */ @@ -74,16 +95,26 @@ typedef struct { sl_se_sha1_multipart_context_t sha1_ctx; sl_se_sha224_multipart_context_t sha224_ctx; sl_se_sha256_multipart_context_t sha256_ctx; - #ifdef WOLFSSL_SILABS_SHA384 + #ifdef WOLFSSL_SILABS_SE_SHA384 sl_se_sha384_multipart_context_t sha384_ctx; #endif - #ifdef WOLFSSL_SILABS_SHA512 + #ifdef WOLFSSL_SILABS_SE_SHA512 sl_se_sha512_multipart_context_t sha512_ctx; #endif } hash_type_ctx; + /* See the note on the streaming variant above. */ + byte started; } wc_silabs_sha_t; #endif +/* Return the raw SE Manager status so callers can separate an unsupported + * command from a hardware failure. */ +int wc_silabs_se_hash_init_status(wc_silabs_sha_t* sha, enum wc_HashType type); +int wc_silabs_se_hash_update_status(wc_silabs_sha_t* sha, const byte* data, + word32 len); +int wc_silabs_se_hash_final_status(wc_silabs_sha_t* sha, byte* hash, + word32 len); + int wc_silabs_se_hash_init(wc_silabs_sha_t* sha, enum wc_HashType type); int wc_silabs_se_hash_update(wc_silabs_sha_t* sha, const byte* data, word32 len); @@ -91,6 +122,6 @@ int wc_silabs_se_hash_final(wc_silabs_sha_t* sha, byte* hash, word32 len); -#endif /* WOLFSSL_SILABS_SE_ACCEL */ +#endif /* WOLFSSL_SILABS_SE_TYPES */ #endif /* _SILABS_HASH_H_ */ diff --git a/wolfssl/wolfcrypt/port/silabs/silabs_random.h b/wolfssl/wolfcrypt/port/silabs/silabs_random.h index 3efe66f3b6..2f8f9db09b 100644 --- a/wolfssl/wolfcrypt/port/silabs/silabs_random.h +++ b/wolfssl/wolfcrypt/port/silabs/silabs_random.h @@ -23,13 +23,14 @@ #ifndef _SILABS_RANDOM_H_ #define _SILABS_RANDOM_H_ +#include -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include int silabs_GenerateRand(byte* output, word32 sz); -#endif /* WOLFSSL_SILABS_SE_ACCEL */ +#endif /* WOLFSSL_SILABS_SE_TYPES */ #endif /* _SILABS_RANDOM_H_ */ diff --git a/wolfssl/wolfcrypt/port/silabs/silabs_settings.h b/wolfssl/wolfcrypt/port/silabs/silabs_settings.h new file mode 100644 index 0000000000..a0dcabc8d5 --- /dev/null +++ b/wolfssl/wolfcrypt/port/silabs/silabs_settings.h @@ -0,0 +1,110 @@ +/* silabs_settings.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 + */ + +/* Compile time configuration for the Silicon Labs crypto callback port. Macros + * only, no SDK headers, so settings.h can include it to select engines and map + * WC_USE_DEVID before the wolfcrypt test and benchmark read it. + * + * WOLFSSL_SILABS_CRYPTOCB enables the port and must always be defined. On its + * own it offloads every supported engine. To offload a subset, also define one + * or more of these, in which case only those are offloaded: + * WOLFSSL_SILABS_CRYPTOCB_TRNG + * WOLFSSL_SILABS_CRYPTOCB_HASH + * WOLFSSL_SILABS_CRYPTOCB_CIPHER + * WOLFSSL_SILABS_CRYPTOCB_CMAC + * WOLFSSL_SILABS_CRYPTOCB_ECC + * WOLFSSL_SILABS_CRYPTOCB_KDF + * An engine macro on its own does not enable the port. + * + * There is no RSA engine - Series 2 has no RSA hardware. There is no HMAC + * engine either: wolfCrypt gives the inner and outer hash contexts the Hmac's + * devId, so every block is already offloaded through the hash engine, and the + * SE's own HMAC state cannot buffer a partial block. + */ + +#ifndef WOLFSSL_SILABS_SETTINGS_H +#define WOLFSSL_SILABS_SETTINGS_H + +#ifdef WOLFSSL_SILABS_CRYPTOCB + +/* The two SiLabs ports are mutually exclusive. The direct port replaces the + * software implementations at compile time, which would leave the callback + * port with nothing to fall back to when the SE declines an operation. */ +#ifdef WOLFSSL_SILABS_SE_ACCEL + #error "SILABS_CRYPTOCB and SILABS_SE_ACCEL are mutually exclusive" +#endif + +/* The port routes operations through the wolfSSL crypto callback framework. */ +#ifndef WOLF_CRYPTO_CB + #define WOLF_CRYPTO_CB +#endif + +/* CMAC hangs its SE multipart state off the object devCtx, so the port needs + * the free callback to release it. */ +#ifndef WOLF_CRYPTO_CB_FREE + #define WOLF_CRYPTO_CB_FREE +#endif + +/* Pulls the SE context members into Aes, ecc_key and wc_Sha*, shared with the + * direct port. Defined in settings.h for WOLFSSL_SILABS_SE_ACCEL as well. */ +#ifndef WOLFSSL_SILABS_SE_TYPES + #define WOLFSSL_SILABS_SE_TYPES +#endif + +/* If the port is on but no specific engine was requested, enable the full + * supported set. */ +#if !defined(WOLFSSL_SILABS_CRYPTOCB_TRNG) && \ + !defined(WOLFSSL_SILABS_CRYPTOCB_HASH) && \ + !defined(WOLFSSL_SILABS_CRYPTOCB_CIPHER) && \ + !defined(WOLFSSL_SILABS_CRYPTOCB_CMAC) && \ + !defined(WOLFSSL_SILABS_CRYPTOCB_ECC) && \ + !defined(WOLFSSL_SILABS_CRYPTOCB_KDF) + #define WOLFSSL_SILABS_CRYPTOCB_TRNG + #define WOLFSSL_SILABS_CRYPTOCB_HASH + #define WOLFSSL_SILABS_CRYPTOCB_CIPHER + #define WOLFSSL_SILABS_CRYPTOCB_CMAC + #ifdef HAVE_ECC + #define WOLFSSL_SILABS_CRYPTOCB_ECC + #endif + #define WOLFSSL_SILABS_CRYPTOCB_KDF +#endif + + +/* Device id for the SE crypto callback; set WOLFSSL_SILABS_DEVID (or + * WC_USE_DEVID) to any int but INVALID_DEVID (-2), an id not an address. */ +#ifndef WOLFSSL_SILABS_DEVID + #define WOLFSSL_SILABS_DEVID 0x5345 /* 'SE' */ +#endif + +/* Route the unmodified wolfcrypt test and benchmark through this device. + * + * Not under the host compile-test, where the SE Manager stub does no crypto + * and every known-answer vector would fail. That build is a compile gate; + * correctness is established on EFR32 silicon. */ +#ifndef WOLFSSL_SILABS_HOST_TEST + #ifndef WC_USE_DEVID + #define WC_USE_DEVID WOLFSSL_SILABS_DEVID + #endif +#endif + +#endif /* WOLFSSL_SILABS_CRYPTOCB */ + +#endif /* WOLFSSL_SILABS_SETTINGS_H */ diff --git a/wolfssl/wolfcrypt/port/silabs/silabs_shim.h b/wolfssl/wolfcrypt/port/silabs/silabs_shim.h new file mode 100644 index 0000000000..1122a819ad --- /dev/null +++ b/wolfssl/wolfcrypt/port/silabs/silabs_shim.h @@ -0,0 +1,399 @@ +/* silabs_shim.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 + */ + +/* Host compile-test stand-in for the slice of the Silicon Labs SE Manager the + * port references. Compiled ONLY under WOLFSSL_SILABS_HOST_TEST, it lets CI + * build the port's dispatch and compile-time guards with no Simplicity SDK + * installed. On target the real SE Manager headers are used instead. + * + * Every stub returns SL_STATUS_NOT_SUPPORTED and does NO crypto, which the + * port maps to CRYPTOCB_UNAVAILABLE, so this build also exercises every + * engine's decline-and-fall-back path. Correctness is validated on EFR32 + * silicon, never through these stubs. + * + * Signatures mirror Simplicity SDK + * platform/security/sl_component/se_manager/inc/. Add a stub here when the + * port starts calling a new sl_se_* function. The device is modeled as + * Series 2 Config 5 (EFR32xG25) with Secure Vault High. + */ + +#ifndef _WOLFPORT_SILABS_SHIM_H_ +#define _WOLFPORT_SILABS_SHIM_H_ + +#ifdef WOLFSSL_SILABS_HOST_TEST + +#include +#include + +/* ---- Device identity (normally from em_device.h) ---- */ +#ifndef SEMAILBOX_PRESENT + #define SEMAILBOX_PRESENT 1 +#endif +#define _SILICON_LABS_32B_SERIES 2 +#define _SILICON_LABS_32B_SERIES_2 1 +#define _SILICON_LABS_32B_SERIES_2_CONFIG 5 +#define _SILICON_LABS_32B_SERIES_2_CONFIG_5 1 +#define _SILICON_LABS_SECURITY_FEATURE_SE 0 +#define _SILICON_LABS_SECURITY_FEATURE_VAULT 1 +#define _SILICON_LABS_SECURITY_FEATURE_ROT 2 +#define _SILICON_LABS_SECURITY_FEATURE_BASE 3 +/* Vault High by default. WOLFSSL_SILABS_HOST_TEST_VAULT_MID models a Vault + * Mid part, compile-testing that every Vault-only path (SHA-384/512, + * P-384/P-521, ChaCha20-Poly1305, SE KDFs, wrapped keys) drops out. */ +#ifdef WOLFSSL_SILABS_HOST_TEST_VAULT_MID + #define _SILICON_LABS_SECURITY_FEATURE _SILICON_LABS_SECURITY_FEATURE_SE +#else + #define _SILICON_LABS_SECURITY_FEATURE _SILICON_LABS_SECURITY_FEATURE_VAULT +#endif + +/* ---- Status codes (sl_status.h) ---- */ +typedef uint32_t sl_status_t; +#define SL_STATUS_OK ((sl_status_t)0x0000) +#define SL_STATUS_FAIL ((sl_status_t)0x0001) +#define SL_STATUS_INVALID_PARAMETER ((sl_status_t)0x0021) +#define SL_STATUS_NOT_SUPPORTED ((sl_status_t)0x000F) +#define SL_STATUS_INVALID_SIGNATURE ((sl_status_t)0x002C) + +/* ---- Command context (sl_se_manager_types.h) ---- */ +typedef struct { + uint32_t opaque[4]; +} sl_se_command_context_t; +#define SL_SE_COMMAND_CONTEXT_INIT { { 0, 0, 0, 0 } } + +/* ---- Key descriptor (sl_se_manager_types.h) ---- */ +typedef uint32_t sl_se_key_type_t; +typedef uint32_t sl_se_storage_method_t; + +#define SL_SE_KEY_STORAGE_EXTERNAL_PLAINTEXT ((sl_se_storage_method_t)0) +#define SL_SE_KEY_STORAGE_EXTERNAL_WRAPPED ((sl_se_storage_method_t)1) +#define SL_SE_KEY_STORAGE_INTERNAL_VOLATILE ((sl_se_storage_method_t)2) +#define SL_SE_KEY_STORAGE_INTERNAL_IMMUTABLE ((sl_se_storage_method_t)3) + +#define SL_SE_KEY_TYPE_SYMMETRIC 0x00000000 +#define SL_SE_KEY_TYPE_AES_128 0x00000010 +#define SL_SE_KEY_TYPE_AES_192 0x00000018 +#define SL_SE_KEY_TYPE_AES_256 0x00000020 +#define SL_SE_KEY_TYPE_ECC_P192 0x08000018 +#define SL_SE_KEY_TYPE_ECC_P224 0x0800001C +#define SL_SE_KEY_TYPE_ECC_P256 0x08000020 +#ifndef WOLFSSL_SILABS_HOST_TEST_VAULT_MID + #define SL_SE_KEY_TYPE_ECC_P384 0x08000030 + #define SL_SE_KEY_TYPE_ECC_P521 0x08000042 +#endif +#define SL_SE_KEY_TYPE_ECC_X25519 0x0b000020 +#ifndef WOLFSSL_SILABS_HOST_TEST_VAULT_MID + #define SL_SE_KEY_TYPE_ECC_X448 0x0b000038 +#endif +#define SL_SE_KEY_TYPE_ECC_ED25519 0x0c000020 +/* Secure Vault High only. */ +#ifndef WOLFSSL_SILABS_HOST_TEST_VAULT_MID + #define SL_SE_KEY_TYPE_CHACHA20 0x00000020 + #define SL_SE_KEY_TYPE_ECC_ED448 0x0c000038 +#endif + +#define SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PRIVATE_KEY (1UL << 12) +#define SL_SE_KEY_FLAG_ASYMMETRIC_BUFFER_HAS_PUBLIC_KEY (1UL << 13) +#define SL_SE_KEY_FLAG_ASYMMETRIC_SIGNING_ONLY (1UL << 14) +#define SL_SE_KEY_FLAG_NON_EXPORTABLE (1UL << 24) +#define SL_SE_KEY_FLAG_IS_DEVICE_GENERATED (1UL << 25) + +typedef struct { + sl_se_key_type_t type; + uint32_t flags; + uint32_t size; + struct { + sl_se_storage_method_t method; + union { + struct { + uint8_t* pointer; /* matches sl_se_buffer_t in the SDK */ + uint32_t size; + } buffer; + uint32_t slot; + } location; + } storage; +} sl_se_key_descriptor_t; + +/* ---- Key slots and wrapped-key sizing (sl_se_manager_defines.h) ---- */ +#define SL_SE_KEY_SLOT_VOLATILE_0 0x00 +#define SL_SE_KEY_SLOT_VOLATILE_1 0x01 +#define SL_SE_KEY_SLOT_VOLATILE_2 0x02 +#define SL_SE_KEY_SLOT_VOLATILE_3 0x03 +#define SL_SE_KEY_SLOT_TRUSTZONE_ROOT_KEY 0xF7 +#define SL_SE_KEY_SLOT_APPLICATION_SECURE_DEBUG_KEY 0xF8 +#define SL_SE_KEY_SLOT_APPLICATION_AES_128_KEY 0xFA +#define SL_SE_KEY_SLOT_APPLICATION_SECURE_BOOT_KEY 0xFC +#define SL_SE_KEY_SLOT_APPLICATION_ATTESTATION_KEY 0xFE +#define SL_SE_KEY_SLOT_SE_ATTESTATION_KEY 0xFF + +#define SLI_SE_WRAPPED_KEY_OVERHEAD (12 + 16) + +/* ---- Internal (built-in) keys (sl_se_manager_internal_keys.h) ---- */ +#define SL_SE_APPLICATION_ATTESTATION_KEY { 0, 0, 0, { 0, { { NULL, 0 } } } } + +/* ---- Cipher (sl_se_manager_cipher.h) ---- */ +typedef enum { + SL_SE_DECRYPT = 0, + SL_SE_ENCRYPT = 1 +} sl_se_cipher_operation_t; + +/* ---- Hash (sl_se_manager_hash.h) ---- */ +typedef enum { + SL_SE_HASH_NONE = 0, + SL_SE_HASH_SHA1, + SL_SE_HASH_SHA224, + SL_SE_HASH_SHA256, + SL_SE_HASH_SHA384, + SL_SE_HASH_SHA512 +} sl_se_hash_type_t; + +#define SLI_SE_SHIM_HASH_CTX(bits, blocksz) \ + struct { \ + uint32_t hash_type; \ + uint64_t total; \ + uint32_t state[(bits) / 32]; \ + uint8_t buffer[blocksz]; \ + } + +typedef SLI_SE_SHIM_HASH_CTX(160, 64) sl_se_sha1_multipart_context_t; +typedef SLI_SE_SHIM_HASH_CTX(256, 64) sl_se_sha224_multipart_context_t; +typedef SLI_SE_SHIM_HASH_CTX(256, 64) sl_se_sha256_multipart_context_t; +typedef SLI_SE_SHIM_HASH_CTX(512, 128) sl_se_sha384_multipart_context_t; +typedef SLI_SE_SHIM_HASH_CTX(512, 128) sl_se_sha512_multipart_context_t; + +/* Selects the SDK v4+ multipart hash interface in silabs_hash.h, matching the + * SE major version two used by Series 2 Config 5. The SDK defines these PRF + * identifiers as aliases of the hash enum. */ +#define SL_SE_PRF_HMAC_SHA1 SL_SE_HASH_SHA1 +#define SL_SE_PRF_HMAC_SHA224 SL_SE_HASH_SHA224 +#define SL_SE_PRF_HMAC_SHA256 SL_SE_HASH_SHA256 +#define SL_SE_PRF_HMAC_SHA384 SL_SE_HASH_SHA384 +#define SL_SE_PRF_HMAC_SHA512 SL_SE_HASH_SHA512 +typedef sl_se_hash_type_t sl_se_pbkdf2_prf_type_t; + +/* ---- CMAC / CTR (sl_se_manager_cipher.h, sl_se_manager_config.h) ---- */ +#define SL_SE_AES_BLOCK_SIZE (16u) +#ifndef SLI_SE_AES_CTR_NUM_BLOCKS_BUFFERED + #define SLI_SE_AES_CTR_NUM_BLOCKS_BUFFERED 1 +#endif + +typedef struct { + uint8_t state[16]; + uint8_t data_in[16]; + uint8_t data_out[16]; + size_t length; +} sl_se_cmac_multipart_context_t; + +/* ---- Function stubs ---- */ +static inline sl_status_t sl_se_init(void) + { return SL_STATUS_OK; } +static inline sl_status_t sl_se_deinit(void) + { return SL_STATUS_OK; } +static inline sl_status_t sl_se_init_command_context( + sl_se_command_context_t* c) + { (void)c; return SL_STATUS_OK; } + +static inline sl_status_t sl_se_get_random(sl_se_command_context_t* c, + void* out, uint32_t n) + { (void)c; (void)out; (void)n; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_aes_crypt_ecb(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, sl_se_cipher_operation_t op, + size_t len, const unsigned char* in, unsigned char* out) + { (void)c; (void)k; (void)op; (void)len; (void)in; (void)out; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_aes_crypt_cbc(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, sl_se_cipher_operation_t op, + size_t len, unsigned char* iv, const unsigned char* in, + unsigned char* out) + { (void)c; (void)k; (void)op; (void)len; (void)iv; (void)in; (void)out; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_gcm_crypt_and_tag( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + sl_se_cipher_operation_t op, size_t len, const unsigned char* iv, + size_t ivLen, const unsigned char* add, size_t addLen, + const unsigned char* in, unsigned char* out, size_t tagLen, + unsigned char* tag) + { (void)c; (void)k; (void)op; (void)len; (void)iv; (void)ivLen; + (void)add; (void)addLen; (void)in; (void)out; (void)tagLen; (void)tag; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_gcm_auth_decrypt( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + size_t len, const unsigned char* iv, size_t ivLen, + const unsigned char* add, size_t addLen, const unsigned char* in, + unsigned char* out, size_t tagLen, const unsigned char* tag) + { (void)c; (void)k; (void)len; (void)iv; (void)ivLen; (void)add; + (void)addLen; (void)in; (void)out; (void)tagLen; (void)tag; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_ccm_encrypt_and_tag( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + size_t len, const unsigned char* iv, size_t ivLen, + const unsigned char* add, size_t addLen, const unsigned char* in, + unsigned char* out, unsigned char* tag, size_t tagLen) + { (void)c; (void)k; (void)len; (void)iv; (void)ivLen; (void)add; + (void)addLen; (void)in; (void)out; (void)tag; (void)tagLen; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_ccm_auth_decrypt( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + size_t len, const unsigned char* iv, size_t ivLen, + const unsigned char* add, size_t addLen, const unsigned char* in, + unsigned char* out, const unsigned char* tag, size_t tagLen) + { (void)c; (void)k; (void)len; (void)iv; (void)ivLen; (void)add; + (void)addLen; (void)in; (void)out; (void)tag; (void)tagLen; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_hash_multipart_starts(void* hashCtx, + sl_se_command_context_t* c, sl_se_hash_type_t type) + { (void)hashCtx; (void)c; (void)type; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_hash_multipart_update(void* hashCtx, + sl_se_command_context_t* c, const uint8_t* in, size_t len) + { (void)hashCtx; (void)c; (void)in; (void)len; + return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_hash_multipart_finish(void* hashCtx, + sl_se_command_context_t* c, uint8_t* digest, size_t len) + { (void)hashCtx; (void)c; (void)digest; (void)len; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_validate_key( + const sl_se_key_descriptor_t* k) + { (void)k; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_get_storage_size( + const sl_se_key_descriptor_t* k, uint32_t* sz) + { (void)k; (void)sz; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_generate_key(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k) + { (void)c; (void)k; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_export_public_key( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + const sl_se_key_descriptor_t* pub) + { (void)c; (void)k; (void)pub; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_ecc_sign(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, sl_se_hash_type_t hashAlg, + int hashedMessage, const unsigned char* msg, size_t msgLen, + unsigned char* sig, size_t sigLen) + { (void)c; (void)k; (void)hashAlg; (void)hashedMessage; (void)msg; + (void)msgLen; (void)sig; (void)sigLen; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_ecc_verify(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, sl_se_hash_type_t hashAlg, + int hashedMessage, const unsigned char* msg, size_t msgLen, + const unsigned char* sig, size_t sigLen) + { (void)c; (void)k; (void)hashAlg; (void)hashedMessage; (void)msg; + (void)msgLen; (void)sig; (void)sigLen; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_ecdh_compute_shared_secret( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* priv, + const sl_se_key_descriptor_t* pub, const sl_se_key_descriptor_t* out) + { (void)c; (void)priv; (void)pub; (void)out; + return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_aes_crypt_ctr(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, size_t len, uint32_t* ncOff, + unsigned char* nonceCounter, unsigned char* streamBlock, + const unsigned char* in, unsigned char* out) + { (void)c; (void)k; (void)len; (void)ncOff; (void)nonceCounter; + (void)streamBlock; (void)in; (void)out; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_cmac(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, const unsigned char* in, + size_t inLen, unsigned char* out) + { (void)c; (void)k; (void)in; (void)inLen; (void)out; + return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_cmac_multipart_starts( + sl_se_cmac_multipart_context_t* m, sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k) + { (void)m; (void)c; (void)k; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_cmac_multipart_update( + sl_se_cmac_multipart_context_t* m, sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, const uint8_t* in, size_t inLen) + { (void)m; (void)c; (void)k; (void)in; (void)inLen; + return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_cmac_multipart_finish( + sl_se_cmac_multipart_context_t* m, sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, uint8_t* out) + { (void)m; (void)c; (void)k; (void)out; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_hmac(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* k, sl_se_hash_type_t hashType, + const uint8_t* msg, size_t msgLen, uint8_t* out, size_t outLen) + { (void)c; (void)k; (void)hashType; (void)msg; (void)msgLen; (void)out; + (void)outLen; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_hmac_multipart_starts( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + sl_se_hash_type_t hashType, const uint8_t* msg, size_t msgLen, + uint8_t* stateOut, size_t stateOutLen) + { (void)c; (void)k; (void)hashType; (void)msg; (void)msgLen; + (void)stateOut; (void)stateOutLen; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_hmac_multipart_update( + sl_se_command_context_t* c, sl_se_hash_type_t hashType, + const uint8_t* msg, size_t msgLen, uint8_t* state, size_t stateLen) + { (void)c; (void)hashType; (void)msg; (void)msgLen; (void)state; + (void)stateLen; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_hmac_multipart_finish( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + sl_se_hash_type_t hashType, const uint8_t* msg, size_t msgLen, + uint8_t* state, size_t stateLen, uint8_t* out, size_t outLen) + { (void)c; (void)k; (void)hashType; (void)msg; (void)msgLen; (void)state; + (void)stateLen; (void)out; (void)outLen; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_chacha20_poly1305_encrypt_and_tag( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + size_t len, const unsigned char* nonce, const unsigned char* add, + size_t addLen, const unsigned char* in, unsigned char* out, + unsigned char* tag) + { (void)c; (void)k; (void)len; (void)nonce; (void)add; (void)addLen; + (void)in; (void)out; (void)tag; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_chacha20_poly1305_auth_decrypt( + sl_se_command_context_t* c, const sl_se_key_descriptor_t* k, + size_t len, const unsigned char* nonce, const unsigned char* add, + size_t addLen, const unsigned char* in, unsigned char* out, + const unsigned char* tag) + { (void)c; (void)k; (void)len; (void)nonce; (void)add; (void)addLen; + (void)in; (void)out; (void)tag; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_derive_key_hkdf(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* inKey, sl_se_hash_type_t hashType, + const unsigned char* salt, size_t saltLen, const unsigned char* info, + size_t infoLen, sl_se_key_descriptor_t* outKey) + { (void)c; (void)inKey; (void)hashType; (void)salt; (void)saltLen; + (void)info; (void)infoLen; (void)outKey; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_derive_key_pbkdf2(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* inKey, sl_se_pbkdf2_prf_type_t prf, + const unsigned char* salt, size_t saltLen, uint32_t iterations, + sl_se_key_descriptor_t* outKey) + { (void)c; (void)inKey; (void)prf; (void)salt; (void)saltLen; + (void)iterations; (void)outKey; return SL_STATUS_NOT_SUPPORTED; } + +static inline sl_status_t sl_se_import_key(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* in, const sl_se_key_descriptor_t* out) + { (void)c; (void)in; (void)out; return SL_STATUS_NOT_SUPPORTED; } +static inline sl_status_t sl_se_export_key(sl_se_command_context_t* c, + const sl_se_key_descriptor_t* in, const sl_se_key_descriptor_t* out) + { (void)c; (void)in; (void)out; return SL_STATUS_NOT_SUPPORTED; } + +#endif /* WOLFSSL_SILABS_HOST_TEST */ + +#endif /* _WOLFPORT_SILABS_SHIM_H_ */ diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index a57a4c06b7..e02a3d46ba 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -455,6 +455,20 @@ #include #endif +/* SE Manager context members are embedded in the public Aes, ecc_key and + * wc_Sha* structs. Both SiLabs ports need them, so gate those members on this + * umbrella rather than on either port's own macro. */ +#if defined(WOLFSSL_SILABS_SE_ACCEL) && !defined(WOLFSSL_SILABS_SE_TYPES) + #define WOLFSSL_SILABS_SE_TYPES +#endif + +/* Silicon Labs crypto callback port: enable the callback and map WC_USE_DEVID + * before the rest of settings.h and before the test and benchmark read it. + * Macro only, no SDK dependencies. */ +#if defined(WOLFSSL_SILABS_CRYPTOCB) + #include +#endif + /* Forward propagation of the legacy parent gate to the canonical name * (HAVE_DILITHIUM -> WOLFSSL_HAVE_MLDSA). Always active: required so that * a user_settings.h or build flag using only the legacy spelling still diff --git a/wolfssl/wolfcrypt/sha.h b/wolfssl/wolfcrypt/sha.h index adda36320b..7116d33c73 100644 --- a/wolfssl/wolfcrypt/sha.h +++ b/wolfssl/wolfcrypt/sha.h @@ -82,7 +82,7 @@ #ifdef WOLFSSL_ESP32_CRYPT #include #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include #endif #if defined(WOLFSSL_MAX3266X) || defined(WOLFSSL_MAX3266X_OLD) @@ -161,7 +161,14 @@ struct wc_Sha { #else word32 digest[WC_SHA_DIGEST_SIZE / sizeof(word32)]; #endif -#endif +#endif /* end of the hardware-vs-software member chain */ + +/* The callback port keeps the software implementation compiled in for + * fallback, so the SE context is added alongside those members rather than + * replacing them - hence this block sits outside the chain closed above. */ +#if defined(WOLFSSL_SILABS_CRYPTOCB) + wc_silabs_sha_t silabsCtx; +#endif /* WOLFSSL_SILABS_CRYPTOCB */ void* heap; #ifdef WOLFSSL_PIC32MZ_HASH hashUpdCache cache; /* cache for updates */ diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index 3c35163a8a..48a90cd0de 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -93,7 +93,7 @@ #if defined(WOLFSSL_CRYPTOCELL) #include #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include #endif #if defined(WOLFSSL_KCAPI_HASH) @@ -199,7 +199,14 @@ struct wc_Sha256 { word32 loLen; /* length in bytes */ word32 hiLen; /* length in bytes */ -#endif +#endif /* end of the hardware-vs-software member chain */ + +/* The callback port keeps the software implementation compiled in for + * fallback, so the SE context is added alongside those members rather than + * replacing them - hence this block sits outside the chain closed above. */ +#if defined(WOLFSSL_SILABS_CRYPTOCB) + wc_silabs_sha_t silabsCtx; +#endif /* WOLFSSL_SILABS_CRYPTOCB */ void* heap; #ifdef WOLFSSL_PIC32MZ_HASH hashUpdCache cache; /* cache for updates */ diff --git a/wolfssl/wolfcrypt/sha512.h b/wolfssl/wolfcrypt/sha512.h index 26cd75a366..0f75da9287 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -59,7 +59,7 @@ #ifdef WOLFSSL_ESP32_CRYPT #include #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) #include #endif #if defined(WOLFSSL_PSOC6_CRYPTO) @@ -176,7 +176,7 @@ struct wc_Sha512 { !defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA384)) WC_ESP32SHA ctx; #endif -#if defined(WOLFSSL_SILABS_SE_ACCEL) +#if defined(WOLFSSL_SILABS_SE_TYPES) wc_silabs_sha_t silabsCtx; #endif #ifdef WOLFSSL_KCAPI_HASH diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 726f715877..60a84baa5e 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -1568,8 +1568,9 @@ enum wc_KdfType { WC_KDF_TYPE_HKDF = 1, WC_KDF_TYPE_TWOSTEP_CMAC = 2, /* NIST SP 800-56C two-step cmac kdf. */ WC_KDF_TYPE_HKDF_EXTRACT = 3, - WC_KDF_TYPE_HKDF_EXPAND = 4 - /* Future: WC_KDF_TYPE_PBKDF2 = 5, WC_KDF_TYPE_SCRYPT = 6, etc. */ + WC_KDF_TYPE_HKDF_EXPAND = 4, + WC_KDF_TYPE_PBKDF2 = 5 + /* Future: WC_KDF_TYPE_SCRYPT = 6, etc. */ }; /* hash types */