diff --git a/.github/workflows/build-config-matrix.yml b/.github/workflows/build-config-matrix.yml index b1d015b..619a2c8 100644 --- a/.github/workflows/build-config-matrix.yml +++ b/.github/workflows/build-config-matrix.yml @@ -40,6 +40,8 @@ jobs: modifiers: "-HAVE_AES_ECB +WOLFPSA_NO_AES_BITSLICED +WOLFSSL_AES_TOUCH_LINES" - name: aes-fast modifiers: "+WOLFPSA_NO_AES_BITSLICED +WOLFPSA_AES_FAST" + - name: aes-fast-gcm-table + modifiers: "+WOLFPSA_NO_AES_BITSLICED +WOLFPSA_AES_FAST +GCM_TABLE_4BIT" - name: aes-ctr modifiers: "-WOLFSSL_AES_COUNTER" - name: aes-cfb @@ -109,3 +111,20 @@ jobs: env: BUILD_TARGET: ${{ matrix.config.target || 'libwolfpsa.a' }} run: ./build-test/build-variant.sh "${{ matrix.config.name }}" ${{ matrix.config.modifiers }} + + psa-config-policy: + name: psa_config.h policy / wolfSSL ${{ matrix.wolfssl-ref }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + wolfssl-ref: [ master, v5.9.4-stable ] + steps: + - name: Check out wolfPSA + uses: actions/checkout@v4 + + - name: Clone sibling wolfSSL + run: git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} https://github.com/wolfSSL/wolfssl ../wolfssl + + - name: Check accepted and refused configurations + run: ./build-test/psa-config-policy.sh ../wolfssl diff --git a/build-test/psa-config-policy.sh b/build-test/psa-config-policy.sh new file mode 100755 index 0000000..f071641 --- /dev/null +++ b/build-test/psa-config-policy.sh @@ -0,0 +1,95 @@ +#!/bin/sh +# +# Check that src/psa_config.h accepts and refuses the AES and GHASH +# configurations it should, by preprocessing it against the sibling wolfSSL. +# build-test/user_settings.h is the config, so WOLFPSA_NO_AES_BITSLICED can +# take the constant-time AES core away. +# +# Usage: build-test/psa-config-policy.sh [WOLFSSL_PATH] + +set -u + +repo_root="$(cd "$(dirname "$0")/.." && pwd)" +wolfssl="${1:-${repo_root}/../wolfssl}" +cc="${CC:-cc}" +fails=0 + +# wolfcrypt/fips.h ships only with a FIPS bundle; an empty one lets the +# HAVE_FIPS cases preprocess against a plain wolfSSL tree. +stub="$(mktemp -d)" +trap 'rm -rf "${stub}"' EXIT +mkdir -p "${stub}/wolfssl/wolfcrypt" +: > "${stub}/wolfssl/wolfcrypt/fips.h" + +# check [-DFLAG ...] +# The outcome is "pass", or a fragment of the wolfPSA #error the build must +# stop on, so a failure for any other reason does not count. +check() { + want="$1" + shift + out="$(echo '#include "psa_config.h"' | "${cc}" -x c -fsyntax-only \ + -I"${repo_root}/build-test" -I"${repo_root}/src" -I"${wolfssl}" \ + -I"${stub}" \ + -DWOLFSSL_USER_SETTINGS -DHAVE_AESGCM -DHAVE_AES_ECB "$@" - 2>&1)" + rc=$? + if [ "${want}" = "pass" ]; then + [ "${rc}" -eq 0 ] && ok=1 || ok=0 + else + case "${out}" in + *"wolfPSA: ${want}"*) ok=1 ;; + *) ok=0 ;; + esac + fi + if [ "${ok}" -eq 1 ]; then + echo "ok $*" + else + echo "FAIL expected '${want}': $*" + echo "${out}" | grep -m3 "error" + fails=$((fails + 1)) + fi +} + +ghash="GHASH is not constant time" +nobs="-DWOLFPSA_NO_AES_BITSLICED" +hwaes="-DWOLF_CRYPTO_CB -DWOLF_CRYPTO_CB_ONLY_AES" +fips2="-DHAVE_FIPS -DHAVE_FIPS_VERSION=2 -DHAVE_FIPS_VERSION_MAJOR=2" +fips5="-DHAVE_FIPS -DHAVE_FIPS_VERSION=5 -DHAVE_FIPS_VERSION_MAJOR=5" +fips6="-DHAVE_FIPS -DHAVE_FIPS_VERSION=6 -DHAVE_FIPS_VERSION_MAJOR=6" + +check pass +check pass ${nobs} -DWOLFSSL_AES_TOUCH_LINES +check pass ${nobs} ${hwaes} +check "AES backend is not constant time" ${nobs} +check "AES backend is not constant time" ${nobs} -DWOLFPSA_AES_HW_BACKEND +check "${ghash}" ${nobs} ${hwaes} -DGCM_TABLE +check "${ghash}" -DGCM_SMALL +check "${ghash}" -DGCM_WORD32 +check "${ghash}" -DGCM_TABLE +check "${ghash}" -DGCM_TABLE -DWOLFPSA_GHASH_HW_BACKEND +check "${ghash}" -DGCM_TABLE_4BIT +check "${ghash}" -DAES_GCM_GMULT_NCT +check "the masked GHASH needs a 64-bit type" -DNO_64BIT +check "the Arm AES assembly" -DWOLFSSL_ARMASM +check "the PowerPC AES assembly" -DWOLFSSL_PPC64_ASM +check "the PowerPC AES assembly" -DWOLFSSL_PPC32_ASM +check "the RISC-V assembly" -DWOLFSSL_RISCV_ASM +check pass -DWOLFSSL_RISCV_ASM -DWOLFSSL_RISCV_SCALAR_CRYPTO_ASM +check pass -DWOLFSSL_RISCV_ASM -DWOLFSSL_RISCV_VECTOR_CRYPTO_ASM +check pass -DWOLFSSL_RISCV_ASM -DWOLFSSL_RISCV_SCALAR_CRYPTO_ASM -DGCM_TABLE_4BIT + +check "FIPS modules before v6" ${fips5} +check "FIPS v2 has no constant-time GHASH" ${fips2} ${nobs} ${hwaes} +check "FIPS v5 masks GHASH only under" ${fips5} ${nobs} ${hwaes} +check pass ${fips5} ${nobs} ${hwaes} -DAES_GCM_GMULT_CT +check pass ${fips6} +check pass -DWOLFPSA_AES_FAST ${fips2} +check pass -DWOLFPSA_AES_FAST ${fips5} + +# WOLFPSA_AES_FAST waives each of the refusals above. +for flag in GCM_SMALL GCM_WORD32 GCM_TABLE GCM_TABLE_4BIT AES_GCM_GMULT_NCT \ + NO_64BIT WOLFSSL_ARMASM WOLFSSL_PPC64_ASM WOLFSSL_PPC32_ASM \ + WOLFSSL_RISCV_ASM; do + check pass -DWOLFPSA_AES_FAST "-D${flag}" +done + +[ "${fails}" -eq 0 ] diff --git a/src/psa_config.h b/src/psa_config.h index a7f3fcf..1fd2df0 100644 --- a/src/psa_config.h +++ b/src/psa_config.h @@ -28,6 +28,7 @@ #define WOLFPSA_CONFIG_H #include +#include #if defined(WOLFSSL_PSA_ENGINE) && !defined(NO_AES) @@ -50,7 +51,9 @@ * outside would otherwise satisfy the policy check below on any build. */ #undef WOLFPSA_AES_HW_BACKEND -#if defined(WOLFSSL_ARMASM) || defined(WOLFSSL_RISCV_ASM) || \ +#if (defined(WOLFSSL_RISCV_ASM) && \ + (defined(WOLFSSL_RISCV_SCALAR_CRYPTO_ASM) || \ + defined(WOLFSSL_RISCV_VECTOR_CRYPTO_ASM))) || \ defined(FREESCALE_LTC) || defined(FREESCALE_MMCAU) || \ defined(WOLFSSL_SILABS_SE_ACCEL) || defined(WOLFSSL_PSOC6_CRYPTO) || \ defined(WOLFSSL_AFALG) || defined(WOLFSSL_DEVCRYPTO_AES) || \ @@ -60,11 +63,46 @@ #define WOLFPSA_AES_HW_BACKEND #endif -#if !defined(WOLFPSA_AES_FAST) && !defined(WC_AES_BITSLICED) && \ - !defined(WOLFSSL_AES_TOUCH_LINES) && !defined(WOLFPSA_AES_HW_BACKEND) +/* The RISC-V crypto extensions run all of GCM in assembly, never the C GHASH. */ +#undef WOLFPSA_GHASH_HW_BACKEND +#if defined(WOLFSSL_RISCV_ASM) && \ + (defined(WOLFSSL_RISCV_SCALAR_CRYPTO_ASM) || \ + defined(WOLFSSL_RISCV_VECTOR_CRYPTO_ASM)) + #define WOLFPSA_GHASH_HW_BACKEND +#endif + +#ifndef WOLFPSA_AES_FAST +#if defined(WOLFSSL_ARMASM) +#error "wolfPSA: the Arm AES assembly falls back to a T-table core and ignores WOLFSSL_AES_TOUCH_LINES. Define WOLFPSA_AES_FAST, without WOLFSSL_AES_TOUCH_LINES or WC_AES_BITSLICED, to accept it." +#elif defined(WOLFSSL_PPC64_ASM) || defined(WOLFSSL_PPC32_ASM) +#error "wolfPSA: the PowerPC AES assembly falls back to a T-table core and ignores WOLFSSL_AES_TOUCH_LINES. Define WOLFPSA_AES_FAST, without WOLFSSL_AES_TOUCH_LINES or WC_AES_BITSLICED, to accept it." +#elif defined(WOLFSSL_RISCV_ASM) && !defined(WOLFPSA_AES_HW_BACKEND) +#error "wolfPSA: the RISC-V assembly without the scalar or vector crypto extension uses a T-table AES and a branching GHASH. Define WOLFPSA_AES_FAST, without WOLFSSL_AES_TOUCH_LINES or WC_AES_BITSLICED, to accept it." +#elif defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) && \ + !defined(WOLFPSA_AES_HW_BACKEND) +#error "wolfPSA: FIPS modules before v6 have neither WC_AES_BITSLICED nor WOLFSSL_AES_TOUCH_LINES. Define WOLFPSA_AES_FAST, without either of them, to accept the T-table core." +#elif !defined(WC_AES_BITSLICED) && !defined(WOLFSSL_AES_TOUCH_LINES) && \ + !defined(WOLFPSA_AES_HW_BACKEND) #error "wolfPSA: AES backend is not constant time. Select WC_AES_BITSLICED or WOLFSSL_AES_TOUCH_LINES, or define WOLFPSA_AES_FAST to accept the T-table core." #endif +/* Only the 64-bit GHASH built with no GCM method set is masked; FIPS v5 masks + * it only under AES_GCM_GMULT_CT, and FIPS v2 never does. */ +#if defined(HAVE_AESGCM) && !defined(WOLFPSA_GHASH_HW_BACKEND) +#if defined(GCM_SMALL) || defined(GCM_WORD32) || defined(GCM_TABLE) || \ + defined(GCM_TABLE_4BIT) || defined(AES_GCM_GMULT_NCT) +#error "wolfPSA: GHASH is not constant time. Leave the GCM method unset and AES_GCM_GMULT_NCT undefined for the masked multiply (an LTC part without its GCM engine sets GCM_TABLE itself), or define WOLFPSA_AES_FAST, without WOLFSSL_AES_TOUCH_LINES or WC_AES_BITSLICED." +#elif !defined(WORD64_AVAILABLE) +#error "wolfPSA: the masked GHASH needs a 64-bit type, and WORD64_AVAILABLE is off (NO_64BIT, or a 16-bit CPU). Remove NO_64BIT, or define WOLFPSA_AES_FAST, without WOLFSSL_AES_TOUCH_LINES or WC_AES_BITSLICED, to accept the branching one." +#elif defined(HAVE_FIPS) && FIPS_VERSION3_LT(5,0,0) +#error "wolfPSA: FIPS v2 has no constant-time GHASH. Define WOLFPSA_AES_FAST, without WOLFSSL_AES_TOUCH_LINES or WC_AES_BITSLICED, to accept it." +#elif defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0) && \ + !defined(AES_GCM_GMULT_CT) +#error "wolfPSA: FIPS v5 masks GHASH only under AES_GCM_GMULT_CT. Define it, or define WOLFPSA_AES_FAST, without WOLFSSL_AES_TOUCH_LINES or WC_AES_BITSLICED." +#endif +#endif /* HAVE_AESGCM && !WOLFPSA_GHASH_HW_BACKEND */ +#endif /* !WOLFPSA_AES_FAST */ + #if defined(WOLFPSA_AES_FAST) && \ (defined(WC_AES_BITSLICED) || defined(WOLFSSL_AES_TOUCH_LINES)) #error "wolfPSA: WOLFPSA_AES_FAST conflicts with WC_AES_BITSLICED/WOLFSSL_AES_TOUCH_LINES" diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f607c6f..32bfe0e 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -68,8 +68,9 @@ config WOLFPSA_AES_FAST bool "Accept wolfCrypt's T-table AES core (not constant time)" help Define WOLFPSA_AES_FAST, which waives src/psa_config.h's requirement for - an AES backend with no secret-indexed table load, and leaves - WOLFSSL_AES_CONSTANT_TIME off by default. + an AES backend and a GHASH with no secret-indexed table load, and leaves + WOLFSSL_AES_CONSTANT_TIME off by default. Builds with the Arm assembly + need it, since that assembly falls back to a T-table core. The T-table core is wolfCrypt's fastest software AES, and its access pattern is key- and state-dependent. Enable this only where that timing diff --git a/zephyr/README.md b/zephyr/README.md index 2900666..29451a0 100644 --- a/zephyr/README.md +++ b/zephyr/README.md @@ -82,7 +82,15 @@ config, wolfPSA's Kconfig turns both on; a settings file must set them itself: `WOLFPSA_AES_FAST` to waive it and take wolfCrypt's faster T-table core instead. `zephyr/user_settings_example.h` selects `WOLFSSL_AES_TOUCH_LINES`: it leaves `sizeof(Aes)` at 416 bytes, where `WC_AES_BITSLICED` would grow it - to 123,296 at the default `WC_AES_BS_WORD_SIZE` of 64. + to 123,296 at the default `WC_AES_BS_WORD_SIZE` of 64. AES-GCM also needs + the masked GHASH, which the wolfSSL module builds by default; a GCM table or + `GCM_SMALL` fails the check unless `WOLFPSA_AES_FAST` is set. The Arm + symmetric assembly (`CONFIG_WOLFCRYPT_ARM_SYMMETRIC_ASM`, which + `CONFIG_WOLFCRYPT_ASM` turns on for AArch64, ARMv7-M, ARMv8-M mainline and + 32-bit Cortex-A/R) is not constant time either and needs `WOLFPSA_AES_FAST`. + That one option waives both checks and cannot be combined with + `WOLFSSL_AES_TOUCH_LINES` or `WC_AES_BITSLICED`, so a build refused only for + its GHASH gives up the constant-time AES core as well. - `WC_ALLOW_ECC_ZERO_HASH` when `HAVE_ECC` is on, because `psa_sign_hash()`/`psa_verify_hash()` must accept an all-zero digest.