From 14957ed03ff74ba4ef1f6bb6735bac195cd76c7a Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 14 Aug 2026 10:09:41 -0700 Subject: [PATCH 1/3] Add a crypto callback hook for finite field Diffie-Hellman key agreement --- wolfcrypt/src/cryptocb.c | 33 ++++++++++++++++++++++++++++ wolfcrypt/src/dh.c | 42 ++++++++++++++++++++++++++++++++++-- wolfssl/wolfcrypt/cryptocb.h | 22 +++++++++++++++++++ wolfssl/wolfcrypt/dh.h | 1 + 4 files changed, 96 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index a214f012db5..15fc708f3f9 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -753,6 +753,39 @@ int wc_CryptoCb_RsaGetSize(const RsaKey* key, int* keySize) } #endif /* !NO_RSA */ +#ifndef NO_DH +int wc_CryptoCb_Dh(DhKey* key, const byte* priv, word32 privSz, + const byte* otherPub, word32 pubSz, byte* agree, word32* agreeSz) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + if (key == NULL) + return ret; + + /* locate registered callback */ + dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK); + if (dev && dev->cb) { + wc_CryptoInfo cryptoInfo; + XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo)); + cryptoInfo.algo_type = WC_ALGO_TYPE_PK; + cryptoInfo.pk.type = WC_PK_TYPE_DH; + cryptoInfo.pk.dh.key = key; + cryptoInfo.pk.dh.priv = priv; + cryptoInfo.pk.dh.privSz = privSz; + cryptoInfo.pk.dh.otherPub = otherPub; + cryptoInfo.pk.dh.pubSz = pubSz; + cryptoInfo.pk.dh.agree = agree; + cryptoInfo.pk.dh.agreeSz = agreeSz; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} +#endif /* !NO_DH */ + + #ifdef HAVE_ECC #ifdef HAVE_ECC_DHE int wc_CryptoCb_MakeEccKey(WC_RNG* rng, int keySize, ecc_key* key, int curveId) diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index 7aa02588153..388daac9fa9 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -36,6 +36,9 @@ #endif #include +#ifdef WOLF_CRYPTO_CB + #include +#endif #ifdef WOLFSSL_HAVE_SP_DH #include @@ -973,6 +976,7 @@ int wc_InitDhKey_ex(DhKey* key, void* heap, int devId) key->heap = heap; /* for XMALLOC/XFREE in future */ key->trustedGroup = 0; + key->devId = devId; #ifdef WC_DH_INITIAL_RUNTIME_ENABLEMENT if (! wc_dh_enabled) @@ -990,8 +994,6 @@ int wc_InitDhKey_ex(DhKey* key, void* heap, int devId) /* handle as async */ ret = wolfAsync_DevCtxInit(&key->asyncDev, WOLFSSL_ASYNC_MARKER_DH, key->heap, devId); -#else - (void)devId; #endif #ifdef WOLFSSL_KCAPI_DH @@ -2137,6 +2139,41 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, #endif } +#ifdef WOLF_CRYPTO_CB + /* Dispatched here, after the checks above, so a device gets validated + * inputs and the SP 800-56A guarantees do not depend on each driver + * reimplementing them. Placing it here rather than at the top of + * wc_DhAgree() also means the validation runs once, not twice, when a + * callback declines and software takes over. */ + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_Dh(key, priv, privSz, otherPub, pubSz, agree, + agreeSz); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + /* The shared secret must not be 1 (SP 800-56A 5.7.1.1). The + * software path below checks the mp_int; the device has already + * produced bytes, so check those. */ + if (ret == 0) { + word32 i; + byte acc = 0; + + for (i = 0; (i + 1) < *agreeSz; i++) { + acc |= agree[i]; + } + if ((acc == 0) && (*agreeSz > 0) && + (agree[*agreeSz - 1] == 1)) { + WOLFSSL_MSG("wc_DhAgree shared secret is one"); + return MP_VAL; + } + } + return ret; + } + ret = 0; /* fall through to software */ + } +#endif + #if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_HAVE_SP_DH) && \ defined(WOLFSSL_SP_NONBLOCK) && defined(WOLFSSL_SP_SMALL) && \ !defined(WOLFSSL_SP_FAST_MODEXP) @@ -2437,6 +2474,7 @@ int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz, const byte* priv, return FIPS_NOT_ALLOWED_E; #endif + #ifdef WOLFSSL_KCAPI_DH (void)priv; (void)privSz; diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index 0be294381b3..fa3242241dc 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -35,6 +35,9 @@ #ifdef WOLF_CRYPTO_CB +#ifndef NO_DH + #include +#endif #ifndef NO_RSA #include #endif @@ -220,6 +223,20 @@ typedef struct wc_CryptoInfo { } rsa_pss_verify; #endif #endif + #ifndef NO_DH + /* Finite field Diffie-Hellman shared secret. Key generation is + * deliberately not routed: the private exponent should come from + * the caller's WC_RNG, not from a device. */ + struct { + DhKey* key; + const byte* priv; + word32 privSz; + const byte* otherPub; + word32 pubSz; + byte* agree; + word32* agreeSz; + } dh; + #endif #ifdef HAVE_ECC #ifdef HAVE_ECC_DHE struct { @@ -883,6 +900,11 @@ WOLFSSL_LOCAL int wc_CryptoCb_RsaCheckPrivKey(RsaKey* key, const byte* pubKey, WOLFSSL_LOCAL int wc_CryptoCb_RsaGetSize(const RsaKey* key, int* keySize); #endif /* !NO_RSA */ +#ifndef NO_DH +WOLFSSL_LOCAL int wc_CryptoCb_Dh(DhKey* key, const byte* priv, word32 privSz, + const byte* otherPub, word32 pubSz, byte* agree, word32* agreeSz); +#endif + #ifdef HAVE_ECC WOLFSSL_LOCAL int wc_CryptoCb_MakeEccKey(WC_RNG* rng, int keySize, ecc_key* key, int curveId); diff --git a/wolfssl/wolfcrypt/dh.h b/wolfssl/wolfcrypt/dh.h index 85d3e95f4a2..cbf170210c8 100644 --- a/wolfssl/wolfcrypt/dh.h +++ b/wolfssl/wolfcrypt/dh.h @@ -112,6 +112,7 @@ struct DhKey { #ifdef WC_DH_NONBLOCK DhNb* nb; /* non-blocking context, NULL when not in non-block mode */ #endif + int devId; }; #ifndef WC_DH_TYPE_DEFINED From f323f360fd98b5d6a746b53a4068037881c2a00f Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 14 Aug 2026 10:09:41 -0700 Subject: [PATCH 2/3] Add WOLFSSL_NO_DH_GEN_PARAMS to build DH without domain parameter generation --- CMakeLists.txt | 7 +++ cmake/options.h.in | 3 ++ configure.ac | 60 +++++++++++++++++++++++- src/pk.c | 5 ++ tests/api/test_dh.c | 1 + tests/unit-mcdc/test_dh_fault_whitebox.c | 7 +++ wolfcrypt/src/dh.c | 2 +- wolfcrypt/test/test.c | 7 +-- wolfssl/wolfcrypt/dh.h | 6 ++- 9 files changed, 91 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 69375d09fba..91bf220deef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1612,6 +1612,10 @@ add_option("WOLFSSL_KEYGEN" "Enable key generation (default: disabled)" "no" "yes;no") +add_option("WOLFSSL_DH_GEN_PARAMS" + "Enable DH domain parameter generation with key generation; protocols use the fixed FFDHE groups (default: enabled)" + "yes" "yes;no") + add_option("WOLFSSL_CERTGEN" "Enable cert generation (default: disabled)" "no" "yes;no") @@ -3046,6 +3050,9 @@ endif() if(WOLFSSL_KEYGEN) list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_KEY_GEN") + if(NOT WOLFSSL_DH_GEN_PARAMS) + list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_NO_DH_GEN_PARAMS") + endif() endif() if(WOLFSSL_CERTGEN) list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_CERT_GEN") diff --git a/cmake/options.h.in b/cmake/options.h.in index ac9bc57f4de..423bdaca8e1 100644 --- a/cmake/options.h.in +++ b/cmake/options.h.in @@ -341,6 +341,9 @@ extern "C" { #cmakedefine WOLFSSL_IP_ALT_NAME #undef WOLFSSL_KEY_GEN #cmakedefine WOLFSSL_KEY_GEN + +#undef WOLFSSL_NO_DH_GEN_PARAMS +#cmakedefine WOLFSSL_NO_DH_GEN_PARAMS #undef WOLFSSL_NO_ASM #cmakedefine WOLFSSL_NO_ASM #undef WOLFSSL_NO_SHAKE128 diff --git a/configure.ac b/configure.ac index 971249f17b1..d62f486fe23 100644 --- a/configure.ac +++ b/configure.ac @@ -4688,6 +4688,35 @@ then done fi +# NXP QorIQ SEC (the PowerPC T-series security engine, a CAAM derivative). +# Takes a comma separated list selecting the environment backend, e.g. +# --enable-sec-qoriq=baremetal +AC_ARG_ENABLE([sec-qoriq], + [AS_HELP_STRING([--enable-sec-qoriq],[Enable wolfSSL support for the NXP QorIQ SEC engine, T1040/T2080 (default: disabled)])], + [ ENABLED_SEC_QORIQ=$enableval ], + [ ENABLED_SEC_QORIQ=no ] + ) + +if test "$ENABLED_SEC_QORIQ" != "no" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SEC_QORIQ" + + for v in `echo $ENABLED_SEC_QORIQ | tr "," " "` + do + case $v in + yes | baremetal) + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SEC_QORIQ_BAREMETAL" + ;; + linux) + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SEC_QORIQ_LINUX" + ;; + *) + AC_MSG_ERROR([Invalid choice for --enable-sec-qoriq: $v (want baremetal or linux)]) + ;; + esac + done +fi + AC_ARG_ENABLE([caam], [AS_HELP_STRING([--enable-caam],[Enable wolfSSL support for CAAM (default: disabled)])], [ ENABLED_CAAM=$enableval ], @@ -5456,11 +5485,36 @@ fi # KEY GENERATION AC_ARG_ENABLE([keygen], - [AS_HELP_STRING([--enable-keygen],[Enable key generation (only applies to RSA key generation) (default: disabled)])], + [AS_HELP_STRING([--enable-keygen],[Enable key generation. Takes yes, no, or a comma separated list of: all, no-dh-params (drop DH domain parameter generation, keeping DH key generation; protocols use the fixed FFDHE groups) (default: disabled)])], [ ENABLED_KEYGEN=$enableval ], [ ENABLED_KEYGEN=no ] ) +# Expand the list form of --enable-keygen into the individual sub-features. +ENABLED_DH_GEN_PARAMS=yes +if test "$ENABLED_KEYGEN" != "no" && test "$ENABLED_KEYGEN" != "yes" +then + for kg in `echo $ENABLED_KEYGEN | tr ',' ' '` + do + case $kg in + all) + ;; + no-dh-params | nodhparams | nodhparamgen) + ENABLED_DH_GEN_PARAMS=no + ;; + *) + AC_MSG_ERROR([Invalid choice for --enable-keygen: $kg. Use yes, no, all or no-dh-params.]) + ;; + esac + done + ENABLED_KEYGEN=yes +fi + +if test "$ENABLED_DH_GEN_PARAMS" = "no" +then + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_DH_GEN_PARAMS" +fi + if test "$ENABLED_BIND" = "yes" || test "$ENABLED_NTP" = "yes" || \ test "$ENABLED_LIBSSH2" = "yes" || test "$ENABLED_OPENRESTY" = "yes" || \ test "$ENABLED_NGINX" = "yes" || test "$ENABLED_WOLFENGINE" = "yes" || \ @@ -11550,7 +11604,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 "$ENABLED_SEC_QORIQ" != "no" || test "x$ENABLED_RTL8735B" != "xno" || test "x$ENABLED_VAULTIC" = "xyes" then ENABLED_CRYPTOCB=yes fi @@ -13381,6 +13435,7 @@ AM_CONDITIONAL([BUILD_BENCHMARK],[test "$ENABLED_BENCHMARK" = "yes"]) AM_CONDITIONAL([BUILD_RC2],[test "x$ENABLED_RC2" = "xyes"]) AM_CONDITIONAL([BUILD_CUDA],[test "x$ENABLED_CUDA" = "xyes"]) AM_CONDITIONAL([BUILD_CAAM],[test "x$ENABLED_CAAM" != "xno"]) +AM_CONDITIONAL([BUILD_SEC_QORIQ],[test "x$ENABLED_SEC_QORIQ" != "xno"]) AM_CONDITIONAL([BUILD_QNXCAAM],[test "x$ENABLED_CAAM_QNX" = "xyes"]) AM_CONDITIONAL([BUILD_IOTSAFE],[test "x$ENABLED_IOTSAFE" = "xyes"]) AM_CONDITIONAL([BUILD_IOTSAFE_HWRNG],[test "x$ENABLED_IOTSAFE_HWRNG" = "xyes"]) @@ -13803,6 +13858,7 @@ echo " * BLAKE2S: $ENABLED_BLAKE2S" echo " * SipHash: $ENABLED_SIPHASH" echo " * CMAC: $ENABLED_CMAC" echo " * keygen: $ENABLED_KEYGEN" +echo " * DH parameter generation: $ENABLED_DH_GEN_PARAMS" echo " * acert: $ENABLED_ACERT" echo " * certgen: $ENABLED_CERTGEN" echo " * certreq: $ENABLED_CERTREQ" diff --git a/src/pk.c b/src/pk.c index 7526b4924d9..f4129f4b276 100644 --- a/src/pk.c +++ b/src/pk.c @@ -4695,11 +4695,16 @@ int wolfSSL_DH_generate_parameters_ex(WOLFSSL_DH* dh, int prime_len, } } if (ret == 1) { + #ifndef WOLFSSL_NO_DH_GEN_PARAMS /* Generate parameters into internal DH key. */ if (wc_DhGenerateParams(rng, prime_len, key) != 0) { WOLFSSL_ERROR_MSG("wc_DhGenerateParams error"); ret = 0; } + #else + WOLFSSL_ERROR_MSG("DH parameter generation disabled in this build"); + ret = 0; + #endif } /* Free local random number generator if created. */ diff --git a/tests/api/test_dh.c b/tests/api/test_dh.c index a6a430132f5..943eed3096c 100644 --- a/tests/api/test_dh.c +++ b/tests/api/test_dh.c @@ -1006,6 +1006,7 @@ int test_wc_DhGenerateParams_and_ExportRaw(void) * generate-and-export flow below is only valid with the full SP math * (WOLFSSL_SP_MATH_ALL), fastmath or heapmath backends. */ #if !defined(NO_DH) && defined(WOLFSSL_KEY_GEN) && !defined(WOLFSSL_SP_MATH) && \ + !defined(WOLFSSL_NO_DH_GEN_PARAMS) && \ !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) DhKey dh; WC_RNG rng; diff --git a/tests/unit-mcdc/test_dh_fault_whitebox.c b/tests/unit-mcdc/test_dh_fault_whitebox.c index 9a02db1dc9d..87f9139bb5f 100644 --- a/tests/unit-mcdc/test_dh_fault_whitebox.c +++ b/tests/unit-mcdc/test_dh_fault_whitebox.c @@ -724,6 +724,7 @@ static void test_agree_nonblock(void) * subgroup) that cannot be produced deterministically without a working * fault-injection hook into the prime.c/sp_int.c backends this campaign's * allocator hook does not reach - left as residuals (see report). */ +#ifndef WOLFSSL_NO_DH_GEN_PARAMS static void test_generate_params(void) { WC_RNG rng; @@ -745,6 +746,7 @@ static void test_generate_params(void) wc_FreeRng(&rng); } +#endif /* !WOLFSSL_NO_DH_GEN_PARAMS */ int main(void) { @@ -774,7 +776,12 @@ int main(void) WB_NOTE("WC_DH_NONBLOCK not built; nb cache decisions " "(2070/2085/2098/2116) skipped"); #endif +#ifndef WOLFSSL_NO_DH_GEN_PARAMS test_generate_params(); +#else + WB_NOTE("WOLFSSL_NO_DH_GEN_PARAMS built; parameter generation decisions " + "(3293/3299) skipped"); +#endif printf("done (%s)\n", wb_fail ? "FAILURES" : "ok"); return 0; diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index 388daac9fa9..e59f084f017 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -3219,7 +3219,7 @@ int wc_DhCopyNamedKey(int name, } -#ifdef WOLFSSL_KEY_GEN +#if defined(WOLFSSL_KEY_GEN) && !defined(WOLFSSL_NO_DH_GEN_PARAMS) /* modulus_size in bits */ int wc_DhGenerateParams(WC_RNG *rng, int modSz, DhKey *dh) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index eed540a0f4a..f999d4c2b52 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -32502,7 +32502,7 @@ static wc_test_ret_t dh_fips_generate_test(WC_RNG *rng) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_gen_test); } -#ifdef WOLFSSL_KEY_GEN +#if defined(WOLFSSL_KEY_GEN) && !defined(WOLFSSL_NO_DH_GEN_PARAMS) wc_FreeDhKey(key); ret = wc_InitDhKey_ex(key, HEAP_HINT, devId); if (ret != 0) @@ -32521,7 +32521,7 @@ static wc_test_ret_t dh_fips_generate_test(WC_RNG *rng) #endif if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), exit_gen_test); -#endif /* WOLFSSL_KEY_GEN */ +#endif /* WOLFSSL_KEY_GEN && !WOLFSSL_NO_DH_GEN_PARAMS */ #endif /* HAVE_SELFTEST */ ret = 0; @@ -32600,7 +32600,8 @@ static wc_test_ret_t dh_generate_test(WC_RNG *rng) ret = 0; #endif -#if !defined(HAVE_FIPS) && defined(WOLFSSL_NO_DH186) +#if !defined(HAVE_FIPS) && defined(WOLFSSL_NO_DH186) && \ + !defined(WOLFSSL_NO_DH_GEN_PARAMS) { byte priv[260]; byte pub[260]; diff --git a/wolfssl/wolfcrypt/dh.h b/wolfssl/wolfcrypt/dh.h index cbf170210c8..b336b9a3210 100644 --- a/wolfssl/wolfcrypt/dh.h +++ b/wolfssl/wolfcrypt/dh.h @@ -250,7 +250,11 @@ WOLFSSL_API int wc_DhCheckPrivKey_ex(DhKey* key, const byte* priv, word32 privSz, const byte* prime, word32 primeSz); WOLFSSL_API int wc_DhCheckKeyPair(DhKey* key, const byte* pub, word32 pubSz, const byte* priv, word32 privSz); -#ifdef WOLFSSL_KEY_GEN +/* Domain parameter generation is a separate, expensive facility from key + * generation: it searches for a safe prime. Protocols in practice use the + * fixed groups (FFDHE, RFC 3526), so a build can keep DH key generation and + * drop parameter generation with WOLFSSL_NO_DH_GEN_PARAMS. */ +#if defined(WOLFSSL_KEY_GEN) && !defined(WOLFSSL_NO_DH_GEN_PARAMS) WOLFSSL_API int wc_DhGenerateParams(WC_RNG *rng, int modSz, DhKey *dh); #endif WOLFSSL_API int wc_DhExportParamsRaw(DhKey* dh, byte* p, word32* pSz, From 1e80aaca3507202f0e939bf2d0c52a86dbe0d358 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 14 Aug 2026 10:09:41 -0700 Subject: [PATCH 3/3] Add NXP QorIQ SEC (CAAM) hardware crypto port for PowerPC T-series --- wolfcrypt/src/asn.c | 17 +- wolfcrypt/src/include.am | 19 + wolfcrypt/src/port/nxp/README.md | 371 +++++++- wolfcrypt/src/port/nxp/sec_qoriq.c | 577 +++++++++++++ wolfcrypt/src/port/nxp/sec_qoriq_aes.c | 440 ++++++++++ wolfcrypt/src/port/nxp/sec_qoriq_baremetal.c | 185 ++++ wolfcrypt/src/port/nxp/sec_qoriq_cb.c | 865 +++++++++++++++++++ wolfcrypt/src/port/nxp/sec_qoriq_hash.c | 188 ++++ wolfcrypt/src/port/nxp/sec_qoriq_linux.c | 291 +++++++ wolfcrypt/src/port/nxp/sec_qoriq_pkha.c | 632 ++++++++++++++ wolfcrypt/src/port/nxp/sec_qoriq_rng.c | 211 +++++ wolfcrypt/src/wc_port.c | 19 + wolfcrypt/test/test.c | 39 + wolfssl/wolfcrypt/include.am | 6 + wolfssl/wolfcrypt/port/nxp/sec_qoriq.h | 733 ++++++++++++++++ wolfssl/wolfcrypt/settings.h | 33 + wolfssl/wolfcrypt/wc_port.h | 2 +- 17 files changed, 4619 insertions(+), 9 deletions(-) create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq.c create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq_aes.c create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq_baremetal.c create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq_cb.c create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq_hash.c create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq_linux.c create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq_pkha.c create mode 100644 wolfcrypt/src/port/nxp/sec_qoriq_rng.c create mode 100644 wolfssl/wolfcrypt/port/nxp/sec_qoriq.h diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 934a98cb6d6..b04ddcdb979 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -33005,13 +33005,18 @@ int DecodeECC_DSA_Sig_Ex(const byte* sig, word32 sigLen, mp_int* r, mp_int* s, /* Clear dynamic data and set mp_ints to put r and s into. */ XMEMSET(dataASN, 0, sizeof(dataASN)); if (init) { - GetASN_MP(&dataASN[DSASIGASN_IDX_R], r); - GetASN_MP(&dataASN[DSASIGASN_IDX_S], s); - } - else { - GetASN_MP_Inited(&dataASN[DSASIGASN_IDX_R], r); - GetASN_MP_Inited(&dataASN[DSASIGASN_IDX_S], s); + /* Initialize here rather than leaving it to the item store, which + * only does so once an item has parsed. A decode that fails before + * that, or that stores r and then fails on s, would otherwise reach + * the mp_clear() calls below with values that were never + * initialized. */ + ret = mp_init_multi(r, s, NULL, NULL, NULL, NULL); + if (ret != MP_OKAY) { + return ret; + } } + GetASN_MP_Inited(&dataASN[DSASIGASN_IDX_R], r); + GetASN_MP_Inited(&dataASN[DSASIGASN_IDX_S], s); /* Decode the DSA signature. */ ret = GetASN_Items(dsaSigASN, dataASN, dsaSigASN_Length, 0, sig, &idx, diff --git a/wolfcrypt/src/include.am b/wolfcrypt/src/include.am index f0e7ca3c717..8f794c5f363 100644 --- a/wolfcrypt/src/include.am +++ b/wolfcrypt/src/include.am @@ -88,6 +88,14 @@ EXTRA_DIST += wolfcrypt/src/port/ti/ti-aes.c \ wolfcrypt/src/port/nxp/dcp_port.c \ wolfcrypt/src/port/nxp/se050_port.c \ wolfcrypt/src/port/nxp/README.md \ + wolfcrypt/src/port/nxp/sec_qoriq.c \ + wolfcrypt/src/port/nxp/sec_qoriq_cb.c \ + wolfcrypt/src/port/nxp/sec_qoriq_hash.c \ + wolfcrypt/src/port/nxp/sec_qoriq_aes.c \ + wolfcrypt/src/port/nxp/sec_qoriq_rng.c \ + wolfcrypt/src/port/nxp/sec_qoriq_pkha.c \ + wolfcrypt/src/port/nxp/sec_qoriq_baremetal.c \ + wolfcrypt/src/port/nxp/sec_qoriq_linux.c \ wolfcrypt/src/port/nxp/casper_port.c \ wolfcrypt/src/port/nxp/hashcrypt_port.c \ wolfcrypt/src/port/atmel/README.md \ @@ -243,6 +251,17 @@ endif EXTRA_DIST += wolfcrypt/src/port/sealsq/README.md +if BUILD_SEC_QORIQ +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq_cb.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq_hash.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq_aes.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq_rng.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq_pkha.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq_baremetal.c +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/nxp/sec_qoriq_linux.c +endif + if BUILD_CAAM src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/caam/wolfcaam_init.c src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/caam/wolfcaam_qnx.c diff --git a/wolfcrypt/src/port/nxp/README.md b/wolfcrypt/src/port/nxp/README.md index 55f7351a643..4b55d25f6d2 100644 --- a/wolfcrypt/src/port/nxp/README.md +++ b/wolfcrypt/src/port/nxp/README.md @@ -1,6 +1,7 @@ # wolfSSL NXP Hardware Acceleration Ports -wolfSSL supports hardware acceleration on NXP DCP, LTC (KSDK), LPC55S69, and SE050. +wolfSSL supports hardware acceleration on NXP DCP, LTC (KSDK), LPC55S69, +SE050, and the QorIQ SEC. ## NXP LPC55S69 @@ -48,7 +49,373 @@ NOTE: Both can be defined with no problem. For details on wolfSSL integration with NXP SE050, see [README_SE050.md](./README_SE050.md). +## NXP QorIQ SEC + +The SEC is the security engine on NXP's QorIQ PowerPC T-series parts. It +shares the CAAM descriptor architecture used by the i.MX parts, but this is a +separate, self-contained port under `wolfcrypt/src/port/nxp/`; it does not +build any of `wolfcrypt/src/port/caam/`. + +### Supported hardware + +Verified on real silicon: + +| Part | Board | SEC | Era | CCSRBAR | +|---|---|---|---|---| +| T2080E rev 1.1 | Curtiss-Wright VPX3-152 | 5.2 | 6 | `0xEF000000` | +| T1040E rev 1.1 | NXP T1040D4RDB | 5.0 | 6 | `0xFE000000` | + +Both place the SEC at `CCSRBAR + 0x300000` with four job rings at `+0x1000`, +`+0x2000`, `+0x3000` and `+0x4000`, and both declare `fsl,sec-v4.0` +compatibility, so one driver covers them. They differ only in instance counts +and PKHA version, which affect throughput rather than the programming model: + +| | T2080 | T1040 | +|---|---|---| +| Job rings / DECOs | 4 / 4 | 4 / 2 | +| AESA / MDHA units | 4 / 4 | 2 / 2 | +| RNG / PKHA units | 1 / 1 | 1 / 1 | +| PKHA version | 2 | 1 | + +Note the device trees for both parts declare `fsl,sec-era = <5>`, but the +`CCBVID` register reports era 6 on both. The driver trusts the register. + +#### The "E" suffix matters + +QorIQ parts ship in security-enabled and security-disabled orderable +variants. A part without the SEC has no engine to talk to. `wc_SecQoriqInit()` +checks this at run time from the SVR (bit `0x80000`) and returns +`NOT_COMPILED_IN` on a non-E part rather than touching the block. U-Boot +prints the same thing as a trailing `E` on the CPU name, for example +`CPU0: T2080E`. + +### What is accelerated + +| Algorithm | Status | +|---|---| +| AES-CBC, AES-CTR, AES-ECB (128/192/256) | Supported | +| AES-GCM, including AAD | Supported with a 12 byte IV and a full 16 byte tag; engine checks the tag on decrypt | +| ECDSA sign and verify | Supported on any prime curve wolfCrypt carries, up to 1023 bits | +| ECDH shared secret | Supported on the same curves | +| RSA public and private operations | Supported up to 4096 bit, private key form 1 (d and n) | +| Finite field DH key agreement | Supported on the fixed FFDHE groups, via the same modular exponentiation | +| SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, MD5 | Driver API only, see below | +| RNG4 | Supported, seeds the wolfCrypt DRBG | +| DES/3DES, HMAC, CMAC, XTS, CCM | Not implemented yet | + +Everything not implemented returns `CRYPTOCB_UNAVAILABLE`, so wolfCrypt falls +back to software. An unimplemented algorithm is a performance question, never +a correctness one. + +#### Public key passes the curve parameters explicitly + +The i.MX CAAM parts let a descriptor name one of a handful of built-in curves +with a small index (ECDSEL) instead of carrying the domain parameters, and +wolfSSL's i.MX port uses that. **It does not work on the QorIQ parts tested +here.** Every flag position in bits 25:17 of the protocol block, combined with +every curve index 0 to 31, is refused with DECO error `0x82`. So is the +"message representative is already hashed" flag in the operation's PROTINFO, +which comes back as DECO error `0x81`. + +This port therefore supplies the prime, order, base point and curve +coefficients in the descriptor. That costs four extra words and a small +conversion from wolfCrypt's parameter tables, and in exchange it works for +**any prime curve wolfCrypt carries** rather than the five the engine has +built in, Brainpool and the SECP-K1 curves included. The only ceiling is +the PKHA's 1023 bit width. + +Two cases fall back to software because the engine cannot express them: + +- A key with no public point loaded. wolfCrypt derives one during a software + verify; the engine has no equivalent, and handing it zeros would produce a + spurious "bad signature". +- A message representative that reduces to zero. ECDSA is degenerate there + and the engine refuses it. + +Signing draws its per-signature nonce from RNG4 inside the engine, so +`wc_SecQoriqEccSign()` instantiates the RNG if it is not already running. +A descriptor submitted without it fails with CCB error `0x54`, "RNG not +instantiated". + +#### RSA and Diffie-Hellman + +RSA reaches the callback at `wc_RsaFunction`, which is the raw exponentiation +with padding already applied or not yet stripped, so it maps straight onto the +engine's two RSA protocols. Private key operations use form 1, which takes `d` +and `n` directly; the CRT forms want the prime factors and buy speed this port +does not need to chase yet. + +Offloading a private key operation bypasses wolfCrypt's own base blinding. +That blinding exists to protect the *software* modular exponentiation from +timing analysis, and the engine is not the code it defends, but a build that +would rather keep it can leave RSA in software with +`WOLFSSL_SEC_QORIQ_NO_RSA`. + +Finite field Diffie-Hellman is the same modular exponentiation, so +`wc_SecQoriqEcdh()`'s finite field counterpart is just `wc_SecQoriqModExp()` +with the group prime as the modulus. Reaching it needed a new callback hook +in wolfCrypt: `WC_PK_TYPE_DH` was in the enum but had no entry in +`wc_CryptoInfo`, no `wc_CryptoCb_Dh()` and no call site in `dh.c`, so no port +could offload DH at all. That hook is part of this work. + +Only key agreement is routed, and only over the fixed FFDHE groups. That is +what TLS uses, and it is the whole intended scope: **DH parameter generation +is not supported and will not be**, on the engine or through the callback. +Key generation also stays in software, so the private exponent comes from the +caller's `WC_RNG` rather than from a device. + +#### Hashing is not routed through the crypto callback + +The driver implements single-shot hashing (`wc_SecQoriqSha256()` and friends) +but does not answer `WC_ALGO_TYPE_HASH`. wolfCrypt calls the device once per +`Update` and again for `Final`, which needs either the streaming descriptor +(class 2 context saved and restored around each call) or `WOLFSSL_HASH_KEEP` +so wolfCrypt accumulates the message and asks once. The hardware supports +streaming; the driver does not implement it yet. Until then hashing through +the normal wolfCrypt API stays in software. + +#### RNG feeds the DRBG, it does not replace it + +The router answers `WC_ALGO_TYPE_SEED` only. `WC_ALGO_TYPE_RNG` is +deliberately left unhandled so `wc_RNG_GenerateBlock()` keeps running +wolfCrypt's own DRBG, seeded from the SEC through `wc_GenerateSeed()`. +Answering RNG directly would hand callers raw engine output and take +wolfCrypt's DRBG, its reseeding policy and its health checks out of the path. + +RNG4 powers up with no DRBG state handle instantiated, and neither U-Boot nor +wolfBoot does it on the boards tested (`RDSTA` reads 0). `wc_SecQoriqRngInit()` +performs the instantiation, retrying with a wider entropy sample if the +statistical checks reject the first attempt. + +### Building + + ./configure --host=powerpc-linux-gnu CC=powerpc-linux-gnu-gcc \ + --enable-sec-qoriq=baremetal --enable-aesgcm + +`--enable-sec-qoriq` selects the environment backend: + +- `baremetal` (default) is a flat, identity mapped address space, which is how + U-Boot and wolfBoot leave the e5500/e6500. DMA memory comes from a static + pool. This is the tested path. +- `linux` maps the SEC through `/dev/mem` and resolves DMA addresses with + `/proc/self/pagemap`. It initialises correctly on a stock kernel, but it + **does not offload anything yet**; see "The Linux backend on a 36-bit part" + below before using it. + +##### The Linux backend on a 36-bit part + +Exercised on a T1040D4RDB running the board's stock Linux 3.12. Three things +have to be right before the engine is even reachable, and one of them is +currently a wall: + +1. **Use the operating system's view of CCSR.** These parts have a 36-bit + physical address space, so under Linux CCSR is at `0xF_FE000000`, not the + `0xFE000000` that bare metal sees. Set `SEC_QORIQ_CCSRBAR_PHYS` (a 64-bit + value, separate from `SEC_QORIQ_CCSRBAR`) and build with + `-D_FILE_OFFSET_BITS=64` so `off_t` can carry the mmap offset. Getting this + wrong maps ordinary DRAM, and `CONFIG_STRICT_DEVMEM` refuses it with + "Program ... tried to access /dev/mem between fe0e0000->fe0e1000". +2. **Release the job ring from the kernel.** The in-tree `caam` driver claims + all four rings at boot. Unbind the one this driver uses: + `echo ffe301000.jr > /sys/bus/platform/drivers/caam_jr/unbind`. +3. **The 4 GB pointer limit stops the offload.** The driver runs the engine in + 32-bit descriptor pointer mode, so it refuses any buffer whose physical + address does not fit in 32 bits. On a 4 GB board ordinary user pages sit + well above that (measured: virtual `0x100b4f10` -> physical + `0x1_EF9C2F10`), so every job is refused, the callback returns + `CRYPTOCB_UNAVAILABLE`, and software quietly does the work. + `wolfcrypt_test` therefore passes in full while offloading nothing, and the + benchmark's HW and SW rows come out identical (RSA-2048 public 790.6 vs + 788.9 ops/s). Making this useful needs 64-bit descriptor pointer mode + (`MCFGR[PS]`), or DMA buffers allocated below 4 GB, which userspace cannot + arrange on its own. + +The fallback behaving silently is by design, but it does mean a Linux user has +no signal that nothing is being accelerated. Check the offload counters in +`SecQoriqDev` rather than assuming. + +Enabling the port forces `WOLF_CRYPTO_CB` on. A minimal build that wants to +call the driver API directly without the callback layer can define +`WOLFSSL_SEC_QORIQ_NO_CRYPTOCB`. + +#### Configuration macros + +| Macro | Default | Meaning | +|---|---|---| +| `SEC_QORIQ_CCSRBAR` | `0xFE000000` | Physical base of the CCSR window. Board and boot-loader specific: the CW VPX3-152 U-Boot relocates it to `0xEF000000`. | +| `SEC_QORIQ_CCSRBAR_PHYS` | `SEC_QORIQ_CCSRBAR` | The same window as the OS sees it, as a 64-bit value. Only the Linux backend uses it. On a 36-bit part CCSR is `0xF_FE000000`. | +| `SEC_QORIQ_JR_INDEX` | 0 | Which of the four job rings to claim. | +| `SEC_QORIQ_RING_SIZE` | 4 | Entries per ring. | +| `SEC_QORIQ_MIN_OFFLOAD_SZ` | 256 | Below this many bytes, AES-CBC/CTR/ECB go to software. See the benchmarks. | +| `SEC_QORIQ_DMA_POOL_SZ` | 8192 | Bare-metal static pool backing the job rings. | +| `WOLFSSL_SEC_QORIQ_DEVID` | `0x53454351` | devId used to select the engine. | +| `WOLFSSL_SEC_QORIQ_NO_PKHA` | undefined | Leave ECDSA and ECDH in software even on a part with a PKHA. | +| `WOLFSSL_SEC_QORIQ_SWAP_REGS` | undefined | Reserved. Defining it is currently a compile error: it would swap MMIO access but not descriptor or ring words, so a little-endian host needs that added first. | + +### Verification + +`wolfcrypt_test()` passes in full with the engine enabled: **28 tests, zero +failures**, on the CW VPX3-152 (T2080E) bare metal under wolfBoot's test-app. +That run pushed **3,407,201 descriptors** through the job ring. + +The port also exposes offload counters on `SecQoriqDev` (`jobCount`, +`cbHashCount` / `cbHashOffload`, `cbCipherCount` / `cbCipherOffload`, +`cbPkCount` / `cbPkOffload`, `cbSeedCount`) so a build can prove what actually +reached the engine rather than inferring it from timings. On the run above: +cipher 3,407,450 seen and 3,407,201 offloaded, hash 343,747 seen and **0 +offloaded**, which is exactly what the identical SHA software/hardware rows +below should look like. + +### Performance + +#### wolfCrypt benchmark, software versus engine + +`wolfcrypt/benchmark`, `BENCH_EMBEDDED` (1 KB buffers), on the CW VPX3-152 +(T2080E, CPU 1200 MHz). wolfCrypt prints the software and hardware rows from +the same run, so these are directly comparable. MiB/s. + +| Algorithm | Software | SEC | Speedup | +|---|---:|---:|---:| +| AES-128-CBC-enc | 21.96 | **122.83** | 5.6x | +| AES-128-CTR | 22.08 | **119.06** | 5.4x | +| AES-128-GCM-enc | 8.07 | **107.42** | 13.3x | +| AES-256-GCM-enc | 6.94 | **102.50** | 14.8x | +| AES-128-GCM-enc-no_AAD | 8.19 | **118.99** | 14.5x | +| AES-256-GCM-enc-no_AAD | 7.19 | **107.20** | 14.9x | +| SHA-256 | 36.83 | 36.31 | not offloaded | +| SHA-384 | 20.25 | 20.09 | not offloaded | + +Public key, same run, in operations per second: + +| Operation | Software | SEC | Speedup | +|---|---:|---:|---:| +| ECDSA P-256 verify | 49.39 | **591.98** | **12.0x** | +| ECDSA P-256 sign | 89.53 | **785.66** | **8.8x** | +| ECDHE P-256 agree | 100.18 | **803.40** | **8.0x** | + +Verify gains the most, which is the right way round: it is the operation a +TLS client performs on every handshake and the slowest of the three in +software. At 1.69 ms against 20.2 ms, certificate chain validation stops being +the dominant cost of a connection on this silicon. + +AES-GCM is the standout at 13-15x, and it is also the TLS 1.3 record cipher. +Software GHASH on this core has no carryless-multiply support and runs at a +flat 7-8 MiB/s no matter the key size, so the engine wins by a wide margin. +The SHA rows are unchanged because hashing is deliberately not routed; see +above. + +These numbers were taken with `NO_ASM=1`. The PPC assembly cannot be enabled +in the same run because wolfSSL's PPC asm SHA-512/384 currently fails the +large streaming-input case in `wolfcrypt_test()` (`test.c:7069`), a +pre-existing issue unrelated to this port. + +#### Size sweep from the bring-up harness + +A separate bare-metal harness measured the same operations across buffer +sizes to find where offloading starts to pay. Both sides ran in the same +binary over the same buffers, timed with the e6500 timebase, in KB/s. +`SEC/SW` above 1.00 means the engine wins. + +Software here is the configuration wolfBoot ships on this board: PPC32 +assembly for SHA, plain C for AES. + +| Algorithm | Size | SEC | Software | SEC/SW | +|---|---:|---:|---:|---:| +| SHA-256 | 64 | 17568 | 20782 | 0.84x | +| SHA-256 | 256 | 63891 | 36286 | 1.76x | +| SHA-256 | 1400 | 218644 | 45021 | 4.85x | +| SHA-256 | 16384 | 440574 | 48021 | 9.17x | +| SHA-384 | 64 | 17459 | 7288 | 2.39x | +| SHA-384 | 256 | 58757 | 13123 | 4.47x | +| SHA-384 | 1400 | 194183 | 22217 | 8.74x | +| SHA-384 | 16384 | 368240 | 26954 | 13.66x | +| AES-128-CBC | 64 | 13720 | 22389 | 0.61x | +| AES-128-CBC | 256 | 49356 | 35612 | 1.38x | +| AES-128-CBC | 1392 | 165630 | 42376 | 3.90x | +| AES-128-CBC | 16384 | 322236 | 44089 | 7.30x | +| AES-128-GCM | 64 | 13269 | 5760 | 2.30x | +| AES-128-GCM | 256 | 47029 | 7528 | 6.24x | +| AES-128-GCM | 1400 | 161533 | 8169 | 19.77x | +| AES-128-GCM | 16384 | 320900 | 8335 | 38.50x | + +There is a fixed per-job cost of roughly 3.5 microseconds: descriptor +construction, two cache flushes, the ring write, the MMIO kick and the +completion poll. That is why every SEC row at 64 bytes lands near 13-17.5 +MB/s regardless of algorithm. Above about 1 KB it amortises away and the +engine reaches 320-440 MB/s. + +Consequences worth knowing: + +- **AES-GCM wins at every size**, including 64 bytes, because software GHASH + on this core has no carryless-multiply support and runs at a flat 8 MB/s. + This is also the TLS 1.3 record cipher, so it is the case that matters most. +- **SHA-384 wins at every size**, because the 64-bit SHA-512 core is expensive + in 32-bit software. +- **SHA-256 and AES-CBC lose below roughly 128 bytes.** Both have fast + software paths here. `SEC_QORIQ_MIN_OFFLOAD_SZ` keeps small AES buffers in + software for this reason. + +The driver currently uses one job ring, submits one descriptor at a time and +busy-waits for completion. The T2080 has four rings and four DECOs, so +batching or overlapping submissions would cut the effective per-job cost and +move the crossover down toward small-record sizes. + +### Limitations + +- Buffers must be physically contiguous. There is no scatter-gather support, + so fragmented TLS records need to be linearised by the caller. The Linux + backend verifies contiguity page by page and refuses a buffer that + straddles non-adjacent frames rather than DMAing past the first one. +- A single command carries at most 64 KB. Hashing chains multiple FIFO loads + to cover longer messages; AES does not, so the callback declines larger AES + buffers and lets software take them. +- AES-GCM accepts only a 12 byte IV. The AESA derives J0 = IV || 0^31 || 1, + which is the SP 800-38D construction for that length only; other lengths + need J0 = GHASH(IV || pad || len(IV)), which the engine does not do. +- AES-GCM accepts only a full 16 byte tag. The engine performs the ICV + comparison itself on decrypt and rejects a truncated tag, so shorter tags + go to software. TLS always uses 16. +- Public key covers ECDSA and ECDH only. RSA and finite-field DH modular + exponentiation are not wired up yet, and key generation stays in software so + that the caller's `WC_RNG` remains the source of the private key. +- The domain parameters are converted from wolfCrypt's tables on every public + key call. It is a few hundred bytes of byte-string work against a + millisecond of engine time, so it does not show up in the measurements + above, but caching them per curve is the obvious next saving. +- ECDSA sign uses the engine's internal per-signature nonce rather than the + `WC_RNG` the caller passed, because the protocol descriptor generates it in + hardware. Deterministic ECDSA (RFC 6979) therefore cannot be offloaded and + falls back to software. +- A digest wider than the curve order is reduced to the leftmost order bits, + as ECDSA prescribes, so SHA-384 or SHA-512 over P-256 offloads normally. + The one shape declined is a curve whose order does not fill the fixed width + block, because wolfCrypt finishes such a truncation with a sub-byte shift + that this layout cannot express, and copying whole bytes instead would + build a signature its own verifier rejects. No curve in common use is + affected: P-256 and P-384 have byte aligned orders, and P-521 never + truncates, no digest wolfCrypt carries being wider than its 521 bit order. +- The port is stack hungry on the public key paths, roughly 900 bytes on top + of what wolfCrypt's own ECC frames need (a `MAX_ECC_BYTES * 9` domain + parameter block plus a `MAX_ECC_BYTES * 4` scratch in the router). Bare + metal targets should budget for it or build with `WOLFSSL_SMALL_STACK`, + which moves both to the heap. +- 32-bit descriptor pointers only. All DMA buffers must live below 4 GB. + `wc_SecQoriqInit()` refuses to run if the engine is already in 64-bit + pointer mode, and buffer translation fails rather than truncating. +- AES-CBC/CTR/ECB require whole 16-byte blocks. Partial-block CTR, where + wolfCrypt keeps leftover keystream, falls back to software. +- One job ring, polled, serialised on the wolfSSL hardware mutex. No + interrupt support and no concurrent submission. +- Output buffers should not share a cache line with data another thread + writes while a job is in flight. The driver flushes before submitting and + invalidates afterwards, which is safe for its own accesses, but a + concurrent writer to an adjacent object in the same line can still lose an + update on a non-coherent path. +- There is no in-tree test for the port yet. It was validated with an + external bare-metal known-answer harness covering hashing, AES CBC/CTR/ECB, + GCM including a corrupted-tag rejection, RNG4, and ECDSA/ECDH cross-checked + against the software implementation. + ## Support For questions please email support@wolfssl.com - diff --git a/wolfcrypt/src/port/nxp/sec_qoriq.c b/wolfcrypt/src/port/nxp/sec_qoriq.c new file mode 100644 index 00000000000..1b7dd7f3ffe --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq.c @@ -0,0 +1,577 @@ +/* sec_qoriq.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 + */ + +/* + * Core of the QorIQ SEC driver: bring-up, job ring management, descriptor + * assembly and submission. Platform specifics live behind the seam declared + * at the bottom of sec_qoriq.h. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_SEC_QORIQ + +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +/* The one device this driver manages; a second would need its own ring. */ +static SecQoriqDev secQoriqDev; + +/****************************************************************************** + Register access. CCSR registers are big endian and so is the CPU, so no + swap is needed; the swap path exists for a little endian QorIQ host. + ****************************************************************************/ + +#ifdef WOLFSSL_SEC_QORIQ_SWAP_REGS + #define SEC_SWAP32(x) \ + ((((x) & 0x000000FFU) << 24) | (((x) & 0x0000FF00U) << 8) | \ + (((x) & 0x00FF0000U) >> 8) | (((x) & 0xFF000000U) >> 24)) +#else + #define SEC_SWAP32(x) (x) +#endif + +word32 wc_SecQoriqRead(const byte* base, word32 off) +{ + return SEC_SWAP32(*(volatile const word32*)(base + off)); +} + +void wc_SecQoriqWrite(byte* base, word32 off, word32 val) +{ + *(volatile word32*)(base + off) = SEC_SWAP32(val); +} + +#define secRead wc_SecQoriqRead +#define secWrite wc_SecQoriqWrite + +SecQoriqDev* wc_SecQoriqGetDev(void) +{ + if (secQoriqDev.initialized == 0) { + return NULL; + } + return &secQoriqDev; +} + +/****************************************************************************** + Descriptor assembly + ****************************************************************************/ + +int wc_SecQoriqDescInit(SecQoriqDesc* desc) +{ + if (desc == NULL) { + return BAD_FUNC_ARG; + } + + XMEMSET(desc, 0, sizeof(SecQoriqDesc)); + desc->desc[0] = SEC_QORIQ_CMD_HEAD; + desc->idx = 1; + desc->startIdx = 1; + + return 0; +} + +int wc_SecQoriqDescAddWord(SecQoriqDesc* desc, word32 in) +{ + if (desc == NULL) { + return BAD_FUNC_ARG; + } + if (desc->idx >= SEC_QORIQ_DESC_MAX_WORDS) { + WOLFSSL_MSG("sec_qoriq: descriptor full"); + return BUFFER_E; + } + + desc->desc[desc->idx++] = in; + return 0; +} + +/* Only 32-bit pointer mode is supported, so an address above 4 GB is a hard + * error rather than a silent truncation. */ +int wc_SecQoriqDescAddPtr(SecQoriqDesc* desc, word64 phys) +{ + if (desc == NULL) { + return BAD_FUNC_ARG; + } + if ((phys >> 32) != 0) { + WOLFSSL_MSG("sec_qoriq: buffer above 4GB, needs 64-bit pointer mode"); + return BAD_FUNC_ARG; + } + + return wc_SecQoriqDescAddWord(desc, (word32)phys); +} + +/* Append a command word carrying a length, followed by the buffer address. */ +int wc_SecQoriqDescAddBuf(SecQoriqDesc* desc, word32 cmd, const byte* buf, + word32 bufSz) +{ + word64 phys; + int ret; + + if (desc == NULL || buf == NULL) { + return BAD_FUNC_ARG; + } + if (bufSz > SEC_QORIQ_MAX_XFER_SZ) { + WOLFSSL_MSG("sec_qoriq: buffer too large for a single command"); + return BUFFER_E; + } + + ret = wc_SecQoriqDescAddWord(desc, cmd | bufSz); + if (ret != 0) { + return ret; + } + + phys = wc_SecQoriqVirtToPhysLen((void*)buf, bufSz); + if (phys == 0) { + WOLFSSL_MSG("sec_qoriq: buffer not translatable or not contiguous"); + return BAD_FUNC_ARG; + } + + return wc_SecQoriqDescAddPtr(desc, phys); +} + +/* Patch the header with the final length. Low 7 bits are the total word + * count, bits 22:16 the index at which the job descriptor proper starts. */ +static void secDescFinalize(SecQoriqDesc* desc) +{ + desc->desc[0] &= 0xFFFFFF80U; + desc->desc[0] |= (desc->idx & 0x7FU) | ((desc->startIdx & 0x7FU) << 16); +} + +/****************************************************************************** + Error decoding + ****************************************************************************/ + +int wc_SecQoriqParseError(word32 status) +{ + word32 src = (status & SEC_QORIQ_SSRC_MASK) >> SEC_QORIQ_SSRC_SHIFT; + + if (status == 0) { + return 0; + } + + /* A CCB error with error id ICVCHK is the AEAD integrity check failing: + * an authentication failure, not a hardware fault. */ + if (src == SEC_QORIQ_SSRC_CCB && + (status & SEC_QORIQ_CCBERR_ERRID_MASK) == + SEC_QORIQ_CCBERR_ERRID_ICV) { + WOLFSSL_MSG("sec_qoriq: ICV check failed"); + return AES_GCM_AUTH_E; + } + +#ifdef DEBUG_WOLFSSL + { + const char* srcStr; + + switch (src) { + case SEC_QORIQ_SSRC_CCB: srcStr = "CCB"; break; + case SEC_QORIQ_SSRC_JUMP: srcStr = "jump halt, user"; break; + case SEC_QORIQ_SSRC_DECO: srcStr = "DECO"; break; + case SEC_QORIQ_SSRC_QI: srcStr = "queue interface"; break; + case SEC_QORIQ_SSRC_JR: srcStr = "job ring"; break; + case SEC_QORIQ_SSRC_JUMP_CC: srcStr = "jump halt, condition"; + break; + default: srcStr = "unknown"; break; + } + WOLFSSL_MSG_EX("sec_qoriq: job failed, status 0x%08x, source %s", + status, srcStr); + } +#endif + + return WC_HW_E; +} + +/****************************************************************************** + Job submission + ****************************************************************************/ + +/* One job ring, one descriptor in flight. Without this mutex two threads + * racing through the callback overwrite each other's ring slot and + * desynchronise the index mirrors. */ +static int secRunLocked(SecQoriqDev* dev, SecQoriqDesc* desc); +static int secJrReset(SecQoriqDev* dev); +static void secRingsProgram(SecQoriqDev* dev); + +int wc_SecQoriqRun(SecQoriqDev* dev, SecQoriqDesc* desc) +{ + return wc_SecQoriqRunEx(dev, desc, NULL); +} + +int wc_SecQoriqRunEx(SecQoriqDev* dev, SecQoriqDesc* desc, word32* statusOut) +{ + int ret; + + if (dev == NULL || desc == NULL) { + return BAD_FUNC_ARG; + } + + if (statusOut != NULL) { + *statusOut = 0; + } + + ret = wolfSSL_CryptHwMutexLock(); + if (ret != 0) { + return ret; + } + + ret = secRunLocked(dev, desc); + + /* Read the status while still holding the lock: dev->lastStatus belongs + * to whichever job retired most recently, which under concurrency is not + * necessarily this one. */ + if (statusOut != NULL) { + *statusOut = dev->lastStatus; + } + + wolfSSL_CryptHwMutexUnLock(); + + return ret; +} + +void wc_SecQoriqForceZeroDma(void* buf, word32 sz) +{ + if (buf == NULL || sz == 0) { + return; + } + + ForceZero(buf, sz); + (void)wc_SecQoriqCacheFlush(buf, sz); +} + +static int secRunLocked(SecQoriqDev* dev, SecQoriqDesc* desc) +{ + word64 descPhys; + word32 descBytes; + word32 status; + word32 waited; + word32 slot; + int ret; + + if (dev == NULL || desc == NULL) { + return BAD_FUNC_ARG; + } + if (dev->initialized == 0) { + return WC_HW_E; + } + + /* The failure paths below return before a new status is read, so a + * caller classifying the outcome must not see a stale one. */ + dev->lastStatus = 0; + + dev->jobCount++; + secDescFinalize(desc); + descBytes = desc->idx * (word32)sizeof(word32); + + descPhys = wc_SecQoriqVirtToPhysLen(desc->desc, descBytes); + if (descPhys == 0 || (descPhys >> 32) != 0) { + WOLFSSL_MSG("sec_qoriq: descriptor not reachable by the engine"); + return BAD_FUNC_ARG; + } + + /* The engine reads the descriptor from memory, so push it out first. */ + ret = wc_SecQoriqCacheFlush(desc->desc, descBytes); + if (ret != 0) { + return ret; + } + + if (secRead(dev->jr, SEC_QORIQ_IRSA) == 0) { + WOLFSSL_MSG("sec_qoriq: no room on the input ring"); + return WC_HW_E; + } + + dev->inRing[dev->inIdx] = (word32)descPhys; + ret = wc_SecQoriqCacheFlush(&dev->inRing[dev->inIdx], sizeof(word32)); + if (ret != 0) { + return ret; + } + + /* Publish the entry, then tell the engine one job was added. */ + secWrite(dev->jr, SEC_QORIQ_IRJA, 1); + dev->inIdx = (dev->inIdx + 1) % SEC_QORIQ_RING_SIZE; + + waited = 0; + while (secRead(dev->jr, SEC_QORIQ_ORSF) == 0) { + if (++waited > SEC_QORIQ_POLL_MAX) { + /* The engine may still hold this descriptor, which lives on the + * caller's stack, and the index mirrors no longer track it. + * Reset the ring so it cannot write into a frame about to be + * reused. If the reset fails, mark the device dead so later + * calls fail fast rather than corrupt memory. */ + WOLFSSL_MSG("sec_qoriq: timed out waiting for job completion"); + if (secJrReset(dev) != 0) { + dev->initialized = 0; + } + else { + secRingsProgram(dev); + } + return WC_HW_E; + } + wc_SecQoriqCpuRelax(); + } + + /* Output entries are two words wide: {descriptor address, status}. */ + slot = dev->outIdx * 2; + + ret = wc_SecQoriqCacheInval(&dev->outRing[slot], 2 * sizeof(word32)); + if (ret != 0) { + return ret; + } + + /* Confirm the engine retired our descriptor before trusting its + * status. */ + if (dev->outRing[slot] != (word32)descPhys) { + WOLFSSL_MSG("sec_qoriq: output ring returned an unexpected job"); + secWrite(dev->jr, SEC_QORIQ_ORJR, 1); + dev->outIdx = (dev->outIdx + 1) % SEC_QORIQ_RING_SIZE; + return WC_HW_E; + } + status = dev->outRing[slot + 1]; + dev->lastStatus = status; + + /* Release the slot back to the engine. */ + secWrite(dev->jr, SEC_QORIQ_ORJR, 1); + dev->outIdx = (dev->outIdx + 1) % SEC_QORIQ_RING_SIZE; + + return wc_SecQoriqParseError(status); +} + +/****************************************************************************** + Bring-up + ****************************************************************************/ + +/* Reset the job ring; the engine clears JRCR[RESET] once drained. */ +static int secJrReset(SecQoriqDev* dev) +{ + word32 waited = 0; + + secWrite(dev->jr, SEC_QORIQ_JRCR, SEC_QORIQ_JRCR_RESET); + while (secRead(dev->jr, SEC_QORIQ_JRCR) & SEC_QORIQ_JRCR_RESET) { + if (++waited > SEC_QORIQ_POLL_MAX) { + WOLFSSL_MSG("sec_qoriq: job ring reset did not complete"); + return WC_HW_E; + } + wc_SecQoriqCpuRelax(); + } + + /* Clear any latched interrupt status left over from a previous owner. */ + secWrite(dev->jr, SEC_QORIQ_JRINT, secRead(dev->jr, SEC_QORIQ_JRINT)); + + /* The engine restarts at slot 0 after a ring reset, so our mirrors do + * too. */ + dev->inIdx = 0; + dev->outIdx = 0; + + return 0; +} + +static int secRingsAlloc(SecQoriqDev* dev) +{ + word32 inSz = SEC_QORIQ_RING_SIZE * (word32)sizeof(word32); + word32 outSz = SEC_QORIQ_RING_SIZE * 2 * (word32)sizeof(word32); + + dev->inRing = (word32*)wc_SecQoriqDmaAlloc(inSz, &dev->inRingPhys); + if (dev->inRing == NULL) { + return MEMORY_E; + } + + dev->outRing = (word32*)wc_SecQoriqDmaAlloc(outSz, &dev->outRingPhys); + if (dev->outRing == NULL) { + wc_SecQoriqDmaFree(dev->inRing, dev->inRingPhys, inSz); + dev->inRing = NULL; + return MEMORY_E; + } + + if ((dev->inRingPhys >> 32) != 0 || (dev->outRingPhys >> 32) != 0) { + WOLFSSL_MSG("sec_qoriq: rings allocated above 4GB"); + return MEMORY_E; + } + + XMEMSET(dev->inRing, 0, inSz); + XMEMSET(dev->outRing, 0, outSz); + + if (wc_SecQoriqCacheFlush(dev->inRing, inSz) != 0 || + wc_SecQoriqCacheFlush(dev->outRing, outSz) != 0) { + return WC_HW_E; + } + + return 0; +} + +/* Newest first: the bare-metal backend is a bump allocator that can only + * rewind its most recent allocation. */ +static void secRingsFree(SecQoriqDev* dev) +{ + if (dev->outRing != NULL) { + wc_SecQoriqDmaFree(dev->outRing, dev->outRingPhys, + SEC_QORIQ_RING_SIZE * 2 * (word32)sizeof(word32)); + dev->outRing = NULL; + } + if (dev->inRing != NULL) { + wc_SecQoriqDmaFree(dev->inRing, dev->inRingPhys, + SEC_QORIQ_RING_SIZE * (word32)sizeof(word32)); + dev->inRing = NULL; + } +} + +/* The base address registers are 64-bit even in 32-bit pointer mode. */ +static void secRingsProgram(SecQoriqDev* dev) +{ + secWrite(dev->jr, SEC_QORIQ_IRBA_MS, (word32)(dev->inRingPhys >> 32)); + secWrite(dev->jr, SEC_QORIQ_IRBA_LS, (word32)dev->inRingPhys); + secWrite(dev->jr, SEC_QORIQ_IRS, SEC_QORIQ_RING_SIZE); + + secWrite(dev->jr, SEC_QORIQ_ORBA_MS, (word32)(dev->outRingPhys >> 32)); + secWrite(dev->jr, SEC_QORIQ_ORBA_LS, (word32)dev->outRingPhys); + secWrite(dev->jr, SEC_QORIQ_ORS, SEC_QORIQ_RING_SIZE); +} + +int wc_SecQoriqInit(void) +{ + SecQoriqDev* dev = &secQoriqDev; + word32 svr = 0; + word32 mcfgr, scfgr, chanumMs; + int ret; + + if (dev->initialized) { + return 0; + } + + XMEMSET(dev, 0, sizeof(SecQoriqDev)); + dev->jrIndex = SEC_QORIQ_JR_INDEX; + + /* A part without the engine has no SEC block, and reading its address + * space is not guaranteed to fault cleanly. */ + ret = wc_SecQoriqGetSvr(&svr); + if (ret != 0) { + return ret; + } + if ((svr & SEC_QORIQ_SVR_E_BIT) == 0) { + WOLFSSL_MSG("sec_qoriq: part has no security engine (not an E SKU)"); + return NOT_COMPILED_IN; + } + + ret = wc_SecQoriqMapRegs(&dev->regs); + if (ret != 0) { + return ret; + } + dev->jr = dev->regs + SEC_QORIQ_JR_OFFSET(dev->jrIndex); + + /* Cache what later code needs to make capability decisions. */ + dev->era = (secRead(dev->regs, SEC_QORIQ_CCBVID) & + SEC_QORIQ_CCBVID_ERA_MASK) >> SEC_QORIQ_CCBVID_ERA_SHIFT; + dev->chaNumLs = secRead(dev->regs, SEC_QORIQ_CHANUM_LS); + dev->chaVidLs = secRead(dev->regs, SEC_QORIQ_CHAVID_LS); + + chanumMs = secRead(dev->regs, SEC_QORIQ_CHANUM_MS); + if (((chanumMs >> SEC_QORIQ_CHANUM_MS_JRNUM_SHIFT) & 0xF) <= + dev->jrIndex) { + WOLFSSL_MSG("sec_qoriq: requested job ring does not exist"); + wc_SecQoriqUnmapRegs(dev->regs); + return BAD_FUNC_ARG; + } + + /* Refuse rather than hand the engine addresses it reads as 64-bit. */ + mcfgr = secRead(dev->regs, SEC_QORIQ_MCFGR); + if (mcfgr & SEC_QORIQ_MCFGR_LONG_PTR) { + WOLFSSL_MSG("sec_qoriq: engine is in 64-bit pointer mode, unsupported"); + wc_SecQoriqUnmapRegs(dev->regs); + return NOT_COMPILED_IN; + } + dev->longPtr = 0; + + ret = secRingsAlloc(dev); + if (ret != 0) { + secRingsFree(dev); + wc_SecQoriqUnmapRegs(dev->regs); + return ret; + } + + ret = secJrReset(dev); + if (ret != 0) { + secRingsFree(dev); + wc_SecQoriqUnmapRegs(dev->regs); + return ret; + } + + secRingsProgram(dev); + + /* With virtualization enabled the ring stays parked until started. + * Both boards tested have VIRT_EN clear, but honour it anyway. */ + scfgr = secRead(dev->regs, SEC_QORIQ_SCFGR); + if (scfgr & SEC_QORIQ_SCFGR_VIRT_EN) { + word32 jrstart = secRead(dev->regs, SEC_QORIQ_JRSTART); + secWrite(dev->regs, SEC_QORIQ_JRSTART, + jrstart | SEC_QORIQ_JRSTART_JR(dev->jrIndex)); + } + + /* RNG offload needs state handle 0 instantiated, which the boot loaders + * tested do not do. Record it and let the RNG paths decide. */ + dev->rngReady = (secRead(dev->regs, SEC_QORIQ_RDSTA) & + SEC_QORIQ_RDSTA_IF0) ? 1 : 0; + + dev->initialized = 1; + +#if defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_SEC_QORIQ_NO_CRYPTOCB) + ret = wc_SecQoriqRegisterCryptoCb(); + if (ret != 0) { + WOLFSSL_MSG("sec_qoriq: could not register the crypto callback"); + dev->initialized = 0; + secRingsFree(dev); + wc_SecQoriqUnmapRegs(dev->regs); + return ret; + } +#endif + + WOLFSSL_MSG("sec_qoriq: initialized"); + return 0; +} + +int wc_SecQoriqFree(void) +{ + SecQoriqDev* dev = &secQoriqDev; + + if (dev->initialized == 0) { + return 0; + } + +#if defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_SEC_QORIQ_NO_CRYPTOCB) + wc_SecQoriqUnregisterCryptoCb(); +#endif + + (void)secJrReset(dev); + secRingsFree(dev); + wc_SecQoriqUnmapRegs(dev->regs); + + XMEMSET(dev, 0, sizeof(SecQoriqDev)); + + return 0; +} + +#endif /* WOLFSSL_SEC_QORIQ */ diff --git a/wolfcrypt/src/port/nxp/sec_qoriq_aes.c b/wolfcrypt/src/port/nxp/sec_qoriq_aes.c new file mode 100644 index 00000000000..b517add4cd7 --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq_aes.c @@ -0,0 +1,440 @@ +/* sec_qoriq_aes.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 + */ + +/* + * Confidentiality-only AES modes on the QorIQ SEC, driven through the AESA. + * CBC, CTR and ECB. The authenticated modes need a different descriptor + * shape and live elsewhere. + * + * Descriptor shape, following the engine's KEY -> context -> input -> output + * ordering: + * + * KEY class 1, no write back + * LOAD_CTX class 1, the IV or counter block (CBC and CTR only) + * OPERATION class 1, the mode, encrypt or decrypt + * FIFO_LOAD class 1, the message, last + * FIFO_STORE the result + * STORE_CTX class 1, the updated IV or counter (CBC and CTR only) + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SEC_QORIQ) && !defined(NO_AES) + +#include +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +#define SEC_QORIQ_AES_BLOCK 16 + + + + +static int secAesModeCtxOffset(word32 mode, word32* ofstOut, int* needsIv) +{ + switch (mode) { + case SEC_QORIQ_AESCBC: + *ofstOut = SEC_QORIQ_CTX_OFST_CBC; + *needsIv = 1; + return 0; + case SEC_QORIQ_AESCTR: + *ofstOut = SEC_QORIQ_CTX_OFST_CTR; + *needsIv = 1; + return 0; + case SEC_QORIQ_AESECB: + *ofstOut = 0; + *needsIv = 0; + return 0; + default: + break; + } + + WOLFSSL_MSG("sec_qoriq: unsupported AES mode"); + return BAD_FUNC_ARG; +} + +/* iv is updated in place when the mode keeps a chaining value, matching what + * wolfCrypt expects of wc_AesCbcEncrypt and friends. */ +int wc_SecQoriqAes(word32 mode, int encrypt, const byte* key, word32 keySz, + byte* iv, const byte* in, word32 inSz, byte* out) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + SecQoriqDesc desc; + word32 ofst = 0; + int needsIv = 0; + int ret; + + if (key == NULL || in == NULL || out == NULL) { + return BAD_FUNC_ARG; + } + if (keySz != 16 && keySz != 24 && keySz != 32) { + WOLFSSL_MSG("sec_qoriq: bad AES key size"); + return BAD_FUNC_ARG; + } + if (inSz == 0 || (inSz % SEC_QORIQ_AES_BLOCK) != 0) { + /* the partial-block handling of CTR is done by the caller */ + WOLFSSL_MSG("sec_qoriq: AES input must be a whole number of blocks"); + return BAD_FUNC_ARG; + } + if (dev == NULL) { + return WC_HW_E; + } + + ret = secAesModeCtxOffset(mode, &ofst, &needsIv); + if (ret != 0) { + return ret; + } + if (needsIv && iv == NULL) { + return BAD_FUNC_ARG; + } + + ret = wc_SecQoriqDescInit(&desc); + if (ret != 0) { + return ret; + } + + /* NWB: the engine must not write the expanded key back to memory. */ + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_KEY | SEC_QORIQ_CLASS1 | + SEC_QORIQ_CMD_NWB, key, keySz); + if (ret != 0) { + return ret; + } + + if (needsIv) { + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_LOAD_CTX | + SEC_QORIQ_CLASS1 | ofst, iv, SEC_QORIQ_AES_BLOCK); + if (ret != 0) { + return ret; + } + } + + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_OP | SEC_QORIQ_CLASS1 | + mode | SEC_QORIQ_ALG_UPDATE | + (encrypt ? SEC_QORIQ_ENC : SEC_QORIQ_DEC)); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_L | SEC_QORIQ_CLASS1 | + SEC_QORIQ_FIFOL_TYPE_MSG | SEC_QORIQ_FIFOL_TYPE_LC1, in, inSz); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_S | + SEC_QORIQ_FIFOS_TYPE_MSG, out, inSz); + if (ret != 0) { + return ret; + } + + if (needsIv) { + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_STORE_CTX | + SEC_QORIQ_CLASS1 | ofst, iv, SEC_QORIQ_AES_BLOCK); + if (ret != 0) { + return ret; + } + } + + /* Everything the engine reads has to be in memory, and anything it + * writes must not be shadowed by a dirty line. */ + ret = wc_SecQoriqCacheFlush((void*)key, keySz); + if (ret == 0) { + ret = wc_SecQoriqCacheFlush((void*)in, inSz); + } + if (ret == 0) { + ret = wc_SecQoriqCacheFlush(out, inSz); + } + if (ret == 0 && needsIv) { + ret = wc_SecQoriqCacheFlush(iv, SEC_QORIQ_AES_BLOCK); + } + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqRun(dev, &desc); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqCacheInval(out, inSz); + if (ret == 0 && needsIv) { + ret = wc_SecQoriqCacheInval(iv, SEC_QORIQ_AES_BLOCK); + } + + return ret; +} + +int wc_SecQoriqAesCbcEncrypt(const byte* key, word32 keySz, byte* iv, + const byte* in, word32 inSz, byte* out) +{ + return wc_SecQoriqAes(SEC_QORIQ_AESCBC, 1, key, keySz, iv, in, inSz, out); +} + +int wc_SecQoriqAesCbcDecrypt(const byte* key, word32 keySz, byte* iv, + const byte* in, word32 inSz, byte* out) +{ + return wc_SecQoriqAes(SEC_QORIQ_AESCBC, 0, key, keySz, iv, in, inSz, out); +} + +int wc_SecQoriqAesCtrEncrypt(const byte* key, word32 keySz, byte* iv, + const byte* in, word32 inSz, byte* out) +{ + /* CTR is its own inverse. */ + return wc_SecQoriqAes(SEC_QORIQ_AESCTR, 1, key, keySz, iv, in, inSz, out); +} + +int wc_SecQoriqAesEcbEncrypt(const byte* key, word32 keySz, const byte* in, + word32 inSz, byte* out) +{ + return wc_SecQoriqAes(SEC_QORIQ_AESECB, 1, key, keySz, NULL, in, inSz, + out); +} + +int wc_SecQoriqAesEcbDecrypt(const byte* key, word32 keySz, const byte* in, + word32 inSz, byte* out) +{ + return wc_SecQoriqAes(SEC_QORIQ_AESECB, 0, key, keySz, NULL, in, inSz, + out); +} + +#ifdef HAVE_AESGCM + +/* + * GCM is laid out differently from the confidentiality-only modes: + * + * - the OPERATION comes before the IV rather than after it, and the IV + * arrives as a FIFO LOAD of type IV rather than as a context load, + * - the output FIFO STORE is emitted before the input FIFO LOAD, + * - every FIFO LOAD carries FC1 (flush class 1) except the last one, which + * carries LC1 instead. Which load is last depends on whether there is + * AAD and on the direction, so the flag is patched in afterwards, + * - on decrypt the expected tag is loaded as type ICV and the engine does + * the comparison itself, reporting a mismatch as a job error. + * + * Unlike CBC and friends the message length is arbitrary; GCM handles a + * partial trailing block. + */ +int wc_SecQoriqAesGcm(int encrypt, const byte* key, word32 keySz, + const byte* iv, word32 ivSz, const byte* aad, word32 aadSz, + const byte* in, word32 inSz, byte* out, byte* tag, word32 tagSz) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + SecQoriqDesc desc; + word32 lastFifo = 0; + int ret; + + if (key == NULL || iv == NULL || tag == NULL) { + return BAD_FUNC_ARG; + } + if (in == NULL && inSz > 0) { + return BAD_FUNC_ARG; + } + if (out == NULL && inSz > 0) { + return BAD_FUNC_ARG; + } + if (aad == NULL && aadSz > 0) { + return BAD_FUNC_ARG; + } + if (keySz != 16 && keySz != 24 && keySz != 32) { + return BAD_FUNC_ARG; + } + if (tagSz == 0 || tagSz > SEC_QORIQ_AES_BLOCK) { + return BAD_FUNC_ARG; + } + /* The AESA derives J0 = IV || 0^31 || 1, which is only the SP 800-38D + * construction for a 12 byte IV. For any other length J0 is + * GHASH(IV || pad || len(IV)), which the engine does not do and this + * port does not compute in software. Accepting other lengths would + * silently produce ciphertext and tags no peer can verify. */ + if (ivSz != SEC_QORIQ_GCM_IV_SZ) { + WOLFSSL_MSG("sec_qoriq: GCM requires a 12 byte IV"); + return BAD_FUNC_ARG; + } + if (dev == NULL) { + return WC_HW_E; + } + + ret = wc_SecQoriqDescInit(&desc); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_KEY | SEC_QORIQ_CLASS1 | + SEC_QORIQ_CMD_NWB, key, keySz); + if (ret != 0) { + return ret; + } + + /* INITF, not UPDATE: this is a single shot, so the engine has to both + * initialise the GHASH state and finalise the tag. With UPDATE the + * ciphertext still comes out correct but the stored "tag" is an + * unfinalised intermediate. */ + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_OP | SEC_QORIQ_CLASS1 | + SEC_QORIQ_AESGCM | SEC_QORIQ_ALG_INITF | + (encrypt ? SEC_QORIQ_ENC : (SEC_QORIQ_DEC | SEC_QORIQ_ALG_ICV))); + if (ret != 0) { + return ret; + } + + lastFifo = desc.idx; + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_L | SEC_QORIQ_CLASS1 | + SEC_QORIQ_FIFOL_TYPE_FC1 | SEC_QORIQ_FIFOL_TYPE_IV, iv, ivSz); + if (ret != 0) { + return ret; + } + + if (aadSz > 0) { + lastFifo = desc.idx; + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_L | + SEC_QORIQ_CLASS1 | SEC_QORIQ_FIFOL_TYPE_FC1 | + SEC_QORIQ_FIFOL_TYPE_AAD, aad, aadSz); + if (ret != 0) { + return ret; + } + } + + if (inSz > 0) { + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_S | + SEC_QORIQ_FIFOS_TYPE_MSG, out, inSz); + if (ret != 0) { + return ret; + } + + lastFifo = desc.idx; + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_L | + SEC_QORIQ_CLASS1 | SEC_QORIQ_FIFOL_TYPE_FC1 | + SEC_QORIQ_FIFOL_TYPE_MSG, in, inSz); + if (ret != 0) { + return ret; + } + } + + if (!encrypt) { + /* hand the engine the tag to check against */ + lastFifo = desc.idx; + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_L | + SEC_QORIQ_CLASS1 | SEC_QORIQ_FIFOL_TYPE_FC1 | + SEC_QORIQ_FIFOL_TYPE_ICV, tag, tagSz); + if (ret != 0) { + return ret; + } + } + + /* Whichever load ended up last must terminate the class 1 stream. */ + desc.desc[lastFifo] &= ~(word32)SEC_QORIQ_FIFOL_TYPE_FC1; + desc.desc[lastFifo] |= SEC_QORIQ_FIFOL_TYPE_LC1; + + if (encrypt) { + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_STORE_CTX | + SEC_QORIQ_CLASS1, tag, tagSz); + if (ret != 0) { + return ret; + } + } + + ret = wc_SecQoriqCacheFlush((void*)key, keySz); + if (ret == 0) { + ret = wc_SecQoriqCacheFlush((void*)iv, ivSz); + } + if (ret == 0 && aadSz > 0) { + ret = wc_SecQoriqCacheFlush((void*)aad, aadSz); + } + if (ret == 0 && inSz > 0) { + ret = wc_SecQoriqCacheFlush((void*)in, inSz); + } + if (ret == 0 && inSz > 0) { + ret = wc_SecQoriqCacheFlush(out, inSz); + } + if (ret == 0) { + ret = wc_SecQoriqCacheFlush(tag, tagSz); + } + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqRun(dev, &desc); + + /* Every failure returns here. Falling through would let the cache + * maintenance below overwrite ret with its own success, turning a + * hardware fault into a "decrypted" buffer the caller trusts. */ + if (ret != 0) { + /* The engine writes the decrypted text before it checks the tag, so + * on an authentication failure the caller's buffer already holds + * unauthenticated plaintext. Destroy that. + * + * Only then, though. Any other failure means the engine could not do + * the job, and the router turns those into CRYPTOCB_UNAVAILABLE so + * software redoes the work over this same buffer, which for in-place + * decryption is also the ciphertext. */ + if ((ret == WC_NO_ERR_TRACE(AES_GCM_AUTH_E)) && !encrypt && + (inSz > 0) && (out != NULL)) { + (void)wc_SecQoriqCacheInval(out, inSz); + ForceZero(out, inSz); + (void)wc_SecQoriqCacheFlush(out, inSz); + } + return ret; + } + + if (inSz > 0) { + ret = wc_SecQoriqCacheInval(out, inSz); + if (ret != 0) { + return ret; + } + } + if (encrypt) { + ret = wc_SecQoriqCacheInval(tag, tagSz); + } + + return ret; +} + +int wc_SecQoriqAesGcmEncrypt(const byte* key, word32 keySz, const byte* iv, + word32 ivSz, const byte* aad, word32 aadSz, const byte* in, word32 inSz, + byte* out, byte* tag, word32 tagSz) +{ + return wc_SecQoriqAesGcm(1, key, keySz, iv, ivSz, aad, aadSz, in, inSz, + out, tag, tagSz); +} + +int wc_SecQoriqAesGcmDecrypt(const byte* key, word32 keySz, const byte* iv, + word32 ivSz, const byte* aad, word32 aadSz, const byte* in, word32 inSz, + byte* out, const byte* tag, word32 tagSz) +{ + /* the engine only reads the tag on decrypt, the cast keeps the public + * shape const-correct for callers */ + return wc_SecQoriqAesGcm(0, key, keySz, iv, ivSz, aad, aadSz, in, inSz, + out, (byte*)tag, tagSz); +} + +#endif /* HAVE_AESGCM */ + +#endif /* WOLFSSL_SEC_QORIQ && !NO_AES */ diff --git a/wolfcrypt/src/port/nxp/sec_qoriq_baremetal.c b/wolfcrypt/src/port/nxp/sec_qoriq_baremetal.c new file mode 100644 index 00000000000..cbc77e5f999 --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq_baremetal.c @@ -0,0 +1,185 @@ +/* sec_qoriq_baremetal.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 + */ + +/* + * Bare-metal backend for the QorIQ SEC driver. + * + * Assumes a flat, identity mapped address space, which is how both wolfBoot + * and U-Boot leave the e5500/e6500 for the code they hand control to. There + * is no allocator, so DMA memory comes from a static pool. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SEC_QORIQ) && defined(WOLFSSL_SEC_QORIQ_BAREMETAL) + +#include +#include +#include + +/* e5500 and e6500 both use a 64 byte cache line. */ +#ifndef SEC_QORIQ_CACHE_LINE + #define SEC_QORIQ_CACHE_LINE 64 +#endif + +/* Backing store for job rings and any bounce buffers. Descriptors live on + * the caller's stack, which is also DMA reachable on this target. */ +#ifndef SEC_QORIQ_DMA_POOL_SZ + #define SEC_QORIQ_DMA_POOL_SZ 8192 +#endif + +static byte secDmaPool[SEC_QORIQ_DMA_POOL_SZ] + __attribute__((aligned(SEC_QORIQ_CACHE_LINE))); +static word32 secDmaUsed = 0; + +int wc_SecQoriqMapRegs(byte** regsOut) +{ + if (regsOut == NULL) { + return BAD_FUNC_ARG; + } + + /* Physically addressed, nothing to map. */ + *regsOut = (byte*)(SEC_QORIQ_CCSRBAR + SEC_QORIQ_OFFSET); + return 0; +} + +void wc_SecQoriqUnmapRegs(byte* regs) +{ + (void)regs; +} + +/* Bump allocator. The driver allocates its rings once at init and never + * frees them in normal operation, so reclaiming space is not worth the + * complexity; wc_SecQoriqDmaFree only rewinds the most recent allocation. */ +void* wc_SecQoriqDmaAlloc(word32 sz, word64* physOut) +{ + word32 aligned; + byte* ptr; + + if (physOut == NULL || sz == 0) { + return NULL; + } + + aligned = (sz + (SEC_QORIQ_CACHE_LINE - 1)) & + ~(word32)(SEC_QORIQ_CACHE_LINE - 1); + + if (secDmaUsed + aligned > SEC_QORIQ_DMA_POOL_SZ) { + WOLFSSL_MSG("sec_qoriq: DMA pool exhausted"); + return NULL; + } + + ptr = &secDmaPool[secDmaUsed]; + secDmaUsed += aligned; + + *physOut = (word64)(wolfssl_word)ptr; + return ptr; +} + +void wc_SecQoriqDmaFree(void* virt, word64 phys, word32 sz) +{ + word32 aligned = (sz + (SEC_QORIQ_CACHE_LINE - 1)) & + ~(word32)(SEC_QORIQ_CACHE_LINE - 1); + + (void)phys; + + if (virt == NULL) { + return; + } + + /* Only the most recent allocation can be given back. */ + if ((byte*)virt == &secDmaPool[secDmaUsed - aligned]) { + secDmaUsed -= aligned; + } +} + +/* The address space is flat and identity mapped, so every buffer is + * physically contiguous by construction and the length does not matter. */ +word64 wc_SecQoriqVirtToPhysLen(void* virt, word32 len) +{ + (void)len; + return (word64)(wolfssl_word)virt; +} + +/* dcbf writes back and invalidates, which is correct in both directions: + * pushing our writes out to the engine, and dropping any stale line before + * reading what the engine wrote. dcbi would be marginally cheaper on the + * inbound path but risks discarding a dirty line if a buffer is ever shared + * with CPU-written data, so it is deliberately not used. */ +static void secCacheOp(void* virt, word32 sz) +{ + wolfssl_word addr = (wolfssl_word)virt; + wolfssl_word end = addr + sz; + + addr &= ~(wolfssl_word)(SEC_QORIQ_CACHE_LINE - 1); + + __asm__ __volatile__("msync" ::: "memory"); + while (addr < end) { + __asm__ __volatile__("dcbf 0,%0" :: "r"(addr) : "memory"); + addr += SEC_QORIQ_CACHE_LINE; + } + __asm__ __volatile__("msync" ::: "memory"); +} + +int wc_SecQoriqCacheFlush(void* virt, word32 sz) +{ + if (virt == NULL) { + return BAD_FUNC_ARG; + } + secCacheOp(virt, sz); + return 0; +} + +int wc_SecQoriqCacheInval(void* virt, word32 sz) +{ + if (virt == NULL) { + return BAD_FUNC_ARG; + } + secCacheOp(virt, sz); + return 0; +} + +void wc_SecQoriqCpuRelax(void) +{ + /* Keep the compiler from hoisting the polled register read out of the + * loop. There is no useful wait instruction here. */ + __asm__ __volatile__("" ::: "memory"); +} + +int wc_SecQoriqGetSvr(word32* svrOut) +{ + word32 svr; + + if (svrOut == NULL) { + return BAD_FUNC_ARG; + } + + /* SVR is SPR 1023 on the e500 family. */ + __asm__ __volatile__("mfspr %0, 1023" : "=r"(svr)); + *svrOut = svr; + + return 0; +} + +#endif /* WOLFSSL_SEC_QORIQ && WOLFSSL_SEC_QORIQ_BAREMETAL */ diff --git a/wolfcrypt/src/port/nxp/sec_qoriq_cb.c b/wolfcrypt/src/port/nxp/sec_qoriq_cb.c new file mode 100644 index 00000000000..a7fd4e2a324 --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq_cb.c @@ -0,0 +1,865 @@ +/* sec_qoriq_cb.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 + */ + +/* + * Crypto callback router for the QorIQ SEC. + * + * Anything the engine cannot do returns CRYPTOCB_UNAVAILABLE so wolfCrypt + * falls back to software. That fallback is deliberately the default for + * every case that is not explicitly handled, so an unimplemented algorithm + * is a performance question rather than a correctness one. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SEC_QORIQ) && defined(WOLF_CRYPTO_CB) && \ + !defined(WOLFSSL_SEC_QORIQ_NO_CRYPTOCB) + +#include +#include +#include +#include + +#if defined(WOLFSSL_SEC_QORIQ_PKHA) || defined(WOLFSSL_SEC_QORIQ_RSA) + #ifdef WOLFSSL_SEC_QORIQ_PKHA + #include + #include + #endif + #ifdef WOLFSSL_SEC_QORIQ_RSA + #include + #ifndef NO_DH + #include + #endif + #endif + + #ifdef NO_INLINE + #include + #else + #define WOLFSSL_MISC_INCLUDED + #include + #endif +#endif + +/* The fallback promise has to cover run time failures too, not just what + * secCbCanDo() sees in advance: a non-contiguous buffer, a full ring, a job + * that times out. All mean "this engine cannot serve this call", and + * returning them verbatim would abort an operation software handles fine. + * + * AES_GCM_AUTH_E is not translated: that is the engine's answer about the + * data, not about itself, and the caller must see it. */ +static int secCbFallback(int ret) +{ + if (ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG) || + ret == WC_NO_ERR_TRACE(BUFFER_E) || + ret == WC_NO_ERR_TRACE(MEMORY_E) || + ret == WC_NO_ERR_TRACE(WC_HW_E)) { + WOLFSSL_MSG("sec_qoriq: engine declined at run time, using software"); + return CRYPTOCB_UNAVAILABLE; + } + + return ret; +} + +#if defined(WOLFSSL_SEC_QORIQ_PKHA) || defined(WOLFSSL_SEC_QORIQ_RSA) +/* Scratch for key material handed to the engine. Stack resident unless the + * build asks otherwise, and always zeroed back to memory: it was flushed to + * DRAM for DMA, so zeroing cache alone leaves the secret behind. */ +#ifdef WOLFSSL_SMALL_STACK + #define SEC_CB_DECL(name, units) byte* name = NULL + #define SEC_CB_ALLOC(name, sz) \ + do { \ + (name) = (byte*)XMALLOC((sz), NULL, DYNAMIC_TYPE_TMP_BUFFER); \ + if ((name) == NULL) { \ + return MEMORY_E; \ + } \ + XMEMSET((name), 0, (sz)); \ + } while (0) + #define SEC_CB_FREE(name, sz) \ + do { \ + wc_SecQoriqForceZeroDma((name), (sz)); \ + XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER); \ + } while (0) +#else + #define SEC_CB_DECL(name, units) byte name[units] + #define SEC_CB_ALLOC(name, sz) XMEMSET((name), 0, (sz)) + #define SEC_CB_FREE(name, sz) wc_SecQoriqForceZeroDma((name), (sz)) +#endif +#endif + +#ifndef NO_AES + +/* Anything not describable in one descriptor comes back as + * CRYPTOCB_UNAVAILABLE rather than an error, which would propagate straight + * out of wc_AesCbcEncrypt and break calls software handles fine (a buffer + * over 64 KB, or a length that is not a whole number of blocks). */ +static int secCbCanDo(word32 sz, int needBlockMultiple) +{ + if (wc_SecQoriqGetDev() == NULL) { + return 0; + } + if (sz < SEC_QORIQ_MIN_OFFLOAD_SZ || sz > SEC_QORIQ_MAX_XFER_SZ) { + return 0; + } + if (needBlockMultiple && (sz % WC_AES_BLOCK_SIZE) != 0) { + return 0; + } + return 1; +} + +/* wolfCrypt hands the callback the whole buffer for these modes, so they map + * straight onto the driver. */ +static int secCbAes(wc_CryptoInfo* info) +{ + switch (info->cipher.type) { + #ifdef HAVE_AES_CBC + case WC_CIPHER_AES_CBC: + { + Aes* aes = info->cipher.aescbc.aes; + + if (aes == NULL || !secCbCanDo(info->cipher.aescbc.sz, 1)) { + return CRYPTOCB_UNAVAILABLE; + } + if (info->cipher.enc) { + return wc_SecQoriqAesCbcEncrypt((const byte*)aes->devKey, + aes->keylen, (byte*)aes->reg, info->cipher.aescbc.in, + info->cipher.aescbc.sz, info->cipher.aescbc.out); + } + return wc_SecQoriqAesCbcDecrypt((const byte*)aes->devKey, + aes->keylen, (byte*)aes->reg, info->cipher.aescbc.in, + info->cipher.aescbc.sz, info->cipher.aescbc.out); + } + #endif + + #ifdef WOLFSSL_AES_COUNTER + case WC_CIPHER_AES_CTR: + { + Aes* aes = info->cipher.aesctr.aes; + + if (aes == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + /* wolfCrypt allows a partial trailing block and keeps the + * leftover keystream in the context; the engine works in whole + * blocks only. */ + if (aes->left != 0 || !secCbCanDo(info->cipher.aesctr.sz, 1)) { + return CRYPTOCB_UNAVAILABLE; + } + return wc_SecQoriqAesCtrEncrypt((const byte*)aes->devKey, + aes->keylen, (byte*)aes->reg, info->cipher.aesctr.in, + info->cipher.aesctr.sz, info->cipher.aesctr.out); + } + #endif + + #ifdef HAVE_AES_ECB + case WC_CIPHER_AES_ECB: + { + Aes* aes = info->cipher.aesecb.aes; + + if (aes == NULL || !secCbCanDo(info->cipher.aesecb.sz, 1)) { + return CRYPTOCB_UNAVAILABLE; + } + if (info->cipher.enc) { + return wc_SecQoriqAesEcbEncrypt((const byte*)aes->devKey, + aes->keylen, info->cipher.aesecb.in, + info->cipher.aesecb.sz, info->cipher.aesecb.out); + } + return wc_SecQoriqAesEcbDecrypt((const byte*)aes->devKey, + aes->keylen, info->cipher.aesecb.in, + info->cipher.aesecb.sz, info->cipher.aesecb.out); + } + #endif + + #ifdef HAVE_AESGCM + case WC_CIPHER_AES_GCM: + if (info->cipher.enc) { + Aes* aes = info->cipher.aesgcm_enc.aes; + + if (aes == NULL || wc_SecQoriqGetDev() == NULL || + info->cipher.aesgcm_enc.ivSz != SEC_QORIQ_GCM_IV_SZ || + info->cipher.aesgcm_enc.authTagSz != + SEC_QORIQ_GCM_TAG_SZ || + info->cipher.aesgcm_enc.sz > SEC_QORIQ_MAX_XFER_SZ || + info->cipher.aesgcm_enc.authInSz > + SEC_QORIQ_MAX_XFER_SZ) { + return CRYPTOCB_UNAVAILABLE; + } + return wc_SecQoriqAesGcmEncrypt((const byte*)aes->devKey, + aes->keylen, + info->cipher.aesgcm_enc.iv, info->cipher.aesgcm_enc.ivSz, + info->cipher.aesgcm_enc.authIn, + info->cipher.aesgcm_enc.authInSz, + info->cipher.aesgcm_enc.in, info->cipher.aesgcm_enc.sz, + info->cipher.aesgcm_enc.out, + info->cipher.aesgcm_enc.authTag, + info->cipher.aesgcm_enc.authTagSz); + } + else { + Aes* aes = info->cipher.aesgcm_dec.aes; + + if (aes == NULL || wc_SecQoriqGetDev() == NULL || + info->cipher.aesgcm_dec.ivSz != SEC_QORIQ_GCM_IV_SZ || + info->cipher.aesgcm_dec.authTagSz != + SEC_QORIQ_GCM_TAG_SZ || + info->cipher.aesgcm_dec.sz > SEC_QORIQ_MAX_XFER_SZ || + info->cipher.aesgcm_dec.authInSz > + SEC_QORIQ_MAX_XFER_SZ) { + return CRYPTOCB_UNAVAILABLE; + } + return wc_SecQoriqAesGcmDecrypt((const byte*)aes->devKey, + aes->keylen, + info->cipher.aesgcm_dec.iv, info->cipher.aesgcm_dec.ivSz, + info->cipher.aesgcm_dec.authIn, + info->cipher.aesgcm_dec.authInSz, + info->cipher.aesgcm_dec.in, info->cipher.aesgcm_dec.sz, + info->cipher.aesgcm_dec.out, + info->cipher.aesgcm_dec.authTag, + info->cipher.aesgcm_dec.authTagSz); + } + #endif + + default: + break; + } + + return CRYPTOCB_UNAVAILABLE; +} +#endif /* !NO_AES */ + +#ifdef WOLFSSL_SEC_QORIQ_PKHA + +/* Scratch layout, in units of the curve size: the private key or public + * point, plus r and s. Verify needs the most at four. */ +#define SEC_QORIQ_PK_SCRATCH_UNITS 4 + + +/* The curve id lives in one of two places. -1 matches no curve. */ +static int secCbCurveId(ecc_key* key) +{ + const ecc_set_type* dp = key->dp; + + if (dp == NULL) { + dp = wc_ecc_get_curve_params(key->idx); + } + if (dp == NULL) { + return -1; + } + + return dp->id; +} + +/* Common entry checks for the three public key operations. */ +static int secCbEccSetup(ecc_key* key, int* curveId, int* keySz) +{ + if (key == NULL || wc_SecQoriqGetDev() == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + + *curveId = secCbCurveId(key); + *keySz = wc_ecc_size(key); + if (*keySz <= 0 || *keySz > MAX_ECC_BYTES) { + return CRYPTOCB_UNAVAILABLE; + } + + if (wc_SecQoriqEccSupported(*curveId, (word32)*keySz) != 0) { + return CRYPTOCB_UNAVAILABLE; + } + + return 0; +} + +/* Export the public point as raw fixed-width x || y (no 0x04 prefix). + * + * A key loaded from a private scalar alone has no public point until + * something derives it, which wolfCrypt does lazily inside its software + * verify. The engine cannot, and handing it zeros would produce a spurious + * "bad signature", so an absent point goes back to software. */ +static int secCbExportPub(ecc_key* key, byte* out, int keySz) +{ + if (key->type == ECC_PRIVATEKEY_ONLY) { + return CRYPTOCB_UNAVAILABLE; + } + if (mp_iszero(key->pubkey.x) && mp_iszero(key->pubkey.y)) { + return CRYPTOCB_UNAVAILABLE; + } + + if (mp_to_unsigned_bin_len(key->pubkey.x, out, keySz) != MP_OKAY) { + return MP_TO_E; + } + if (mp_to_unsigned_bin_len(key->pubkey.y, out + keySz, keySz) != MP_OKAY) { + return MP_TO_E; + } + + return 0; +} + +#ifdef HAVE_ECC_SIGN +static int secCbEccSign(wc_CryptoInfo* info) +{ + ecc_key* key = info->pk.eccsign.key; + word32 hashSz; + int curveId = 0; + int keySz = 0; + int ret; + byte* priv; + byte* r; + byte* s; + mp_int mpr; + mp_int mps; + SEC_CB_DECL(buf, MAX_ECC_BYTES * SEC_QORIQ_PK_SCRATCH_UNITS); + + if (info->pk.eccsign.in == NULL || info->pk.eccsign.out == NULL || + info->pk.eccsign.outlen == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + +#if defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \ + defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT) + /* The protocol descriptor generates the per-signature nonce inside the + * engine, so RFC 6979 cannot be honoured on this path. */ + if (key != NULL && key->deterministic) { + return CRYPTOCB_UNAVAILABLE; + } +#endif + + ret = secCbEccSetup(key, &curveId, &keySz); + if (ret != 0) { + return ret; + } + + /* A digest wider than the group order is reduced the way ECDSA + * prescribes down in the PKHA layer, which declines the one shape it + * cannot express. */ + hashSz = info->pk.eccsign.inlen; + if (hashSz == 0) { + return CRYPTOCB_UNAVAILABLE; + } + + SEC_CB_ALLOC(buf, (word32)keySz * 3); + priv = buf; + r = buf + keySz; + s = buf + (keySz * 2); + + if (mp_to_unsigned_bin_len(wc_ecc_key_get_priv(key), priv, keySz) != + MP_OKAY) { + ret = MP_TO_E; + } + else { + ret = wc_SecQoriqEccSign(curveId, priv, info->pk.eccsign.in, hashSz, + r, s, (word32)keySz); + } + + /* Rebuild the ASN.1 SEQUENCE the caller expects from the raw pair. */ + if (ret == 0) { + if (mp_init(&mpr) != MP_OKAY) { + ret = MP_INIT_E; + } + else { + if (mp_init(&mps) != MP_OKAY) { + ret = MP_INIT_E; + mp_free(&mpr); + } + else { + if (mp_read_unsigned_bin(&mpr, r, (word32)keySz) != MP_OKAY || + mp_read_unsigned_bin(&mps, s, (word32)keySz) != MP_OKAY) { + ret = MP_READ_E; + } + else { + ret = StoreECC_DSA_Sig(info->pk.eccsign.out, + info->pk.eccsign.outlen, &mpr, &mps); + } + mp_free(&mpr); + mp_free(&mps); + } + } + } + + SEC_CB_FREE(buf, (word32)keySz * 3); + + return ret; +} +#endif /* HAVE_ECC_SIGN */ + +#ifdef HAVE_ECC_VERIFY +static int secCbEccVerify(wc_CryptoInfo* info) +{ + ecc_key* key = info->pk.eccverify.key; + word32 hashSz; + int curveId = 0; + int keySz = 0; + int ret; + byte* pub; + byte* r; + byte* s; + mp_int mpr; + mp_int mps; + SEC_CB_DECL(buf, MAX_ECC_BYTES * SEC_QORIQ_PK_SCRATCH_UNITS); + + if (info->pk.eccverify.sig == NULL || info->pk.eccverify.hash == NULL || + info->pk.eccverify.res == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + + ret = secCbEccSetup(key, &curveId, &keySz); + if (ret != 0) { + return ret; + } + + hashSz = info->pk.eccverify.hashlen; + if (hashSz == 0) { + return CRYPTOCB_UNAVAILABLE; + } + + /* Initialise first and use the form that expects it. The decoder clears + * both halves on its error path, and clearing an mp_int that was never + * initialised makes sp_clear() zero a garbage number of digits off the + * stack. wc_ecc_verify_hash() takes the same route for the same reason. */ + if (mp_init_multi(&mpr, &mps, NULL, NULL, NULL, NULL) != MP_OKAY) { + return CRYPTOCB_UNAVAILABLE; + } + ret = DecodeECC_DSA_Sig_Ex(info->pk.eccverify.sig, + info->pk.eccverify.siglen, &mpr, &mps, 0); + if (ret != 0) { + /* A signature this malformed is software's to reject, so that the + * caller sees exactly the error it would have without the engine. + * The decoder has already cleared both halves. */ + return CRYPTOCB_UNAVAILABLE; + } + +#ifdef WOLFSSL_SMALL_STACK + buf = (byte*)XMALLOC((word32)keySz * 4, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (buf == NULL) { + mp_free(&mpr); + mp_free(&mps); + return MEMORY_E; + } +#endif + XMEMSET(buf, 0, (word32)keySz * 4); + pub = buf; /* public data only, so no zeroization needed on the way out */ + r = buf + (keySz * 2); + s = buf + (keySz * 3); + + /* r or s wider than the curve cannot be valid and does not fit the + * fixed width block either; let software reject it. */ + if (mp_to_unsigned_bin_len(&mpr, r, keySz) != MP_OKAY || + mp_to_unsigned_bin_len(&mps, s, keySz) != MP_OKAY) { + ret = CRYPTOCB_UNAVAILABLE; + } + else { + ret = secCbExportPub(key, pub, keySz); + } + + if (ret == 0) { + ret = wc_SecQoriqEccVerify(curveId, pub, info->pk.eccverify.hash, + hashSz, r, s, (word32)keySz, info->pk.eccverify.res); + } + + mp_free(&mpr); + mp_free(&mps); +#ifdef WOLFSSL_SMALL_STACK + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} +#endif /* HAVE_ECC_VERIFY */ + +#ifdef HAVE_ECC_DHE +static int secCbEcdh(wc_CryptoInfo* info) +{ + ecc_key* privKey = info->pk.ecdh.private_key; + ecc_key* pubKey = info->pk.ecdh.public_key; + int curveId = 0; + int keySz = 0; + int ret; + byte* priv; + byte* peer; + SEC_CB_DECL(buf, MAX_ECC_BYTES * 3); + + if (pubKey == NULL || info->pk.ecdh.out == NULL || + info->pk.ecdh.outlen == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + + ret = secCbEccSetup(privKey, &curveId, &keySz); + if (ret != 0) { + return ret; + } + + /* Both keys have to be on the curve the descriptor names. */ + if (curveId != secCbCurveId(pubKey)) { + return CRYPTOCB_UNAVAILABLE; + } + + if (*info->pk.ecdh.outlen < (word32)keySz) { + return CRYPTOCB_UNAVAILABLE; + } + + SEC_CB_ALLOC(buf, (word32)keySz * 3); + priv = buf; + peer = buf + keySz; + + if (mp_to_unsigned_bin_len(wc_ecc_key_get_priv(privKey), priv, keySz) != + MP_OKAY) { + ret = MP_TO_E; + } + else { + ret = secCbExportPub(pubKey, peer, keySz); + } + + if (ret == 0) { + ret = wc_SecQoriqEcdh(curveId, priv, peer, info->pk.ecdh.out, + (word32)keySz); + } + if (ret == 0) { + *info->pk.ecdh.outlen = (word32)keySz; + } + + /* priv was flushed to memory for the engine, so zeroing cache alone + * would leave the scalar in DRAM. */ + SEC_CB_FREE(buf, (word32)keySz * 3); + + return ret; +} +#endif /* HAVE_ECC_DHE */ + +#endif /* WOLFSSL_SEC_QORIQ_PKHA */ + +#ifdef WOLFSSL_SEC_QORIQ_RSA + +/* Sized by the largest key the build admits, not the largest the engine + * takes, so an RSA-2048 build carries no 4096 bit buffer. */ +#if (RSA_MAX_SIZE / 8) < SEC_QORIQ_PKHA_MAX_RSA_BYTES + #define SEC_QORIQ_RSA_SCRATCH_BYTES (RSA_MAX_SIZE / 8) +#else + #define SEC_QORIQ_RSA_SCRATCH_BYTES SEC_QORIQ_PKHA_MAX_RSA_BYTES +#endif + +/* The callback is reached at wc_RsaFunction, the raw exponentiation with + * padding already applied or not yet stripped, so it maps straight onto the + * engine's RSA protocols. + * + * This bypasses wolfCrypt's base blinding for private key operations. That + * blinding defends the software exponentiation from timing analysis, which + * is not the code running here; a build that wants it kept can set + * WOLFSSL_SEC_QORIQ_NO_RSA. */ +static int secCbRsa(wc_CryptoInfo* info) +{ + RsaKey* key = info->pk.rsa.key; + word32 nSz; + word32 expSz; +#ifndef WOLFSSL_RSA_PUBLIC_ONLY + int isPrivate; +#endif + int ret; + byte* nbuf; + byte* xbuf; + SEC_CB_DECL(buf, SEC_QORIQ_RSA_SCRATCH_BYTES * 2); + + if (key == NULL || info->pk.rsa.in == NULL || info->pk.rsa.out == NULL || + info->pk.rsa.outLen == NULL || wc_SecQoriqGetDev() == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + + switch (info->pk.rsa.type) { + case RSA_PUBLIC_ENCRYPT: + case RSA_PUBLIC_DECRYPT: + #ifndef WOLFSSL_RSA_PUBLIC_ONLY + isPrivate = 0; + #endif + break; + case RSA_PRIVATE_ENCRYPT: + case RSA_PRIVATE_DECRYPT: + #ifdef WOLFSSL_RSA_PUBLIC_ONLY + return CRYPTOCB_UNAVAILABLE; + #else + isPrivate = 1; + break; + #endif + default: + return CRYPTOCB_UNAVAILABLE; + } + + ret = mp_unsigned_bin_size(&key->n); + if (ret <= 0) { + return CRYPTOCB_UNAVAILABLE; + } + nSz = (word32)ret; + if (nSz > SEC_QORIQ_RSA_SCRATCH_BYTES) { + return CRYPTOCB_UNAVAILABLE; + } + + /* The engine works on a whole modulus-width block. */ + if (info->pk.rsa.inLen != nSz || *info->pk.rsa.outLen < nSz) { + return CRYPTOCB_UNAVAILABLE; + } + +#ifndef WOLFSSL_RSA_PUBLIC_ONLY + ret = mp_unsigned_bin_size(isPrivate ? &key->d : &key->e); +#else + ret = mp_unsigned_bin_size(&key->e); +#endif + if (ret <= 0 || (word32)ret > nSz) { + return CRYPTOCB_UNAVAILABLE; + } + expSz = (word32)ret; + + SEC_CB_ALLOC(buf, nSz * 2); + nbuf = buf; + xbuf = buf + nSz; + + if (mp_to_unsigned_bin_len(&key->n, nbuf, (int)nSz) != MP_OKAY) { + ret = MP_TO_E; + } +#ifndef WOLFSSL_RSA_PUBLIC_ONLY + else if (isPrivate) { + if (mp_to_unsigned_bin_len(&key->d, xbuf, (int)expSz) != MP_OKAY) { + ret = MP_TO_E; + } + else { + ret = wc_SecQoriqModExp(info->pk.rsa.in, xbuf, expSz, nbuf, nSz, + info->pk.rsa.out); + } + } +#endif + else { + if (mp_to_unsigned_bin_len(&key->e, xbuf, (int)expSz) != MP_OKAY) { + ret = MP_TO_E; + } + else { + ret = wc_SecQoriqRsaPublic(info->pk.rsa.in, info->pk.rsa.inLen, + nbuf, nSz, xbuf, expSz, info->pk.rsa.out); + } + } + + if (ret == 0) { + *info->pk.rsa.outLen = nSz; + } + + SEC_CB_FREE(buf, nSz * 2); /* xbuf held the private exponent */ + + return ret; +} + +#ifndef NO_DH +/* Finite field DH is the RSA private path's exponentiation: otherPub^priv + * mod p. */ +static int secCbDh(wc_CryptoInfo* info) +{ + DhKey* key = info->pk.dh.key; + word32 pSz; + int ret; + byte* pbuf; + SEC_CB_DECL(buf, SEC_QORIQ_RSA_SCRATCH_BYTES); + + if (key == NULL || info->pk.dh.priv == NULL || + info->pk.dh.otherPub == NULL || info->pk.dh.agree == NULL || + info->pk.dh.agreeSz == NULL || wc_SecQoriqGetDev() == NULL) { + return CRYPTOCB_UNAVAILABLE; + } + + ret = mp_unsigned_bin_size(&key->p); + if (ret <= 0 || (word32)ret > SEC_QORIQ_RSA_SCRATCH_BYTES) { + return CRYPTOCB_UNAVAILABLE; + } + pSz = (word32)ret; + + /* wolfCrypt hands the peer public value at its natural length, which is + * usually but not always the full width. */ + if (info->pk.dh.pubSz != pSz || info->pk.dh.privSz == 0 || + info->pk.dh.privSz > pSz) { + return CRYPTOCB_UNAVAILABLE; + } + + SEC_CB_ALLOC(buf, pSz); + pbuf = buf; + + if (mp_to_unsigned_bin_len(&key->p, pbuf, (int)pSz) != MP_OKAY) { + ret = MP_TO_E; + } + else { + ret = wc_SecQoriqModExp(info->pk.dh.otherPub, info->pk.dh.priv, + info->pk.dh.privSz, pbuf, pSz, info->pk.dh.agree); + } + + if (ret == 0) { + /* wc_DhAgree()'s software paths return the shared secret with + * leading zero bytes stripped and agreeSz shortened to match; the + * fixed-width form is the separate wc_DhAgree_ct() contract. The + * engine always writes a whole modulus, so strip here or a TLS 1.2 + * DHE premaster secret comes out one byte long roughly one time in + * 256 and the handshake fails. */ + word32 lead = 0; + + while ((lead + 1 < pSz) && (info->pk.dh.agree[lead] == 0)) { + lead++; + } + if (lead > 0) { + XMEMMOVE(info->pk.dh.agree, info->pk.dh.agree + lead, + pSz - lead); + } + *info->pk.dh.agreeSz = pSz - lead; + } + + SEC_CB_FREE(buf, pSz); /* the group prime is public, but keep it uniform */ + + return ret; +} +#endif /* !NO_DH */ + +#endif /* WOLFSSL_SEC_QORIQ_RSA */ + +#if defined(WOLFSSL_SEC_QORIQ_PKHA) || defined(WOLFSSL_SEC_QORIQ_RSA) + +static int secCbPk(wc_CryptoInfo* info) +{ + switch (info->pk.type) { + #ifdef WOLFSSL_SEC_QORIQ_RSA + case WC_PK_TYPE_RSA: + return secCbRsa(info); + #ifndef NO_DH + case WC_PK_TYPE_DH: + return secCbDh(info); + #endif + #endif + #ifdef WOLFSSL_SEC_QORIQ_PKHA + #ifdef HAVE_ECC_SIGN + case WC_PK_TYPE_ECDSA_SIGN: + return secCbEccSign(info); + #endif + #ifdef HAVE_ECC_VERIFY + case WC_PK_TYPE_ECDSA_VERIFY: + return secCbEccVerify(info); + #endif + #ifdef HAVE_ECC_DHE + case WC_PK_TYPE_ECDH: + return secCbEcdh(info); + #endif + #endif + default: + break; + } + + /* Key generation stays in software so the caller's WC_RNG remains the + * source of the private key. */ + return CRYPTOCB_UNAVAILABLE; +} + +#endif /* WOLFSSL_SEC_QORIQ_PKHA || WOLFSSL_SEC_QORIQ_RSA */ + +/* Hashing is single shot only, but wolfCrypt calls once per Update and + * again for Final. Serving that needs either the streaming descriptor (class + * 2 context round tripped per call) or WOLFSSL_HASH_KEEP. */ +static int secCbHash(wc_CryptoInfo* info) +{ + (void)info; + return CRYPTOCB_UNAVAILABLE; +} + +static int secQoriqRouter(int devId, wc_CryptoInfo* info, void* ctx) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + int ret = CRYPTOCB_UNAVAILABLE; + + (void)devId; + (void)ctx; + + if (info == NULL) { + return BAD_FUNC_ARG; + } + + switch (info->algo_type) { + case WC_ALGO_TYPE_CIPHER: + #ifndef NO_AES + if (dev != NULL) { + dev->cbCipherCount++; + } + ret = secCbFallback(secCbAes(info)); + if (dev != NULL && ret != CRYPTOCB_UNAVAILABLE) { + dev->cbCipherOffload++; + } + #endif + break; + + case WC_ALGO_TYPE_PK: + #if defined(WOLFSSL_SEC_QORIQ_PKHA) || defined(WOLFSSL_SEC_QORIQ_RSA) + if (dev != NULL) { + dev->cbPkCount++; + } + ret = secCbFallback(secCbPk(info)); + if (dev != NULL && ret != CRYPTOCB_UNAVAILABLE) { + dev->cbPkOffload++; + } + #endif + break; + + case WC_ALGO_TYPE_HASH: + if (dev != NULL) { + dev->cbHashCount++; + } + ret = secCbHash(info); + if (dev != NULL && ret != CRYPTOCB_UNAVAILABLE) { + dev->cbHashOffload++; + } + break; + + #ifndef WC_NO_RNG + /* Seed only. WC_ALGO_TYPE_RNG is left unhandled so + * wc_RNG_GenerateBlock keeps running wolfCrypt's DRBG, seeded from + * the SEC. Answering RNG here would hand callers raw engine output + * and drop the DRBG's reseeding policy and health checks. */ + case WC_ALGO_TYPE_SEED: + if (dev != NULL) { + dev->cbSeedCount++; + } + ret = wc_SecQoriqRandomBlock(info->seed.seed, info->seed.sz); + if (ret != 0) { + /* Fall back to the platform entropy source rather than + * leave the caller with no seed. */ + WOLFSSL_MSG("sec_qoriq: seed failed, falling back"); + ret = CRYPTOCB_UNAVAILABLE; + } + break; + #endif + + default: + /* everything else falls back to software */ + break; + } + + return ret; +} + +int wc_SecQoriqRegisterCryptoCb(void) +{ + return wc_CryptoCb_RegisterDevice(WOLFSSL_SEC_QORIQ_DEVID, + secQoriqRouter, NULL); +} + +void wc_SecQoriqUnregisterCryptoCb(void) +{ + wc_CryptoCb_UnRegisterDevice(WOLFSSL_SEC_QORIQ_DEVID); +} + +#endif /* WOLFSSL_SEC_QORIQ && WOLF_CRYPTO_CB */ diff --git a/wolfcrypt/src/port/nxp/sec_qoriq_hash.c b/wolfcrypt/src/port/nxp/sec_qoriq_hash.c new file mode 100644 index 00000000000..f9c90b381b7 --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq_hash.c @@ -0,0 +1,188 @@ +/* sec_qoriq_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 + */ + +/* + * Message digests on the QorIQ SEC, driven through the MDHA. + * + * Only the single shot form is implemented here: one descriptor that + * initialises, absorbs the whole message and finalises. Streaming update or + * final needs the class 2 context loaded and stored around each call, which + * is a later addition. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_SEC_QORIQ + +#include +#include +#include + +/* Largest byte count a single FIFO LOAD can carry without the extended + * length form. */ +#define SEC_QORIQ_FIFO_MAX 0xFFFF + +/* MD5, SHA-1, SHA-224 and SHA-256 absorb 64 byte blocks; SHA-384 and + * SHA-512 absorb 128. A non final chunk must be a whole number of blocks, + * so the chunk size has to follow the algorithm rather than a fixed 64. */ +static word32 secHashBlockSz(word32 algo) +{ + switch (algo) { + case SEC_QORIQ_SHA384: + case SEC_QORIQ_SHA512: + return 128; + default: + return 64; + } +} + +int wc_SecQoriqHash(word32 algo, word32 digestSz, const byte* in, word32 inSz, + byte* out) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + SecQoriqDesc desc; + word32 offset = 0; + word32 blockSz; + int ret; + + if (out == NULL || digestSz == 0) { + return BAD_FUNC_ARG; + } + if (in == NULL && inSz > 0) { + return BAD_FUNC_ARG; + } + if (dev == NULL) { + return WC_HW_E; + } + + ret = wc_SecQoriqDescInit(&desc); + if (ret != 0) { + return ret; + } + + /* Init and final in one pass over the message. */ + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_OP | SEC_QORIQ_CLASS2 | + algo | SEC_QORIQ_ALG_INITF); + if (ret != 0) { + return ret; + } + + /* The engine reads the message straight out of memory. */ + if (inSz > 0) { + ret = wc_SecQoriqCacheFlush((void*)in, inSz); + if (ret != 0) { + return ret; + } + } + + blockSz = secHashBlockSz(algo); + + /* Feed the message in chunks a single command can describe. Only the + * last chunk carries LC2, which is what tells the MDHA to finalise. + * The loop is pre-tested so an empty message never emits a data load. */ + while (offset < inSz) { + word32 chunk = inSz - offset; + word32 cmd = SEC_QORIQ_CMD_FIFO_L | SEC_QORIQ_CLASS2 | + SEC_QORIQ_FIFOL_TYPE_MSG; + + if (chunk > SEC_QORIQ_FIFO_MAX) { + /* a non final chunk must be a whole number of blocks */ + chunk = SEC_QORIQ_FIFO_MAX - (SEC_QORIQ_FIFO_MAX % blockSz); + } + else { + cmd |= SEC_QORIQ_FIFOL_TYPE_LC2; + } + + ret = wc_SecQoriqDescAddBuf(&desc, cmd, in + offset, chunk); + if (ret != 0) { + return ret; + } + offset += chunk; + } + + /* An empty message still needs the class 2 stream terminated so the + * digest of the empty string comes out. An immediate load of zero + * length does that without referencing a buffer, which is why in may + * legitimately be NULL here. */ + if (inSz == 0) { + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_FIFO_L | + SEC_QORIQ_CLASS2 | SEC_QORIQ_FIFOL_TYPE_MSG | + SEC_QORIQ_FIFOL_TYPE_LC2 | SEC_QORIQ_CMD_IMM); + if (ret != 0) { + return ret; + } + } + + /* Pull the digest out of the class 2 context register. Routed through + * AddBuf so the address translation is checked; a hand-rolled AddPtr + * would happily append a failed translation of 0 and let the engine DMA + * the digest to physical address 0. */ + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_STORE_CTX | + SEC_QORIQ_CLASS2, out, digestSz); + if (ret != 0) { + return ret; + } + + /* Push any dirty lines covering the output buffer out of the way before + * the engine writes it. */ + ret = wc_SecQoriqCacheFlush(out, digestSz); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqRun(dev, &desc); + if (ret != 0) { + return ret; + } + + return wc_SecQoriqCacheInval(out, digestSz); +} + +int wc_SecQoriqSha1(const byte* in, word32 inSz, byte* out) +{ + return wc_SecQoriqHash(SEC_QORIQ_SHA1, 20, in, inSz, out); +} + +int wc_SecQoriqSha224(const byte* in, word32 inSz, byte* out) +{ + return wc_SecQoriqHash(SEC_QORIQ_SHA224, 28, in, inSz, out); +} + +int wc_SecQoriqSha256(const byte* in, word32 inSz, byte* out) +{ + return wc_SecQoriqHash(SEC_QORIQ_SHA256, 32, in, inSz, out); +} + +int wc_SecQoriqSha384(const byte* in, word32 inSz, byte* out) +{ + return wc_SecQoriqHash(SEC_QORIQ_SHA384, 48, in, inSz, out); +} + +int wc_SecQoriqSha512(const byte* in, word32 inSz, byte* out) +{ + return wc_SecQoriqHash(SEC_QORIQ_SHA512, 64, in, inSz, out); +} + +#endif /* WOLFSSL_SEC_QORIQ */ diff --git a/wolfcrypt/src/port/nxp/sec_qoriq_linux.c b/wolfcrypt/src/port/nxp/sec_qoriq_linux.c new file mode 100644 index 00000000000..66ed04e0faa --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq_linux.c @@ -0,0 +1,291 @@ +/* sec_qoriq_linux.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 + */ + +/* + * Linux user space backend for the QorIQ SEC driver. + * + * Maps the SEC block through /dev/mem and resolves DMA addresses with + * /proc/self/pagemap. The kernel's own caam driver must not be bound to the + * job ring this driver claims; blacklist it or unbind that ring first. + * + * NOTE: this backend has not yet been exercised on hardware. The boards on + * hand run bare metal, so sec_qoriq_baremetal.c is the tested path. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SEC_QORIQ) && defined(WOLFSSL_SEC_QORIQ_LINUX) + +#include +#include +#include + +#include +#include +#include +#include + +#ifndef SEC_QORIQ_MEM_DEV + #define SEC_QORIQ_MEM_DEV "/dev/mem" +#endif + +#ifndef SEC_QORIQ_PAGE_SZ + #define SEC_QORIQ_PAGE_SZ 4096 +#endif + +#ifndef SEC_QORIQ_CACHE_LINE + #define SEC_QORIQ_CACHE_LINE 64 +#endif + +int wc_SecQoriqMapRegs(byte** regsOut) +{ + int fd; + void* map; + + if (regsOut == NULL) { + return BAD_FUNC_ARG; + } + + fd = open(SEC_QORIQ_MEM_DEV, O_RDWR | O_SYNC); + if (fd < 0) { + WOLFSSL_MSG("sec_qoriq: cannot open " SEC_QORIQ_MEM_DEV); + return WC_HW_E; + } + + /* CCSR sits high in the physical map, so a 32-bit off_t cannot address + * it. Fail loudly instead of mapping a truncated, wrong address, which on + * a 36-bit part lands in DRAM and is refused by CONFIG_STRICT_DEVMEM. */ + if ((sizeof(off_t) < sizeof(word64)) && + ((SEC_QORIQ_CCSRBAR_PHYS + SEC_QORIQ_OFFSET) > 0x7FFFFFFFULL)) { + close(fd); + WOLFSSL_MSG("sec_qoriq: build with 64-bit off_t to map CCSR"); + return WC_HW_E; + } + + map = mmap(NULL, SEC_QORIQ_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + (off_t)(SEC_QORIQ_CCSRBAR_PHYS + SEC_QORIQ_OFFSET)); + close(fd); + + if (map == MAP_FAILED) { + WOLFSSL_MSG("sec_qoriq: cannot map the SEC block"); + return WC_HW_E; + } + + *regsOut = (byte*)map; + return 0; +} + +void wc_SecQoriqUnmapRegs(byte* regs) +{ + if (regs != NULL) { + munmap(regs, SEC_QORIQ_SIZE); + } +} + +/* Locked, page aligned pages so the physical address stays put for the life + * of the allocation. */ +void* wc_SecQoriqDmaAlloc(word32 sz, word64* physOut) +{ + void* ptr; + word64 phys; + + if (physOut == NULL || sz == 0) { + return NULL; + } + + ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0); + if (ptr == MAP_FAILED) { + return NULL; + } + + /* Fault it in before asking for the translation. */ + XMEMSET(ptr, 0, sz); + + /* MAP_LOCKED pins the pages but does not make them physically + * contiguous, and the engine gets one base address for the whole + * region, so verify the entire span rather than just the first frame. + * A ring bigger than a page is the case that needs it. */ + phys = wc_SecQoriqVirtToPhysLen(ptr, sz); + if (phys == 0) { + munmap(ptr, sz); + return NULL; + } + + *physOut = phys; + return ptr; +} + +void wc_SecQoriqDmaFree(void* virt, word64 phys, word32 sz) +{ + (void)phys; + + if (virt != NULL) { + munmap(virt, sz); + } +} + +/* Resolve one page. The engine is handed a single {physical address, + * length} pair per buffer, which is only valid while the buffer stays inside + * one physical frame, so callers must bound the length; see + * wc_SecQoriqVirtToPhysLen(). */ +static word64 secPhysOfPage(word64 vaddr) +{ + int fd; + word64 entry = 0; + word64 offset; + word64 pfn; + + fd = open("/proc/self/pagemap", O_RDONLY); + if (fd < 0) { + return 0; + } + + offset = (vaddr / SEC_QORIQ_PAGE_SZ) * (word64)sizeof(word64); + if (lseek(fd, (off_t)offset, SEEK_SET) == (off_t)-1 || + read(fd, &entry, sizeof(entry)) != (int)sizeof(entry)) { + close(fd); + return 0; + } + close(fd); + + /* bit 63 says the page is present; the frame number is bits 54:0 */ + if ((entry & (1ULL << 63)) == 0) { + return 0; + } + + pfn = entry & 0x7FFFFFFFFFFFFFULL; + if (pfn == 0) { + /* Either the mapping really is physical page 0 or, far more likely, + * the process lacks CAP_SYS_ADMIN and the kernel zeroed the PFN. + * Aiming engine DMA at physical 0 is not an acceptable guess. */ + WOLFSSL_MSG("sec_qoriq: pagemap PFN is zero, need CAP_SYS_ADMIN"); + return 0; + } + + return (pfn * SEC_QORIQ_PAGE_SZ) + (vaddr % SEC_QORIQ_PAGE_SZ); +} + +/* A buffer is only usable by the engine if it is physically contiguous. + * Ordinary user memory is virtually contiguous but not physically, so verify + * frame by frame and refuse rather than let the engine run off the end of + * the first frame into an unrelated page. */ +word64 wc_SecQoriqVirtToPhysLen(void* virt, word32 len) +{ + word64 vaddr = (word64)(wolfssl_word)virt; + word64 base = secPhysOfPage(vaddr); + word64 off; + + if (base == 0 || len == 0) { + return base; + } + + for (off = SEC_QORIQ_PAGE_SZ - (vaddr % SEC_QORIQ_PAGE_SZ); + off < (word64)len; off += SEC_QORIQ_PAGE_SZ) { + if (secPhysOfPage(vaddr + off) != base + off) { + WOLFSSL_MSG("sec_qoriq: buffer is not physically contiguous"); + return 0; + } + } + + return base; +} + +/* The register window is uncached, but everything these hooks are actually + * called with (the descriptor on the caller's stack, and the caller's key, + * IV, AAD, input, output and tag buffers) is ordinary write-back cached + * memory. dcbf is unprivileged on PowerPC, so the same maintenance the + * bare-metal backend performs works from user space. */ +static void secCacheOp(void* virt, word32 sz) +{ + wolfssl_word addr = (wolfssl_word)virt; + wolfssl_word end = addr + sz; + + addr &= ~(wolfssl_word)(SEC_QORIQ_CACHE_LINE - 1); + + __asm__ __volatile__("msync" ::: "memory"); + while (addr < end) { + __asm__ __volatile__("dcbf 0,%0" :: "r"(addr) : "memory"); + addr += SEC_QORIQ_CACHE_LINE; + } + __asm__ __volatile__("msync" ::: "memory"); +} + +int wc_SecQoriqCacheFlush(void* virt, word32 sz) +{ + if (virt == NULL) { + return BAD_FUNC_ARG; + } + secCacheOp(virt, sz); + return 0; +} + +int wc_SecQoriqCacheInval(void* virt, word32 sz) +{ + if (virt == NULL) { + return BAD_FUNC_ARG; + } + secCacheOp(virt, sz); + return 0; +} + +void wc_SecQoriqCpuRelax(void) +{ + __asm__ __volatile__("" ::: "memory"); +} + +/* SVR is a supervisor register, so read it from the device configuration + * block rather than with mfspr. */ +int wc_SecQoriqGetSvr(word32* svrOut) +{ + int fd; + volatile word32* map; + void* page; + + if (svrOut == NULL) { + return BAD_FUNC_ARG; + } + + fd = open(SEC_QORIQ_MEM_DEV, O_RDONLY | O_SYNC); + if (fd < 0) { + return WC_HW_E; + } + + page = mmap(NULL, SEC_QORIQ_PAGE_SZ, PROT_READ, MAP_SHARED, fd, + (off_t)(SEC_QORIQ_CCSRBAR_PHYS + 0xE0000ULL)); + close(fd); + + if (page == MAP_FAILED) { + return WC_HW_E; + } + + map = (volatile word32*)((byte*)page + 0xA4); + *svrOut = *map; + munmap(page, SEC_QORIQ_PAGE_SZ); + + return 0; +} + +#endif /* WOLFSSL_SEC_QORIQ && WOLFSSL_SEC_QORIQ_LINUX */ diff --git a/wolfcrypt/src/port/nxp/sec_qoriq_pkha.c b/wolfcrypt/src/port/nxp/sec_qoriq_pkha.c new file mode 100644 index 00000000000..d5f14232b65 --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq_pkha.c @@ -0,0 +1,632 @@ +/* sec_qoriq_pkha.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 + */ + +/* + * Public key on the QorIQ SEC, driven through the PKHA. + * + * These are protocol descriptors: the engine reads a protocol data block + * (PDB) of parameters and pointers, and the HEADER's start index tells it to + * begin executing at the OPERATION word after the block. Every operation has + * the same shape (a size word, a list of buffers, an OPERATION), which is + * what secPkhaRun() implements once for all of them. + * + * Domain parameters are supplied explicitly; the built-in curve shortcut is + * refused by this silicon. See the note above SEC_QORIQ_PKHA_PDB_L_SHIFT in + * sec_qoriq.h. + * + * Block layouts, all fields big endian and left padded to a fixed width: + * + * ECDSA sign L|N, q, r, G(x||y), s, f, c, d, a||b + * ECDSA verify L|N, q, r, G(x||y), W(x||y), f, c, d, tmp, a||b + * ECDH L|N, q, r, W(x||y), s, z, a||b + * RSA public e|n, f, g, n, e, f_len + * RSA private d|n, g, f, n, d (key form 1) + * + * where q is the field prime, r the group order, G the base point, s a + * private scalar, W a public point, f the message representative, c and d + * the signature halves, z the shared secret and tmp a scratch block. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFSSL_SEC_QORIQ + +#include + +#if defined(WOLFSSL_SEC_QORIQ_PKHA) || defined(WOLFSSL_SEC_QORIQ_RSA) + +#ifdef WOLFSSL_SEC_QORIQ_PKHA + #include +#endif +#include +#include + +#ifdef NO_INLINE + #include +#else + #define WOLFSSL_MISC_INCLUDED + #include +#endif + +/* Longest block above is verify: nine pointers plus room for a length. */ +#define SEC_PKHA_MAX_ITEMS 10 + +/* A buffer when buf is set, otherwise the literal value in sz. */ +typedef struct SecPkhaItem { + const byte* buf; + word32 sz; + byte out; /* engine writes it, so invalidate afterwards */ +} SecPkhaItem; + +/* Assemble the block, submit it, and do cache maintenance both ways. + * Everything the engine touches is flushed first, outputs included, so no + * dirty line can later be written back over what it produced. */ +static int secPkhaRun(word32 pdbWord0, word32 op, const SecPkhaItem* items, + int count, word32* statusOut) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + SecQoriqDesc desc; + word64 phys; + int ret; + int i; + + if (dev == NULL) { + return WC_HW_E; + } + + ret = wc_SecQoriqDescInit(&desc); + if (ret == 0) { + ret = wc_SecQoriqDescAddWord(&desc, pdbWord0); + } + + for (i = 0; (ret == 0) && (i < count); i++) { + if (items[i].buf == NULL) { + ret = wc_SecQoriqDescAddWord(&desc, items[i].sz); + continue; + } + /* No length accompanies a PDB pointer, so the size only confirms + * contiguity. Refusing beats appending a failed translation of zero, + * which would have the engine touch physical address 0. */ + phys = wc_SecQoriqVirtToPhysLen((void*)items[i].buf, items[i].sz); + if (phys == 0) { + WOLFSSL_MSG("sec_qoriq: PKHA buffer not translatable"); + ret = BAD_FUNC_ARG; + break; + } + ret = wc_SecQoriqDescAddPtr(&desc, phys); + } + + if (ret == 0) { + /* Execution starts at the OPERATION; everything above is the PDB. */ + desc.startIdx = desc.idx; + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_OP | + SEC_QORIQ_PROT_UNIDI | op); + } + + for (i = 0; (ret == 0) && (i < count); i++) { + if (items[i].buf != NULL) { + ret = wc_SecQoriqCacheFlush((void*)items[i].buf, items[i].sz); + } + } + + if (ret == 0) { + ret = wc_SecQoriqRunEx(dev, &desc, statusOut); + } + + for (i = 0; i < count; i++) { + if ((items[i].buf != NULL) && items[i].out) { + (void)wc_SecQoriqCacheInval((void*)items[i].buf, items[i].sz); + } + } + + return ret; +} + +#ifdef WOLFSSL_SEC_QORIQ_PKHA + +/* The PKHA is a 1023 bit engine, so P-521 is the largest curve it takes. */ +#define SEC_QORIQ_PKHA_MAX_BYTES 128 + +/* Working set in units of the field size L: q + r + 2 G + 2 ab + f + 2 tmp. */ +#define SEC_QORIQ_PKHA_UNITS 9 + +/* Domain parameters and per-operation buffers in one contiguous block. */ +typedef struct SecPkhaCurve { + byte* q; /* field prime, L */ + byte* r; /* group order, L */ + byte* g; /* base point, 2L */ + byte* ab; /* a and b, 2L */ + byte* f; /* message rep, L */ + byte* tmp; /* verify scratch, 2L */ + word32 L; + word32 sz; +} SecPkhaCurve; + +/* Stack resident by default, bounded by the largest curve the build carries: + * 288 bytes for a P-256 only build, 594 for P-521. */ +#ifdef WOLFSSL_SMALL_STACK + #define SEC_PKHA_DECL(name) byte* name = NULL + #define SEC_PKHA_ALLOC(name, keySz) \ + do { \ + (name) = (byte*)XMALLOC((keySz) * SEC_QORIQ_PKHA_UNITS, NULL, \ + DYNAMIC_TYPE_TMP_BUFFER); \ + if ((name) == NULL) { \ + return MEMORY_E; \ + } \ + } while (0) + #define SEC_PKHA_FREE(name, sz) \ + do { \ + wc_SecQoriqForceZeroDma((name), (sz)); \ + XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER); \ + } while (0) +#else + #define SEC_PKHA_DECL(name) \ + byte name[MAX_ECC_BYTES * SEC_QORIQ_PKHA_UNITS] + #define SEC_PKHA_ALLOC(name, keySz) do { (void)(keySz); } while (0) + #define SEC_PKHA_FREE(name, sz) wc_SecQoriqForceZeroDma((name), (sz)) +#endif + +static int secPkhaHexVal(char c) +{ + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'A') && (c <= 'F')) { + return (c - 'A') + 10; + } + if ((c >= 'a') && (c <= 'f')) { + return (c - 'a') + 10; + } + + return -1; +} + +/* Convert a wolfCrypt hex parameter string to fixed width big endian, right + * aligned and zero padded. Done directly rather than via mp_read_radix() so + * no multi-precision temporary is needed: under SP math an mp_int is sized + * for the build's largest number, hundreds of stack bytes for a string. */ +static int secPkhaParam(const char* hex, byte* out, word32 len) +{ + word32 hlen; + word32 pad; + word32 i; + int v; + + if ((hex == NULL) || (out == NULL) || (len == 0)) { + return BAD_FUNC_ARG; + } + + hlen = (word32)XSTRLEN(hex); + if ((hlen == 0) || (hlen > (len * 2))) { + return BAD_FUNC_ARG; /* wider than the field it has to fit */ + } + + XMEMSET(out, 0, len); + pad = (len * 2) - hlen; + + for (i = 0; i < hlen; i++) { + word32 nib = pad + i; + + v = secPkhaHexVal(hex[i]); + if (v < 0) { + return BAD_FUNC_ARG; + } + if ((nib & 1) == 0) { + out[nib / 2] |= (byte)(v << 4); + } + else { + out[nib / 2] |= (byte)v; + } + } + + return 0; +} + +int wc_SecQoriqEccSupported(int curveId, word32 keySz) +{ + const ecc_set_type* dp; + int idx; + + if ((keySz == 0) || (keySz > SEC_QORIQ_PKHA_MAX_BYTES) || + (keySz > MAX_ECC_BYTES)) { + return NOT_COMPILED_IN; + } + + idx = wc_ecc_get_curve_idx(curveId); + if (idx < 0) { + return NOT_COMPILED_IN; + } + dp = wc_ecc_get_curve_params(idx); + if (dp == NULL) { + return NOT_COMPILED_IN; + } + + /* Every parameter goes into a fixed keySz-wide slot, keySz being the + * field size. A few curves have a group order wider than the field + * (SECP160R1/R2/K1, SECP224K1) that would not fit. Refuse here, where + * the answer becomes a software fallback. */ + if ((XSTRLEN(dp->prime) > (keySz * 2)) || + (XSTRLEN(dp->order) > (keySz * 2)) || + (XSTRLEN(dp->Gx) > (keySz * 2)) || + (XSTRLEN(dp->Gy) > (keySz * 2)) || + (XSTRLEN(dp->Af) > (keySz * 2)) || + (XSTRLEN(dp->Bf) > (keySz * 2))) { + WOLFSSL_MSG("sec_qoriq: curve parameters do not fit the fixed block"); + return NOT_COMPILED_IN; + } + + return 0; +} + +/* Lay the working block over caller storage and fill in the parameters. */ +static int secPkhaLoadCurve(int curveId, word32 keySz, byte* buf, + SecPkhaCurve* c) +{ + const ecc_set_type* dp; + int idx; + int ret; + + XMEMSET(c, 0, sizeof(SecPkhaCurve)); + + idx = wc_ecc_get_curve_idx(curveId); + if (idx < 0) { + return NOT_COMPILED_IN; + } + dp = wc_ecc_get_curve_params(idx); + if (dp == NULL) { + return NOT_COMPILED_IN; + } + + c->L = keySz; + c->sz = keySz * SEC_QORIQ_PKHA_UNITS; + + XMEMSET(buf, 0, c->sz); + c->q = buf; + c->r = c->q + keySz; + c->g = c->r + keySz; + c->ab = c->g + (keySz * 2); + c->f = c->ab + (keySz * 2); + c->tmp = c->f + keySz; + + ret = secPkhaParam(dp->prime, c->q, keySz); + if (ret == 0) { + ret = secPkhaParam(dp->order, c->r, keySz); + } + if (ret == 0) { + ret = secPkhaParam(dp->Gx, c->g, keySz); + } + if (ret == 0) { + ret = secPkhaParam(dp->Gy, c->g + keySz, keySz); + } + if (ret == 0) { + ret = secPkhaParam(dp->Af, c->ab, keySz); + } + if (ret == 0) { + ret = secPkhaParam(dp->Bf, c->ab + keySz, keySz); + } + + return ret; +} + +/* Bit length of a fixed width big endian value. */ +static word32 secPkhaBitLen(const byte* val, word32 len) +{ + word32 i; + word32 bits; + byte b; + + for (i = 0; (i < len) && (val[i] == 0); i++) { + /* skip the zero padding */ + } + if (i == len) { + return 0; + } + + bits = (len - i - 1) * 8; + for (b = val[i]; b != 0; b >>= 1) { + bits++; + } + + return bits; +} + +/* Reduce the digest to the group size as ECDSA prescribes: the leftmost + * orderBits bits when the digest is longer, otherwise a left pad with zeros. + * order is the group order, in the same fixed width block as out. + * + * Returns 1 when the caller must fall back to software, for either of two + * reasons. The representative can come out zero, which makes the ECDSA + * equation degenerate; the engine refuses that, but wolfCrypt's tests sign an + * all-zero digest, so it has to be a fallback rather than a failure. Or a + * truncation is needed and the order does not fill the block: wolfCrypt + * finishes that case with a sub-byte shift, and copying whole bytes here + * would build a different representative and so a signature its own verifier + * rejects. P-256 and P-384 have byte aligned orders and take the fast path; + * P-521 never truncates, no digest being wider than its 521 bit order. */ +static int secPkhaMsgRep(const byte* hash, word32 hashSz, const byte* order, + byte* out, word32 len) +{ + byte acc = 0; + word32 i; + word32 orderBits = secPkhaBitLen(order, len); + + if ((hashSz * 8) > orderBits) { + if (orderBits != (len * 8)) { + return 1; + } + XMEMCPY(out, hash, len); + } + else { + XMEMSET(out, 0, len - hashSz); + XMEMCPY(out + (len - hashSz), hash, hashSz); + } + + for (i = 0; i < len; i++) { + acc |= out[i]; + } + + return acc == 0; +} + +/* First PDB word for the curve protocols: field and order sizes. */ +static word32 secPkhaSizes(word32 L) +{ + return ((L & SEC_QORIQ_PKHA_PDB_L_MASK) << SEC_QORIQ_PKHA_PDB_L_SHIFT) | + (L & SEC_QORIQ_PKHA_PDB_N_MASK); +} + +/* Fill one PDB entry. */ +static void secPkhaSet(SecPkhaItem* it, const byte* buf, word32 sz, int out) +{ + it->buf = buf; + it->sz = sz; + it->out = (byte)out; +} + +int wc_SecQoriqEccSign(int curveId, const byte* priv, const byte* hash, + word32 hashSz, byte* r, byte* s, word32 keySz) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + SecPkhaCurve c; + SecPkhaItem it[SEC_PKHA_MAX_ITEMS]; + SEC_PKHA_DECL(buf); + int ret; + + if ((priv == NULL) || (hash == NULL) || (r == NULL) || (s == NULL) || + (hashSz == 0)) { + return BAD_FUNC_ARG; + } + if (dev == NULL) { + return WC_HW_E; + } + ret = wc_SecQoriqEccSupported(curveId, keySz); + if (ret != 0) { + return ret; + } + +#ifndef WC_NO_RNG + /* The nonce comes from RNG4; without a live state handle the job + * returns a CCB "RNG not instantiated" error. */ + if (dev->rngReady == 0) { + ret = wc_SecQoriqRngInit(); + if (ret != 0) { + return ret; + } + } +#else + return NOT_COMPILED_IN; +#endif + + SEC_PKHA_ALLOC(buf, keySz); + + ret = secPkhaLoadCurve(curveId, keySz, buf, &c); + if ((ret == 0) && secPkhaMsgRep(hash, hashSz, c.r, c.f, keySz)) { + ret = CRYPTOCB_UNAVAILABLE; + } + + if (ret == 0) { + secPkhaSet(&it[0], c.q, keySz, 0); + secPkhaSet(&it[1], c.r, keySz, 0); + secPkhaSet(&it[2], c.g, keySz * 2, 0); + secPkhaSet(&it[3], priv, keySz, 0); + secPkhaSet(&it[4], c.f, keySz, 0); + secPkhaSet(&it[5], r, keySz, 1); + secPkhaSet(&it[6], s, keySz, 1); + secPkhaSet(&it[7], c.ab, keySz * 2, 0); + + ret = secPkhaRun(secPkhaSizes(keySz), + SEC_QORIQ_ECDSA_SIGN | SEC_QORIQ_PKHA_ECC, it, 8, NULL); + } + + SEC_PKHA_FREE(buf, keySz * SEC_QORIQ_PKHA_UNITS); + + return ret; +} + +int wc_SecQoriqEccVerify(int curveId, const byte* pubXY, const byte* hash, + word32 hashSz, const byte* r, const byte* s, word32 keySz, int* res) +{ + SecPkhaCurve c; + SecPkhaItem it[SEC_PKHA_MAX_ITEMS]; + SEC_PKHA_DECL(buf); + word32 status = 0; + int ret; + + if ((pubXY == NULL) || (hash == NULL) || (r == NULL) || (s == NULL) || + (res == NULL) || (hashSz == 0)) { + return BAD_FUNC_ARG; + } + + *res = 0; /* fail closed: only a clean job sets this */ + + ret = wc_SecQoriqEccSupported(curveId, keySz); + if (ret != 0) { + return ret; + } + + SEC_PKHA_ALLOC(buf, keySz); + + ret = secPkhaLoadCurve(curveId, keySz, buf, &c); + if ((ret == 0) && secPkhaMsgRep(hash, hashSz, c.r, c.f, keySz)) { + ret = CRYPTOCB_UNAVAILABLE; + } + + if (ret == 0) { + secPkhaSet(&it[0], c.q, keySz, 0); + secPkhaSet(&it[1], c.r, keySz, 0); + secPkhaSet(&it[2], c.g, keySz * 2, 0); + secPkhaSet(&it[3], pubXY, keySz * 2, 0); + secPkhaSet(&it[4], c.f, keySz, 0); + secPkhaSet(&it[5], r, keySz, 0); + secPkhaSet(&it[6], s, keySz, 0); + secPkhaSet(&it[7], c.tmp, keySz * 2, 1); + secPkhaSet(&it[8], c.ab, keySz * 2, 0); + + ret = secPkhaRun(secPkhaSizes(keySz), + SEC_QORIQ_ECDSA_VERIFY | SEC_QORIQ_PKHA_ECC, it, 9, &status); + + if (ret == 0) { + *res = 1; + } + else if ((((status & SEC_QORIQ_SSRC_MASK) >> SEC_QORIQ_SSRC_SHIFT) == + SEC_QORIQ_SSRC_DECO) && + ((status & 0xFFU) == SEC_QORIQ_DECOERR_SIGVERIFY)) { + /* The engine ran the verify and rejected it: an answer, not a + * failure. The caller reads it from res. */ + WOLFSSL_MSG("sec_qoriq: ECDSA signature rejected"); + ret = 0; + } + } + + SEC_PKHA_FREE(buf, keySz * SEC_QORIQ_PKHA_UNITS); + + return ret; +} + +int wc_SecQoriqEcdh(int curveId, const byte* priv, const byte* peerXY, + byte* out, word32 keySz) +{ + SecPkhaCurve c; + SecPkhaItem it[SEC_PKHA_MAX_ITEMS]; + SEC_PKHA_DECL(buf); + int ret; + + if ((priv == NULL) || (peerXY == NULL) || (out == NULL)) { + return BAD_FUNC_ARG; + } + ret = wc_SecQoriqEccSupported(curveId, keySz); + if (ret != 0) { + return ret; + } + + SEC_PKHA_ALLOC(buf, keySz); + + ret = secPkhaLoadCurve(curveId, keySz, buf, &c); + if (ret == 0) { + /* No base point: the peer's public point stands in for it. */ + secPkhaSet(&it[0], c.q, keySz, 0); + secPkhaSet(&it[1], c.r, keySz, 0); + secPkhaSet(&it[2], peerXY, keySz * 2, 0); + secPkhaSet(&it[3], priv, keySz, 0); + secPkhaSet(&it[4], out, keySz, 1); + secPkhaSet(&it[5], c.ab, keySz * 2, 0); + + ret = secPkhaRun(secPkhaSizes(keySz), + SEC_QORIQ_ECDSA_ECDH | SEC_QORIQ_PKHA_ECC, it, 6, NULL); + } + if (ret != 0) { + wc_SecQoriqForceZeroDma(out, keySz); /* no partial secrets */ + } + + SEC_PKHA_FREE(buf, keySz * SEC_QORIQ_PKHA_UNITS); + + return ret; +} + +#endif /* WOLFSSL_SEC_QORIQ_PKHA */ + +#ifdef WOLFSSL_SEC_QORIQ_RSA + +/* RSA block word 0: exponent length at bit 12, modulus at bit 0. */ +static word32 secRsaSizes(word32 expSz, word32 nSz) +{ + return ((expSz & SEC_QORIQ_RSA_PDB_LEN_MASK) << + SEC_QORIQ_RSA_PDB_E_SHIFT) | + (nSz & SEC_QORIQ_RSA_PDB_LEN_MASK); +} + +int wc_SecQoriqRsaPublic(const byte* in, word32 inSz, const byte* n, + word32 nSz, const byte* e, word32 eSz, byte* out) +{ + SecPkhaItem it[SEC_PKHA_MAX_ITEMS]; + + if ((in == NULL) || (n == NULL) || (e == NULL) || (out == NULL)) { + return BAD_FUNC_ARG; + } + if ((inSz == 0) || (eSz == 0) || (nSz == 0) || + (nSz > SEC_QORIQ_PKHA_MAX_RSA_BYTES) || + (inSz > nSz) || (eSz > nSz)) { + return BAD_FUNC_ARG; + } + + it[0].buf = in; it[0].sz = inSz; it[0].out = 0; + it[1].buf = out; it[1].sz = nSz; it[1].out = 1; + it[2].buf = n; it[2].sz = nSz; it[2].out = 0; + it[3].buf = e; it[3].sz = eSz; it[3].out = 0; + it[4].buf = NULL; it[4].sz = inSz; it[4].out = 0; /* f_len, inline */ + + return secPkhaRun(secRsaSizes(eSz, nSz), SEC_QORIQ_RSA_ENCRYPT, it, 5, + NULL); +} + +int wc_SecQoriqModExp(const byte* in, const byte* d, word32 dSz, + const byte* n, word32 nSz, byte* out) +{ + SecPkhaItem it[SEC_PKHA_MAX_ITEMS]; + + if ((in == NULL) || (d == NULL) || (n == NULL) || (out == NULL)) { + return BAD_FUNC_ARG; + } + if ((dSz == 0) || (nSz == 0) || (nSz > SEC_QORIQ_PKHA_MAX_RSA_BYTES) || + (dSz > nSz)) { + return BAD_FUNC_ARG; + } + + it[0].buf = in; it[0].sz = nSz; it[0].out = 0; + it[1].buf = out; it[1].sz = nSz; it[1].out = 1; + it[2].buf = n; it[2].sz = nSz; it[2].out = 0; + it[3].buf = d; it[3].sz = dSz; it[3].out = 0; + + /* Deliberately no zeroization of out on failure: wolfCrypt calls + * wc_RsaFunction in place, so out aliases in at every call site, and + * wiping it would destroy the ciphertext the software fallback needs. */ + return secPkhaRun(secRsaSizes(dSz, nSz), + SEC_QORIQ_RSA_DECRYPT | SEC_QORIQ_RSA_PRIV_FRM_1, it, 4, NULL); +} + +#endif /* WOLFSSL_SEC_QORIQ_RSA */ + +#endif /* WOLFSSL_SEC_QORIQ_PKHA || WOLFSSL_SEC_QORIQ_RSA */ +#endif /* WOLFSSL_SEC_QORIQ */ diff --git a/wolfcrypt/src/port/nxp/sec_qoriq_rng.c b/wolfcrypt/src/port/nxp/sec_qoriq_rng.c new file mode 100644 index 00000000000..dfbb89240cd --- /dev/null +++ b/wolfcrypt/src/port/nxp/sec_qoriq_rng.c @@ -0,0 +1,211 @@ +/* sec_qoriq_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 + */ + +/* + * RNG4 on the QorIQ SEC. + * + * The block powers up with no DRBG state handle, and neither U-Boot nor + * wolfBoot instantiates one on the boards this was written against (RDSTA + * reads 0 on both T2080 and T1040), so the driver does it itself. + * + * Instantiation goes through the job ring rather than direct DECO0 access. + * Linux uses DECO0 because it must instantiate before handing rings to + * consumers; we already own a ring. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#if defined(WOLFSSL_SEC_QORIQ) && !defined(WC_NO_RNG) + +#include +#include +#include + +/* Program the TRNG sampling parameters. Program mode only, and only with no + * state handle live: changing these under one would invalidate it. */ +static void secKickTrng(SecQoriqDev* dev, word32 entDelay) +{ + word32 val; + + /* program mode */ + val = wc_SecQoriqRead(dev->regs, SEC_QORIQ_RTMCTL); + wc_SecQoriqWrite(dev->regs, SEC_QORIQ_RTMCTL, + val | SEC_QORIQ_RTMCTL_PRGM); + + /* entropy delay: system clocks per entropy sample */ + val = wc_SecQoriqRead(dev->regs, SEC_QORIQ_RTSDCTL); + val &= ~(word32)SEC_QORIQ_RTSDCTL_ENT_DLY_MASK; + val |= entDelay << SEC_QORIQ_RTSDCTL_ENT_DLY_SHIFT; + wc_SecQoriqWrite(dev->regs, SEC_QORIQ_RTSDCTL, val); + + /* the statistical checker's frequency bounds track the sample length */ + wc_SecQoriqWrite(dev->regs, SEC_QORIQ_RTFRQMIN, entDelay >> 2); + wc_SecQoriqWrite(dev->regs, SEC_QORIQ_RTFRQMAX, entDelay << 3); + + /* back to run mode */ + val = wc_SecQoriqRead(dev->regs, SEC_QORIQ_RTMCTL); + wc_SecQoriqWrite(dev->regs, SEC_QORIQ_RTMCTL, + val & ~(word32)SEC_QORIQ_RTMCTL_PRGM); +} + +/* Instantiate state handle 0, then generate the secure keys (JDKEK, TDKEK, + * TDSK). The JUMP waits for the first operation; the LOAD clears the done + * interrupt and returns the RNG to idle. */ +static int secInstantiateRng(SecQoriqDev* dev) +{ + SecQoriqDesc desc; + int ret; + + ret = wc_SecQoriqDescInit(&desc); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_OP | SEC_QORIQ_CLASS1 | + SEC_QORIQ_RNG | SEC_QORIQ_ALG_INIT); + if (ret == 0) { + /* local jump, test all conditions, target the next command */ + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_JUMP | + SEC_QORIQ_JUMP_CLASS1 | 0x01); + } + if (ret == 0) { + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_LOAD_CLRW); + } + if (ret == 0) { + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CLRW_RESET); + } + if (ret == 0) { + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_OP | + SEC_QORIQ_CLASS1 | SEC_QORIQ_RNG | SEC_QORIQ_RNG4_SK); + } + if (ret != 0) { + return ret; + } + + return wc_SecQoriqRun(dev, &desc); +} + +int wc_SecQoriqRngInit(void) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + word32 entDelay = SEC_QORIQ_RTSDCTL_ENT_DLY_MIN; + word32 scfgr; + int ret = WC_HW_E; + + if (dev == NULL) { + return WC_HW_E; + } + + /* Do not touch the TRNG parameters if a handle is already live. */ + if (wc_SecQoriqRead(dev->regs, SEC_QORIQ_RDSTA) & SEC_QORIQ_RDSTA_IF0) { + dev->rngReady = 1; + return 0; + } + + /* A failure usually means the statistical checks rejected the entropy + * at this sample length, so widen it and retry. */ + while (entDelay < SEC_QORIQ_RTSDCTL_ENT_DLY_MAX) { + secKickTrng(dev, entDelay); + + ret = secInstantiateRng(dev); + if (ret == 0) { + break; + } + + entDelay += SEC_QORIQ_RTSDCTL_ENT_DLY_STEP; + } + + if (ret != 0) { + WOLFSSL_MSG("sec_qoriq: RNG4 instantiation failed"); + return ret; + } + + if ((wc_SecQoriqRead(dev->regs, SEC_QORIQ_RDSTA) & + SEC_QORIQ_RDSTA_IF0) == 0) { + WOLFSSL_MSG("sec_qoriq: RNG4 job succeeded but no state handle"); + return WC_HW_E; + } + + /* Read back the deterministic blocks rather than re-deriving them. */ + scfgr = wc_SecQoriqRead(dev->regs, SEC_QORIQ_SCFGR); + wc_SecQoriqWrite(dev->regs, SEC_QORIQ_SCFGR, + scfgr | SEC_QORIQ_SCFGR_RDBENABLE); + + dev->rngReady = 1; + WOLFSSL_MSG("sec_qoriq: RNG4 instantiated"); + + return 0; +} + +int wc_SecQoriqRandomBlock(byte* out, word32 sz) +{ + SecQoriqDev* dev = wc_SecQoriqGetDev(); + SecQoriqDesc desc; + int ret; + + if (out == NULL || sz == 0) { + return BAD_FUNC_ARG; + } + if (dev == NULL) { + return WC_HW_E; + } + if (dev->rngReady == 0) { + ret = wc_SecQoriqRngInit(); + if (ret != 0) { + return ret; + } + } + + ret = wc_SecQoriqDescInit(&desc); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqDescAddWord(&desc, SEC_QORIQ_CMD_OP | SEC_QORIQ_CLASS1 | + SEC_QORIQ_RNG); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqDescAddBuf(&desc, SEC_QORIQ_CMD_FIFO_S | + SEC_QORIQ_FIFOS_TYPE_RNG, out, sz); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqCacheFlush(out, sz); + if (ret != 0) { + return ret; + } + + ret = wc_SecQoriqRun(dev, &desc); + if (ret != 0) { + return ret; + } + + return wc_SecQoriqCacheInval(out, sz); +} + +#endif /* WOLFSSL_SEC_QORIQ && !WC_NO_RNG */ diff --git a/wolfcrypt/src/wc_port.c b/wolfcrypt/src/wc_port.c index 71b031a4086..2bc92d7ffe5 100644 --- a/wolfcrypt/src/wc_port.c +++ b/wolfcrypt/src/wc_port.c @@ -173,6 +173,9 @@ Threading/Mutex options: #if defined(WOLFSSL_CAAM) #include #endif +#if defined(WOLFSSL_SEC_QORIQ) + #include +#endif #if defined(HAVE_ARIA) #include #endif @@ -739,6 +742,19 @@ int wolfCrypt_Init(void) } #endif +#if defined(WOLFSSL_SEC_QORIQ) && !defined(WOLFSSL_SEC_QORIQ_NO_CRYPTOCB) + /* A part without the security engine is not an error: the SEC is + * only fitted on the "E" orderable variants, and everything simply + * stays in software there. */ + ret = wc_SecQoriqInit(); + if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { + ret = 0; + } + else if (ret != 0) { + WOLFCRYPT_INIT_RAISE_BAD_STATE(); + } +#endif + #if defined(HAVE_ARIA) if ((ret = wc_AriaInit()) != 0) { WOLFCRYPT_INIT_RAISE_BAD_STATE(); @@ -869,6 +885,9 @@ int wolfCrypt_Cleanup(void) #if defined(WOLFSSL_CAAM) wc_caamFree(); #endif + #if defined(WOLFSSL_SEC_QORIQ) && !defined(WOLFSSL_SEC_QORIQ_NO_CRYPTOCB) + wc_SecQoriqFree(); + #endif #if defined(WOLFSSL_CRYPTOCELL) cc310_Free(); #endif diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index f999d4c2b52..efa09823f72 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -78805,6 +78805,9 @@ typedef struct { #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) int rsaPssVerifyCount; /* RSA-PSS verify callback invocations */ #endif +#ifndef NO_DH + int dhAgreeCount; /* DH agree callback invocations */ +#endif } myCryptoDevCtx; #ifdef WOLF_CRYPTO_CB_ONLY_RSA @@ -79765,6 +79768,28 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) WOLFSSL_MSG_EX("CryptoDevCb: Pk Type %d\n", info->pk.type); #endif + #ifndef NO_DH + if (info->pk.type == WC_PK_TYPE_DH) { + DhKey* dhKey = info->pk.dh.key; + int dhSaveDevId; + + if (dhKey == NULL) + return BAD_FUNC_ARG; + + myCtx->dhAgreeCount++; + + /* Perform the agreement in software, with the device detached so + * wc_DhAgree() does not dispatch straight back here. */ + dhSaveDevId = dhKey->devId; + dhKey->devId = INVALID_DEVID; + ret = wc_DhAgree(dhKey, info->pk.dh.agree, info->pk.dh.agreeSz, + info->pk.dh.priv, info->pk.dh.privSz, + info->pk.dh.otherPub, info->pk.dh.pubSz); + dhKey->devId = dhSaveDevId; + + return ret; + } + #endif /* !NO_DH */ #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) && \ !defined(WOLF_CRYPTO_CB_ONLY_RSA) if (info->pk.type == WC_PK_TYPE_RSA_PSS_VERIFY) { @@ -82312,6 +82337,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) #if defined(WC_RSA_PSS) && defined(WOLF_CRYPTO_CB_RSA_PAD) myCtx.rsaPssVerifyCount = 0; #endif +#ifndef NO_DH + myCtx.dhAgreeCount = 0; +#endif /* set devId to something other than INVALID_DEVID */ devId = 1; @@ -82338,6 +82366,17 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) ret = rsa_onlycb_test(&myCtx); PRIVATE_KEY_LOCK(); #endif +#ifndef NO_DH + /* Run the DH suite through the device. wc_DhAgree() validates the group + * and the peer public value before it dispatches, so what reaches the + * callback has already been checked; the counter below confirms the + * agreement really crossed the callback boundary rather than quietly + * staying in software. */ + if (ret == 0) + ret = dh_test(); + if (ret == 0 && myCtx.dhAgreeCount == 0) + ret = WC_TEST_RET_ENC_NC; +#endif #if defined(HAVE_ECC) PRIVATE_KEY_UNLOCK(); if (ret == 0) diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am index 6deb44ebc91..03f96690cc3 100644 --- a/wolfssl/wolfcrypt/include.am +++ b/wolfssl/wolfcrypt/include.am @@ -238,6 +238,12 @@ if BUILD_SE050 nobase_include_HEADERS+= wolfssl/wolfcrypt/port/nxp/se050_port.h endif +if BUILD_SEC_QORIQ +nobase_include_HEADERS+= wolfssl/wolfcrypt/port/nxp/sec_qoriq.h +else +noinst_HEADERS+= wolfssl/wolfcrypt/port/nxp/sec_qoriq.h +endif + if BUILD_TROPIC01 nobase_include_HEADERS+= wolfssl/wolfcrypt/port/tropicsquare/tropic01.h endif diff --git a/wolfssl/wolfcrypt/port/nxp/sec_qoriq.h b/wolfssl/wolfcrypt/port/nxp/sec_qoriq.h new file mode 100644 index 00000000000..f863fe380a1 --- /dev/null +++ b/wolfssl/wolfcrypt/port/nxp/sec_qoriq.h @@ -0,0 +1,733 @@ +/* sec_qoriq.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 + */ + +/* + * NXP QorIQ SEC (CAAM) hardware crypto engine, as fitted to the T-series + * PowerPC parts. Verified on T2080 (SEC 5.2) and T1040 (SEC 5.0): both + * report era 6 in CCBVID (their device trees claim 5), both place the SEC at + * CCSR + 0x300000 with four job rings, and both declare "fsl,sec-v4.0", so + * one descriptor format covers them. + * + * The SEC is only fitted on security-enabled ("E") part numbers. + * wc_SecQoriqInit() checks SVR bit 0x80000 at run time. + */ + +#ifndef WOLF_CRYPT_SEC_QORIQ_H +#define WOLF_CRYPT_SEC_QORIQ_H + +#include +#include + +#ifdef WOLFSSL_SEC_QORIQ + +/* Only the MMIO accessors byte swap, so a little endian host would get + * correct registers but wrongly ordered descriptors. Refuse rather than + * produce silently wrong crypto. */ +#ifdef WOLFSSL_SEC_QORIQ_SWAP_REGS + #error "WOLFSSL_SEC_QORIQ_SWAP_REGS is incomplete: descriptor and ring words are not swapped" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/****************************************************************************** + Build configuration + ****************************************************************************/ + +/* devId selecting the SEC through the crypto callback layer. Avoids values + * other ports claim: 7 (CAAM), 8 (SECO/ARIA), 9 (MAX3266X), 807-810 (STM32 + * SAES/DHUK, RealTek HUK). */ +#ifndef WOLFSSL_SEC_QORIQ_DEVID + #define WOLFSSL_SEC_QORIQ_DEVID 0x53454351 /* "SECQ" */ +#endif + + +/* Physical base of the CCSR window, which is board specific: the CW VPX3-152 + * U-Boot relocates it to 0xEF000000, the NXP RDBs default to 0xFE000000. */ +#ifndef SEC_QORIQ_CCSRBAR + #define SEC_QORIQ_CCSRBAR 0xFE000000UL +#endif + +/* The same window as the operating system sees it. Bare metal runs with the + * 32-bit view above, but these parts have a 36-bit physical address space and + * Linux uses the full address: CCSR on a T1040 is 0xF_FE000000, not + * 0xFE000000. Mapping the 32-bit value there hits ordinary DRAM instead, so + * the physical base is a separate 64-bit knob rather than a widened + * SEC_QORIQ_CCSRBAR, which several boards already set from a linker or board + * header as a 32-bit constant. Override per board, for example + * -DSEC_QORIQ_CCSRBAR_PHYS=0xFFE000000ULL. */ +#ifndef SEC_QORIQ_CCSRBAR_PHYS + #define SEC_QORIQ_CCSRBAR_PHYS ((word64)(SEC_QORIQ_CCSRBAR)) +#endif + +/* Offset of the SEC block within CCSR, fixed on every QorIQ part seen. */ +#ifndef SEC_QORIQ_OFFSET + #define SEC_QORIQ_OFFSET 0x300000UL +#endif +#define SEC_QORIQ_SIZE 0x10000UL + +/* Which of the four job rings to claim. */ +#ifndef SEC_QORIQ_JR_INDEX + #define SEC_QORIQ_JR_INDEX 0 +#endif + +/* Job ring pages start one page into the block, one 4 KB page each. */ +#define SEC_QORIQ_JR_OFFSET(n) (0x1000UL + ((unsigned long)(n) * 0x1000UL)) + +/* Descriptors are submitted one at a time, so a short ring suffices. */ +#ifndef SEC_QORIQ_RING_SIZE + #define SEC_QORIQ_RING_SIZE 4 +#endif + +/* Poll limit for job completion; a hung ring must not wedge the caller. */ +#ifndef SEC_QORIQ_POLL_MAX + #define SEC_QORIQ_POLL_MAX 1000000 +#endif + +/* Below this size the fixed per-job cost (about 3.5 us on a 1.2 GHz T2080: + * descriptor build, two cache flushes, ring write, MMIO kick, completion + * poll) outweighs what the engine saves for algorithms with a fast software + * path. Measured AES-CBC crossover is 64 to 256 bytes. GCM always wins on + * the engine and ignores this; hashing is not routed at all. */ +#ifndef SEC_QORIQ_MIN_OFFLOAD_SZ + #define SEC_QORIQ_MIN_OFFLOAD_SZ 256 +#endif + +/* Largest transfer one command can describe; the extended length form is + * not implemented, so anything bigger goes to software. */ +#define SEC_QORIQ_MAX_XFER_SZ 0xFFFF + +/* The only GCM IV length whose J0 construction the AESA implements. */ +#define SEC_QORIQ_GCM_IV_SZ 12 + +/* Full-length tag only: the engine does the ICV comparison itself and will + * not take a truncated tag. TLS always uses 16. */ +#define SEC_QORIQ_GCM_TAG_SZ 16 + +/* Descriptors are capped at 64 words by the hardware. */ +#define SEC_QORIQ_DESC_MAX_WORDS 64 + +/****************************************************************************** + Controller registers, relative to the start of the SEC block + ****************************************************************************/ + +#define SEC_QORIQ_MCFGR 0x0004 /* master configuration */ +#define SEC_QORIQ_SCFGR 0x000C /* security configuration */ +/* Job ring LIODN registers, two words per ring. U-Boot programs these. */ +#define SEC_QORIQ_JRLIODNR(n) (0x0010 + ((n) * 8)) +#define SEC_QORIQ_JRSTART 0x005C /* job ring start (only gates when + * SCFGR[VIRT_EN] is set) */ +#define SEC_QORIQ_DECORSR 0x0094 /* DECO request source */ +#define SEC_QORIQ_DECORR 0x009C /* DECO request */ +#define SEC_QORIQ_DECO_AVAIL 0x0120 /* DECO availability */ +#define SEC_QORIQ_DECO_RESET 0x0124 /* DECO reset */ + +/* DECORSR / DECORR bits, for direct DECO0 access. */ +#define SEC_QORIQ_DECORSR_JR0 0x00000001 +#define SEC_QORIQ_DECORSR_VALID 0x80000000 +#define SEC_QORIQ_DECORR_RQD0 0x00000001 /* request DECO0 direct access */ +#define SEC_QORIQ_DECORR_DEN0 0x00010000 /* DECO0 available */ + +/* MCFGR bits */ +#define SEC_QORIQ_MCFGR_SWRESET 0x80000000 +#define SEC_QORIQ_MCFGR_WDENABLE 0x40000000 +#define SEC_QORIQ_MCFGR_WDFAIL 0x20000000 +#define SEC_QORIQ_MCFGR_DMA_RESET 0x10000000 +#define SEC_QORIQ_MCFGR_LONG_PTR 0x00010000 /* >32-bit descriptor pointers */ + +/* SCFGR bits */ +#define SEC_QORIQ_SCFGR_VIRT_EN 0x00008000 +#define SEC_QORIQ_SCFGR_RDBENABLE 0x00000400 /* faster RNG reads */ + +/* JRSTART bits, one per ring */ +#define SEC_QORIQ_JRSTART_JR(n) (1U << (n)) + +/* Identification and capability registers (the perfmon page, +0xF00-0xFFF) */ +#define SEC_QORIQ_CTPR_MS 0x0FA8 /* compile time parameters, ms half */ +#define SEC_QORIQ_CTPR_LS 0x0FAC +#define SEC_QORIQ_CSTA 0x0FD4 /* controller status */ +#define SEC_QORIQ_RVID 0x0FE0 /* RTIC version */ +#define SEC_QORIQ_CCBVID 0x0FE4 /* CCB version, carries the SEC era */ +#define SEC_QORIQ_CHAVID_MS 0x0FE8 /* per-accelerator version */ +#define SEC_QORIQ_CHAVID_LS 0x0FEC +#define SEC_QORIQ_CHANUM_MS 0x0FF0 /* per-accelerator instance counts */ +#define SEC_QORIQ_CHANUM_LS 0x0FF4 +#define SEC_QORIQ_CAAMVID_MS 0x0FF8 +#define SEC_QORIQ_CAAMVID_LS 0x0FFC + +/* CTPR_MS bits */ +#define SEC_QORIQ_CTPR_MS_QI 0x02000000 /* QMan interface present */ +#define SEC_QORIQ_CTPR_MS_PS 0x00020000 /* >32-bit pointers supported */ +#define SEC_QORIQ_CTPR_MS_DPAA2 0x00002000 + +/* CCBVID: SEC era lives in the top byte */ +#define SEC_QORIQ_CCBVID_ERA_MASK 0xFF000000 +#define SEC_QORIQ_CCBVID_ERA_SHIFT 24 + +/* CHANUM_MS: instance counts */ +#define SEC_QORIQ_CHANUM_MS_JRNUM_SHIFT 28 /* number of job rings */ +#define SEC_QORIQ_CHANUM_MS_DECONUM_SHIFT 24 /* number of DECOs */ + +/* CHANUM_LS / CHAVID_LS share this nibble layout: CHANUM_LS gives the number + * of instances of each accelerator, CHAVID_LS gives their version. */ +#define SEC_QORIQ_CHA_AES_SHIFT 0 +#define SEC_QORIQ_CHA_DES_SHIFT 4 +#define SEC_QORIQ_CHA_ARC4_SHIFT 8 +#define SEC_QORIQ_CHA_MD_SHIFT 12 +#define SEC_QORIQ_CHA_RNG_SHIFT 16 +#define SEC_QORIQ_CHA_SNOW_SHIFT 20 +#define SEC_QORIQ_CHA_KAS_SHIFT 24 +#define SEC_QORIQ_CHA_PK_SHIFT 28 +#define SEC_QORIQ_CHA_MASK 0xF + +/****************************************************************************** + RNG4 registers, relative to the start of the SEC block + ****************************************************************************/ + +#define SEC_QORIQ_RTMCTL 0x0600 /* misc control */ +#define SEC_QORIQ_RTSCMISC 0x0604 +#define SEC_QORIQ_RTPKRRNG 0x0608 +#define SEC_QORIQ_RTPKRMAX 0x060C +#define SEC_QORIQ_RTSDCTL 0x0610 /* seed control, carries entropy delay */ +#define SEC_QORIQ_RTSBLIM 0x0614 +#define SEC_QORIQ_RTFRQMIN 0x0618 +#define SEC_QORIQ_RTFRQMAX 0x061C +#define SEC_QORIQ_RTSTATUS 0x063C +#define SEC_QORIQ_RDSTA 0x06C0 /* DRNG status */ + +/* RTMCTL bits */ +#define SEC_QORIQ_RTMCTL_PRGM 0x00010000 /* 1 = program, 0 = run */ +#define SEC_QORIQ_RTMCTL_ACC 0x00000020 /* TRNG access mode */ +#define SEC_QORIQ_RTMCTL_RESET 0x00000040 /* reset TRNG to defaults */ +#define SEC_QORIQ_RTMCTL_ERR 0x00001000 +#define SEC_QORIQ_RTMCTL_ENT_VAL 0x00000400 /* entropy ready */ + +/* RDSTA bits. IF0 clear means state handle 0 is not instantiated, which is + * how the boot loaders on both boards tested leave it. */ +#define SEC_QORIQ_RDSTA_IF0 0x00000001 +#define SEC_QORIQ_RDSTA_IF1 0x00000002 +#define SEC_QORIQ_RDSTA_PR0 0x00000010 +#define SEC_QORIQ_RDSTA_PR1 0x00000020 +#define SEC_QORIQ_RDSTA_SKVN 0x40000000 +#define SEC_QORIQ_RDSTA_SKVT 0x80000000 + +/* Entropy delay: system clocks per entropy sample, longer being better. The + * floor matches mainline Linux and NXP U-Boot. A shorter window samples the + * ring oscillator too briefly on some parts, leaving the TRNG self checks as + * the only guard against a weakly seeded handle. */ +#define SEC_QORIQ_RTSDCTL_ENT_DLY_SHIFT 16 +#define SEC_QORIQ_RTSDCTL_ENT_DLY_MASK 0xFFFF0000 +#ifndef SEC_QORIQ_RTSDCTL_ENT_DLY_MIN + #define SEC_QORIQ_RTSDCTL_ENT_DLY_MIN 3200 +#endif +#define SEC_QORIQ_RTSDCTL_ENT_DLY_MAX 12800 +#define SEC_QORIQ_RTSDCTL_ENT_DLY_STEP 400 + +/****************************************************************************** + Job ring registers, relative to the start of a job ring page + ****************************************************************************/ + +#define SEC_QORIQ_IRBA 0x0000 /* input ring base, 64-bit */ +#define SEC_QORIQ_IRBA_MS 0x0000 +#define SEC_QORIQ_IRBA_LS 0x0004 +#define SEC_QORIQ_IRS 0x000C /* input ring size */ +#define SEC_QORIQ_IRSA 0x0014 /* input ring slots available */ +#define SEC_QORIQ_IRJA 0x001C /* input ring jobs added */ +#define SEC_QORIQ_ORBA 0x0020 /* output ring base, 64-bit */ +#define SEC_QORIQ_ORBA_MS 0x0020 +#define SEC_QORIQ_ORBA_LS 0x0024 +#define SEC_QORIQ_ORS 0x002C /* output ring size */ +#define SEC_QORIQ_ORJR 0x0034 /* output ring jobs removed */ +#define SEC_QORIQ_ORSF 0x003C /* output ring slots full */ +#define SEC_QORIQ_JRSTA 0x0044 /* job ring output status */ +#define SEC_QORIQ_JRINT 0x004C /* job ring interrupt status */ +#define SEC_QORIQ_JRCFG_HI 0x0050 /* job ring configuration, ms half */ +#define SEC_QORIQ_JRCFG 0x0054 /* job ring configuration, ls half */ +#define SEC_QORIQ_IRRI 0x005C /* input ring read index */ +#define SEC_QORIQ_ORWI 0x0064 /* output ring write index */ +#define SEC_QORIQ_JRCR 0x006C /* job ring command */ + +/* JRCR bits */ +#define SEC_QORIQ_JRCR_RESET 0x00000001 + +/* JRINT bits: 0 = interrupt asserted, 1 = error, 3:2 = halt state. */ +#define SEC_QORIQ_JRINT_JRI 0x00000001 +#define SEC_QORIQ_JRINT_ERR 0x00000002 +#define SEC_QORIQ_JRINT_HALT_MASK 0x0000000C +#define SEC_QORIQ_JRINT_HALT_INPROGRESS 0x00000004 +#define SEC_QORIQ_JRINT_HALT_COMPLETE 0x00000008 + +/* Job status. The top nibble names what reported the failure, telling an + * operation the engine ran and rejected (a failed authentication or + * signature check) apart from one it could not run at all. */ +#define SEC_QORIQ_SSRC_SHIFT 28 +#define SEC_QORIQ_SSRC_MASK 0xF0000000U +#define SEC_QORIQ_SSRC_NONE 0x0 +#define SEC_QORIQ_SSRC_CCB 0x2 +#define SEC_QORIQ_SSRC_JUMP 0x3 +#define SEC_QORIQ_SSRC_DECO 0x4 +#define SEC_QORIQ_SSRC_QI 0x5 +#define SEC_QORIQ_SSRC_JR 0x6 +#define SEC_QORIQ_SSRC_JUMP_CC 0x7 + +/* For a CCB error the error id is the bottom nibble. */ +#define SEC_QORIQ_CCBERR_ERRID_MASK 0x0000000FU +#define SEC_QORIQ_CCBERR_ERRID_ICV 0x0A + +/****************************************************************************** + Descriptor commands, common to every SEC/CAAM era. + ****************************************************************************/ + +#define SEC_QORIQ_CMD_HEAD 0xB0800000 +#define SEC_QORIQ_CMD_KEY 0x00000000 +#define SEC_QORIQ_CMD_LOAD 0x10000000 +#define SEC_QORIQ_CMD_LOAD_CTX 0x10200000 +#define SEC_QORIQ_CMD_IMM 0x00800000 +#define SEC_QORIQ_CMD_FIFO_L 0x20000000 +#define SEC_QORIQ_CMD_FIFO_S 0x60000000 +#define SEC_QORIQ_CMD_STORE 0x50000000 +#define SEC_QORIQ_CMD_STORE_CTX 0x50200000 +#define SEC_QORIQ_CMD_MOVE 0x78000000 +#define SEC_QORIQ_CMD_OP 0x80000000 +#define SEC_QORIQ_CMD_JUMP 0xA0000000 +#define SEC_QORIQ_CMD_SEQI 0xF0000000 +#define SEC_QORIQ_CMD_SEQO 0xF8000000 +#define SEC_QORIQ_CMD_NWB 0x00200000 + +/* Accelerator class selection */ +#define SEC_QORIQ_CLASS1 0x02000000 /* AESA, PKHA */ +#define SEC_QORIQ_CLASS2 0x04000000 /* MDHA */ + +/* OPERATION command state and direction */ +#define SEC_QORIQ_ENC 0x00000001 +#define SEC_QORIQ_DEC 0x00000000 +#define SEC_QORIQ_ALG_UPDATE 0x00000000 +#define SEC_QORIQ_ALG_ICV 0x00000002 +#define SEC_QORIQ_ALG_INIT 0x00000004 +#define SEC_QORIQ_ALG_FINAL 0x00000008 +#define SEC_QORIQ_ALG_INITF 0x0000000C + +/* AES algorithm identifiers */ +#define SEC_QORIQ_AESCTR 0x00100000 +#define SEC_QORIQ_AESCBC 0x00100100 +#define SEC_QORIQ_AESECB 0x00100200 +#define SEC_QORIQ_AESCFB 0x00100300 +#define SEC_QORIQ_AESOFB 0x00100400 +#define SEC_QORIQ_AESCMAC 0x00100600 +#define SEC_QORIQ_AESCCM 0x00100800 +#define SEC_QORIQ_AESGCM 0x00100900 + +/* Message digest algorithm identifiers */ +#define SEC_QORIQ_MD5 0x00400000 +#define SEC_QORIQ_SHA1 0x00410000 +#define SEC_QORIQ_SHA224 0x00420000 +#define SEC_QORIQ_SHA256 0x00430000 +#define SEC_QORIQ_SHA384 0x00440000 +#define SEC_QORIQ_SHA512 0x00450000 + +/* HMAC is the same identifier with the AAI field set to 0x10 */ +#define SEC_QORIQ_HMAC_AAI 0x00000010 + +/* Context offset field used by LOAD/STORE CTX. CBC keeps its IV at offset 0 + * of the class 1 context; CTR's counter lives further in. */ +#define SEC_QORIQ_CTX_OFST_CBC 0x00000000 +#define SEC_QORIQ_CTX_OFST_CTR 0x00001000 + +/* Context sizes: the accelerator's full state width plus the 8 byte running + * message length. The truncated variants share the context width of their + * parent (SHA-224 with SHA-256, SHA-384 with SHA-512), so these do not follow + * from the digest size. */ +#define SEC_QORIQ_MD5_CTXSZ (16 + 8) +#define SEC_QORIQ_SHA1_CTXSZ (20 + 8) +#define SEC_QORIQ_SHA224_CTXSZ (32 + 8) +#define SEC_QORIQ_SHA256_CTXSZ (32 + 8) +#define SEC_QORIQ_SHA384_CTXSZ (64 + 8) +#define SEC_QORIQ_SHA512_CTXSZ (64 + 8) + +/* RNG */ +#define SEC_QORIQ_RNG 0x00500000 +#define SEC_QORIQ_ENTROPY 0x00500001 + +/* RNG4 "generate secure keys" variant, used once during instantiation to + * load the JDKEK, TDKEK and TDSK registers. */ +#define SEC_QORIQ_RNG4_SK 0x00001000 + +/* Pieces of the instantiation descriptor. The JUMP waits for the class 1 + * accelerator to finish; the LOAD writes 1 to the CLRW register, which + * clears the done interrupt and returns the RNG to idle. */ +#define SEC_QORIQ_JUMP_CLASS1 0x02000000 +#define SEC_QORIQ_LOAD_CLRW 0x10880004 +#define SEC_QORIQ_CLRW_RESET 0x00000001 + +/* FIFO LOAD input types */ +#define SEC_QORIQ_FIFOL_TYPE_MSG 0x00100000 +#define SEC_QORIQ_FIFOL_TYPE_IV 0x00200000 +#define SEC_QORIQ_FIFOL_TYPE_AAD 0x00300000 +#define SEC_QORIQ_FIFOL_TYPE_ICV 0x00380000 +#define SEC_QORIQ_FIFOL_TYPE_FC1 0x00010000 +#define SEC_QORIQ_FIFOL_TYPE_LC1 0x00020000 +#define SEC_QORIQ_FIFOL_TYPE_LC2 0x00040000 + +/* FIFO STORE output types */ +#define SEC_QORIQ_FIFOS_TYPE_MSG 0x00300000 +#define SEC_QORIQ_FIFOS_TYPE_RNG 0x00340000 +#define SEC_QORIQ_FIFOS_EXT 0x00400000 +#define SEC_QORIQ_FIFOS_CONT 0x00800000 + +/* PKHA operation identifiers */ +#define SEC_QORIQ_PKHA_OP 0x01000000 +#define SEC_QORIQ_ECDSA_KEYPAIR 0x00140000 +#define SEC_QORIQ_ECDSA_SIGN 0x00150000 +#define SEC_QORIQ_ECDSA_VERIFY 0x00160000 +#define SEC_QORIQ_ECDSA_ECDH 0x00170000 +#define SEC_QORIQ_RSA_ENCRYPT 0x00180000 +#define SEC_QORIQ_RSA_DECRYPT 0x00190000 + +/* PKHA protocol descriptor fields. These sit in the OPERATION word beside + * the operation identifier above. */ +#define SEC_QORIQ_PROT_UNIDI 0x00000000 +#define SEC_QORIQ_PKHA_ECC 0x00000002 + +/* First word of a PKHA protocol data block: field prime and group order + * sizes, in bytes. + * + * Two shortcuts the i.MX CAAM parts offer are refused here, both established + * by experiment on a T2080 (era 6, PKHA v2): + * + * - Naming a built-in curve by index instead of supplying the domain + * parameters. Every flag position in bits 25:17 crossed with every curve + * index 0..31 returns DECO error 0x82, so this port always passes the + * parameters explicitly. It costs a few descriptor words and gains any + * prime curve wolfCrypt knows rather than the handful built in. + * - The "message representative is already hashed" PROTINFO flag, refused + * with DECO error 0x81. Leaving it clear works, and the engine then takes + * the representative as given, which is what wolfCrypt passes anyway. */ +#define SEC_QORIQ_PKHA_PDB_L_SHIFT 7 +#define SEC_QORIQ_PKHA_PDB_L_MASK 0x3FF +#define SEC_QORIQ_PKHA_PDB_N_MASK 0x7F + +/* DECO error id reported when the engine ran a verify and the signature did + * not check out. Distinct from a fault, so it must not be reported as one. */ +#define SEC_QORIQ_DECOERR_SIGVERIFY 0x86 + +/****************************************************************************** + Types + ****************************************************************************/ + +/* A descriptor under construction; the header is patched on submit. */ +typedef struct SecQoriqDesc { + word32 desc[SEC_QORIQ_DESC_MAX_WORDS]; + word32 idx; /* next free word */ + word32 startIdx; /* HEADER START INDEX: the word the engine begins + * executing at. 1 for a plain job descriptor, and the + * index of the OPERATION for a protocol descriptor, + * whose parameter block precedes it. */ +} SecQoriqDesc; + +/* Everything the driver needs to talk to one job ring. + * + * Threading: a process-wide singleton. Job submission is serialised on the + * wolfSSL hardware mutex inside wc_SecQoriqRun(); the rest is deliberately + * weaker: + * + * - lastStatus and the cb* counters are diagnostics, written under no lock. + * lastStatus belongs to whichever job finished last, not the caller's. + * - rngReady and the RNG4 programming behind it are not serialised. Call + * wc_SecQoriqRngInit() once at start-up rather than relying on the lazy + * path in wc_SecQoriqRandomBlock(). + * - wc_SecQoriqInit()/Free() take no lock and do not wait for in-flight + * work. Quiesce every user before tearing the device down, or the + * register window is unmapped under a running job. */ +typedef struct SecQoriqDev { + byte* regs; /* mapped base of the SEC block */ + byte* jr; /* mapped base of the claimed job ring page */ + word32 jrIndex; + word32 era; + word32 chaNumLs; /* cached CHANUM_LS, for capability checks */ + word32 chaVidLs; /* cached CHAVID_LS */ + + /* Input ring: one descriptor pointer per entry. Output ring: two words, + * the descriptor pointer then its status. word32 because the driver runs + * the SEC in 32-bit pointer mode. */ + word32* inRing; + word64 inRingPhys; + word32* outRing; + word64 outRingPhys; + + /* Mirrors of the engine's own head and tail, so submissions land on the + * slot it is about to read and results come from the one it just wrote. + * Getting this wrong does not fail loudly: the stale contents of slot 0 + * look like a completed job. */ + word32 inIdx; + word32 outIdx; + + /* Raw status of the most recent job; the decoded error loses the source + * and error-id fields. */ + word32 lastStatus; + + /* Diagnostics: how much work reached the engine, answering "is this + * really being offloaded" without guessing from timings. */ + word32 jobCount; /* descriptors actually submitted to the ring */ + word32 cbHashCount; /* WC_ALGO_TYPE_HASH seen by the router */ + word32 cbHashOffload; /* ...of those, handed to the engine */ + word32 cbCipherCount; + word32 cbCipherOffload; + word32 cbPkCount; + word32 cbPkOffload; + word32 cbSeedCount; + + byte initialized; + byte rngReady; /* RDSTA[IF0] observed or successfully instantiated */ + byte longPtr; /* MCFGR[LONG_PTR], 0 means 32-bit descriptor pointers */ +} SecQoriqDev; + +/****************************************************************************** + Core API + ****************************************************************************/ + +/* Bring up the SEC and register the crypto callback under + * WOLFSSL_SEC_QORIQ_DEVID. Returns 0 on success. */ +WOLFSSL_API int wc_SecQoriqInit(void); + +/* Tear down and unregister. */ +WOLFSSL_API int wc_SecQoriqFree(void); + +/* Raw register access. Exposed because the RNG4 programming sequence is not + * expressible as a descriptor and has to touch registers directly. */ +WOLFSSL_LOCAL word32 wc_SecQoriqRead(const byte* base, word32 off); +WOLFSSL_LOCAL void wc_SecQoriqWrite(byte* base, word32 off, word32 val); + +/* Accessor for the singleton device, NULL before wc_SecQoriqInit(). */ +WOLFSSL_LOCAL SecQoriqDev* wc_SecQoriqGetDev(void); + +#if defined(WOLF_CRYPTO_CB) && !defined(WOLFSSL_SEC_QORIQ_NO_CRYPTOCB) +/* Called by wc_SecQoriqInit/Free; exposed so an application that manages + * registration itself can do so. */ +WOLFSSL_LOCAL int wc_SecQoriqRegisterCryptoCb(void); +WOLFSSL_LOCAL void wc_SecQoriqUnregisterCryptoCb(void); +#endif + +/* Descriptor assembly. Each returns 0 on success or a negative error. */ +WOLFSSL_LOCAL int wc_SecQoriqDescInit(SecQoriqDesc* desc); +WOLFSSL_LOCAL int wc_SecQoriqDescAddWord(SecQoriqDesc* desc, word32 in); +WOLFSSL_LOCAL int wc_SecQoriqDescAddPtr(SecQoriqDesc* desc, word64 phys); +WOLFSSL_LOCAL int wc_SecQoriqDescAddBuf(SecQoriqDesc* desc, word32 cmd, + const byte* buf, word32 bufSz); + +/* Submit a descriptor to the job ring and wait for it to retire. */ +WOLFSSL_LOCAL int wc_SecQoriqRun(SecQoriqDev* dev, SecQoriqDesc* desc); + +/* As wc_SecQoriqRun(), but also hands back the raw job status of this very + * job, captured while the lock is still held. Callers that classify the + * outcome must use this rather than SecQoriqDev.lastStatus, which belongs to + * whichever job finished last and is not written on every failure path. */ +WOLFSSL_LOCAL int wc_SecQoriqRunEx(SecQoriqDev* dev, SecQoriqDesc* desc, + word32* statusOut); + +/* Decode a job ring status word into a wolfSSL error and, when logging is on, + * a human readable reason. */ +WOLFSSL_LOCAL int wc_SecQoriqParseError(word32 status); + +/****************************************************************************** + Message digests. Single shot only for now: one descriptor initialises, + absorbs the message and finalises. Streaming needs the class 2 context + round tripped around each call and is not implemented yet. + ****************************************************************************/ + +WOLFSSL_LOCAL int wc_SecQoriqHash(word32 algo, word32 digestSz, + const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqSha1(const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqSha224(const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqSha256(const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqSha384(const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqSha512(const byte* in, word32 inSz, byte* out); + +#ifndef NO_AES +/****************************************************************************** + AES, confidentiality-only modes. Input must be a whole number of blocks; + the caller handles any partial trailing block for CTR. Where the mode has a + chaining value, iv is updated in place. + ****************************************************************************/ + +WOLFSSL_LOCAL int wc_SecQoriqAes(word32 mode, int encrypt, const byte* key, + word32 keySz, byte* iv, const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqAesCbcEncrypt(const byte* key, word32 keySz, + byte* iv, const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqAesCbcDecrypt(const byte* key, word32 keySz, + byte* iv, const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqAesCtrEncrypt(const byte* key, word32 keySz, + byte* iv, const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqAesEcbEncrypt(const byte* key, word32 keySz, + const byte* in, word32 inSz, byte* out); +WOLFSSL_LOCAL int wc_SecQoriqAesEcbDecrypt(const byte* key, word32 keySz, + const byte* in, word32 inSz, byte* out); +#endif /* !NO_AES */ + +#ifndef WC_NO_RNG +/****************************************************************************** + RNG4. The boot loaders on the boards tested leave state handle 0 + uninstantiated, so wc_SecQoriqInit() records that and this has to run + before any random data can be drawn. + ****************************************************************************/ + +WOLFSSL_LOCAL int wc_SecQoriqRngInit(void); +WOLFSSL_LOCAL int wc_SecQoriqRandomBlock(byte* out, word32 sz); +#endif /* !WC_NO_RNG */ + +#ifdef HAVE_AESGCM +/* GCM takes an arbitrary message length. On decrypt the engine checks the + * tag itself and a mismatch comes back as AES_GCM_AUTH_E. */ +WOLFSSL_LOCAL int wc_SecQoriqAesGcm(int encrypt, const byte* key, word32 keySz, + const byte* iv, word32 ivSz, const byte* aad, word32 aadSz, + const byte* in, word32 inSz, byte* out, byte* tag, word32 tagSz); +WOLFSSL_LOCAL int wc_SecQoriqAesGcmEncrypt(const byte* key, word32 keySz, + const byte* iv, word32 ivSz, const byte* aad, word32 aadSz, + const byte* in, word32 inSz, byte* out, byte* tag, word32 tagSz); +WOLFSSL_LOCAL int wc_SecQoriqAesGcmDecrypt(const byte* key, word32 keySz, + const byte* iv, word32 ivSz, const byte* aad, word32 aadSz, + const byte* in, word32 inSz, byte* out, const byte* tag, word32 tagSz); +#endif + +/****************************************************************************** + PKHA public key. + + The domain parameters are supplied explicitly in every descriptor. The + engine also offers a shortcut where a small index names one of a handful of + built in curves, but that form is refused on the parts this was written + against; see the note above SEC_QORIQ_PKHA_PDB_L_SHIFT. Passing the + parameters costs a few descriptor words and works for any prime curve + wolfCrypt carries, up to the PKHA's 1023 bit ceiling. + + Every buffer below is fixed length, big endian and zero padded on the left, + the same convention mp_to_unsigned_bin_len() produces. Public keys are the + raw x || y pair, 2 * keySz bytes, with no leading 0x04 point format byte. + ****************************************************************************/ + +/* The two PKHA users are gated independently: a build may want elliptic + * curve work in software (to keep RFC 6979 deterministic signing, say) while + * still offloading RSA, or the reverse. */ +#if !defined(WOLFSSL_SEC_QORIQ_NO_PKHA) && defined(HAVE_ECC) + #define WOLFSSL_SEC_QORIQ_PKHA +#endif +#if !defined(WOLFSSL_SEC_QORIQ_NO_RSA) && !defined(NO_RSA) + #define WOLFSSL_SEC_QORIQ_RSA +#endif + +#ifdef WOLFSSL_SEC_QORIQ_PKHA + +/* Is this curve one the port can drive? Returns 0 if so, NOT_COMPILED_IN + * otherwise. Any prime curve whose parameters wolfCrypt carries qualifies, + * up to the PKHA's 1023 bit ceiling. */ +WOLFSSL_LOCAL int wc_SecQoriqEccSupported(int curveId, word32 keySz); + +/* Sign a message representative. hash is the already hashed message, of any + * length; it is reduced to the group size the way ECDSA prescribes. r and s + * each receive keySz bytes. The engine generates the per-signature nonce + * internally from RNG4, which this brings up if it is not already running. */ +WOLFSSL_LOCAL int wc_SecQoriqEccSign(int curveId, const byte* priv, + const byte* hash, word32 hashSz, byte* r, byte* s, word32 keySz); + +/* Verify. res is set to 1 when the signature is good and 0 when it is not; + * the return value is 0 in both of those cases and negative only when the + * engine itself failed. */ +WOLFSSL_LOCAL int wc_SecQoriqEccVerify(int curveId, const byte* pubXY, + const byte* hash, word32 hashSz, const byte* r, const byte* s, + word32 keySz, int* res); + +/* ECDH. out receives the x coordinate of the shared point, keySz bytes. */ +WOLFSSL_LOCAL int wc_SecQoriqEcdh(int curveId, const byte* priv, + const byte* peerXY, byte* out, word32 keySz); + +#endif /* WOLFSSL_SEC_QORIQ_PKHA */ + +#ifdef WOLFSSL_SEC_QORIQ_RSA + +/* Largest modulus the PKHA will take, 4096 bits. */ +#define SEC_QORIQ_PKHA_MAX_RSA_BYTES 512 + +/* First PDB word of an RSA block: the exponent length in bytes at bit 12 and + * the modulus length in bytes at bit 0. Both fields are 12 bits. */ +#define SEC_QORIQ_RSA_PDB_E_SHIFT 12 +#define SEC_QORIQ_RSA_PDB_LEN_MASK 0xFFF + +/* Private key form 1, the plain d and n pair. The CRT forms need the factors + * and are not used here. */ +#define SEC_QORIQ_RSA_PRIV_FRM_1 0 + +/* out = in^e mod n, with out and n both nSz bytes. */ +WOLFSSL_LOCAL int wc_SecQoriqRsaPublic(const byte* in, word32 inSz, + const byte* n, word32 nSz, const byte* e, word32 eSz, byte* out); + +/* out = in^d mod n, with in, out and n all nSz bytes. This is the general + * modular exponentiation the engine offers, so it also covers finite field + * Diffie-Hellman once wolfCrypt grows a callback hook for it. */ +WOLFSSL_LOCAL int wc_SecQoriqModExp(const byte* in, const byte* d, word32 dSz, + const byte* n, word32 nSz, byte* out); + +#endif /* WOLFSSL_SEC_QORIQ_RSA */ + +/****************************************************************************** + Environment seam. One backend is compiled in: sec_qoriq_baremetal.c for a + flat physically addressed target, sec_qoriq_linux.c for user space on a + running kernel. + ****************************************************************************/ + +/* Map SEC_QORIQ_SIZE bytes of the SEC block. */ +WOLFSSL_LOCAL int wc_SecQoriqMapRegs(byte** regsOut); +WOLFSSL_LOCAL void wc_SecQoriqUnmapRegs(byte* regs); + +/* Allocate DMA capable memory. Must be physically contiguous, and must sit + * below 4 GB while the driver runs in 32-bit descriptor pointer mode. */ +WOLFSSL_LOCAL void* wc_SecQoriqDmaAlloc(word32 sz, word64* physOut); +WOLFSSL_LOCAL void wc_SecQoriqDmaFree(void* virt, word64 phys, word32 sz); + +/* Translate an address and confirm the whole length is physically + * contiguous. The engine gets one {address, length} pair, so a buffer + * straddling non-adjacent frames must be refused. Returns 0 if unusable. + * Every translation goes through this: there is deliberately no unchecked + * single-address form to reach for. */ +WOLFSSL_LOCAL word64 wc_SecQoriqVirtToPhysLen(void* virt, word32 len); + +/* Cache maintenance around every descriptor and data buffer. */ +WOLFSSL_LOCAL int wc_SecQoriqCacheFlush(void* virt, word32 sz); +WOLFSSL_LOCAL int wc_SecQoriqCacheInval(void* virt, word32 sz); + +/* Zero a buffer handed to the engine and push the zeros out to memory. + * ForceZero() alone is not enough for DMA memory: the flush that made the + * buffer readable wrote it back and invalidated the line, so zeroing + * afterwards only dirties cache and leaves the original bytes (a private + * scalar, say) in DRAM. */ +WOLFSSL_LOCAL void wc_SecQoriqForceZeroDma(void* buf, word32 sz); + +/* Yield briefly inside the completion poll loop. */ +WOLFSSL_LOCAL void wc_SecQoriqCpuRelax(void); + +/* Read the SVR so the caller can confirm this is a security enabled part. */ +WOLFSSL_LOCAL int wc_SecQoriqGetSvr(word32* svrOut); +#define SEC_QORIQ_SVR_E_BIT 0x00080000 + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFSSL_SEC_QORIQ */ +#endif /* WOLF_CRYPT_SEC_QORIQ_H */ diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index bdd5018a418..32b5f977c51 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -3207,6 +3207,39 @@ #define WOLFSSL_NO_CAAM_HASH #endif +/* NXP QorIQ SEC, the T-series PowerPC security engine. Shares the CAAM + * descriptor architecture but is a separate, self-contained port. */ +#ifdef WOLFSSL_SEC_QORIQ + /* The engine is normally reached through the crypto callback layer. + * A minimal build (bring-up harness, boot loader) can call the driver + * API directly and skip that layer entirely. */ + #ifndef WOLFSSL_SEC_QORIQ_NO_CRYPTOCB + #undef WOLF_CRYPTO_CB + #define WOLF_CRYPTO_CB + #endif + + /* devId must be visible to every translation unit, not just the ones + * that include the port header: wolfcrypt/test/test.c and the benchmark + * select their device from WC_USE_DEVID and never include sec_qoriq.h. + * Defining it only there left the port registered but never called. */ + #ifndef WOLFSSL_SEC_QORIQ_DEVID + #define WOLFSSL_SEC_QORIQ_DEVID 0x53454351 /* "SECQ" */ + #endif + #if !defined(WC_USE_DEVID) && !defined(WOLFSSL_SEC_QORIQ_NO_CRYPTOCB) + #define WC_USE_DEVID WOLFSSL_SEC_QORIQ_DEVID + #endif + + /* pick a backend if the build did not name one */ + #if !defined(WOLFSSL_SEC_QORIQ_BAREMETAL) && \ + !defined(WOLFSSL_SEC_QORIQ_LINUX) + #define WOLFSSL_SEC_QORIQ_BAREMETAL + #endif + #if defined(WOLFSSL_SEC_QORIQ_BAREMETAL) && \ + defined(WOLFSSL_SEC_QORIQ_LINUX) + #error "Select only one WOLFSSL_SEC_QORIQ backend" + #endif +#endif /* WOLFSSL_SEC_QORIQ */ + #ifdef WOLFSSL_CAAM /* switch for all AES type algos */ #undef WOLFSSL_CAAM_CIPHER diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 3d3faf24ac6..e4f306cbc5c 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -923,7 +923,7 @@ WOLFSSL_LOCAL void wolfSSL_RefWithMutexDec_IfEquals(wolfSSL_RefWithMutex* ref, #if defined(FREESCALE_MMCAU) || defined(WOLFSSL_MICROCHIP_PIC32MZ) || \ defined(STM32_CRYPTO) || defined(STM32_HASH) || defined(STM32_RNG) || \ defined(WOLFSSL_MAX3266X) || defined(WOLFSSL_MAX3266X_OLD) || \ - defined(WOLFSSL_RTL8735B_HUK) + defined(WOLFSSL_RTL8735B_HUK) || defined(WOLFSSL_SEC_QORIQ) #ifndef WOLFSSL_CRYPT_HW_MUTEX #define WOLFSSL_CRYPT_HW_MUTEX 1 #endif