Skip to content

Reject a non-constant-time GHASH and the table-based AES assembly - #34

Open
Frauschi wants to merge 1 commit into
wolfSSL:masterfrom
Frauschi:ghash-ct-check
Open

Frauschi wants to merge 1 commit into
wolfSSL:masterfrom
Frauschi:ghash-ct-check

Conversation

@Frauschi

@Frauschi Frauschi commented Oct 9, 2026

Copy link
Copy Markdown
Member

Description

src/psa_config.h refuses a build whose AES is not constant time, but it only looked at the AES block cipher core. Two non-constant-time setups passed it:

  • GHASH was never checked. Only the 64-bit GHASH that wolfCrypt builds with no GCM method set is masked. GCM_SMALL and GCM_WORD32 branch on the hash subkey, GCM_TABLE and GCM_TABLE_4BIT index tables with secret data, and so does anything built with AES_GCM_GMULT_NCT. Without WORD64_AVAILABLE (NO_64BIT, a 16-bit CPU), aes.c falls back to the branching word32 multiply even with no method set.
  • The table-based AES assembly counted as hardware. WOLFSSL_ARMASM was on the hardware backend list. Every Arm port picks its AES path at run time and falls back to T-table assembly, and WOLFSSL_AES_TOUCH_LINES has no effect on it. That is why WOLFSSL_AESNI was already left off the list. The PowerPC and base RISC-V assembly have the same problem.

This is the wolfPSA side of wolfSSL/wolfssl#11691, which made the Zephyr module's constant-time AES option cover GHASH and refuse the Arm assembly.

Changes

  • GHASH check under HAVE_AESGCM: refuses GCM_SMALL, GCM_WORD32, GCM_TABLE, GCM_TABLE_4BIT, AES_GCM_GMULT_NCT and a build without WORD64_AVAILABLE, whatever the AES backend. A hardware AES core does not imply a hardware GHASH: settings.h itself selects GCM_TABLE for an LTC part without the GCM engine. psa_config.h now includes types.h for WORD64_AVAILABLE.
  • Arm and PowerPC assembly (WOLFSSL_ARMASM, WOLFSSL_PPC64_ASM, WOLFSSL_PPC32_ASM) leave the hardware list and get their own errors.
  • RISC-V assembly stays on the list only with the scalar or vector crypto extension. The base assembly uses a T-table AES (L_AES_base_te) and a GHASH that branches on each bit. The extensions use the AES instructions plus clmul or vghsh and run all of GCM in assembly, so the C GHASH check is skipped for them.
  • FIPS: the aes.c in FIPS v2 and v5 has neither WC_AES_BITSLICED nor WOLFSSL_AES_TOUCH_LINES, so before v6 those macros no longer satisfy the check. FIPS v5 masks GHASH only under AES_GCM_GMULT_CT, and v2 never does.
  • Waiver: WOLFPSA_AES_FAST waives all of it, as it already did for the AES core. It stays the single opt-out, so a build refused only for its GHASH gives up the constant-time AES core with it; zephyr/README.md says so. Each error message names the change that fixes it.

Behaviour change

These configurations built before and now stop with a wolfPSA: error unless WOLFPSA_AES_FAST is defined:

  • AES-GCM with a GCM table or GCM_SMALL. That includes a wolfSSL autoconf build, whose AES-GCM default is the 4-bit table.
  • WOLFSSL_ARMASM, the PowerPC assembly, and RISC-V assembly without the crypto extensions. On Zephyr this is CONFIG_WOLFCRYPT_ASM on AArch64, ARMv7-M, ARMv8-M mainline and 32-bit Cortex-A/R.
  • FIPS v2 and v5 without a hardware AES backend.

On Zephyr the wolfSSL module's default GHASH is already the masked one, so a default build is unaffected.

Testing

  • New build-test/psa-config-policy.sh preprocesses psa_config.h against the sibling wolfSSL with build-test/user_settings.h. It runs 37 cases: each refused configuration must stop on its specific wolfPSA: error, and each refusal has a WOLFPSA_AES_FAST control that must pass. The FIPS cases run through an empty wolfcrypt/fips.h stub, since a plain wolfSSL tree has none. It passes against wolfSSL master and v5.9.4-stable, and removing a branch from the check makes it fail.
  • build-config-matrix.yml runs the script against both wolfSSL refs, and a new aes-fast-gcm-table lane builds the waiver with a GHASH table.
  • Host: libwolfpsa.a builds by default and with AES_FAST=1; the baseline and aes-fast-gcm-table lanes build.
  • Zephyr: the 11 wolfPSA tests pass on native_sim/native/64. On mps2/an521, psa_smoke on the module's Kconfig config builds without assembly (constant-time AES, masked GHASH), is refused with CONFIG_WOLFCRYPT_ASM=y, and builds again with CONFIG_WOLFPSA_AES_FAST=y.

psa_config.h only looked at the AES block cipher core, so two
non-constant-time configurations passed its check.

GHASH. Only the 64-bit GHASH wolfCrypt builds with no GCM method set is
masked. GCM_SMALL and GCM_WORD32 branch on the hash subkey, and
GCM_TABLE and GCM_TABLE_4BIT index tables with secret data, as does
anything built with AES_GCM_GMULT_NCT. Without WORD64_AVAILABLE
(NO_64BIT, a 16-bit CPU) aes.c falls back to the branching word32
multiply even with no method set. The check now rejects all of these
when HAVE_AESGCM is set, whatever the AES backend: a hardware AES core
does not imply a hardware GHASH (settings.h itself selects GCM_TABLE for
an LTC part without the GCM engine). psa_config.h includes types.h for
WORD64_AVAILABLE.

Assembly. WOLFSSL_ARMASM was counted as a hardware AES backend. Every
Arm port picks its AES path at run time from cpuid and falls back to
T-table assembly, the Thumb2 and WOLFSSL_ARMASM_NO_HW_CRYPTO builds
always use it, and WOLFSSL_AES_TOUCH_LINES has no effect on any of them.
That is the reason WOLFSSL_AESNI is already left out, so WOLFSSL_ARMASM
leaves the list and gets its own error. The PowerPC assembly
(WOLFSSL_PPC64_ASM, WOLFSSL_PPC32_ASM) is refused the same way: it too
selects vector AES at run time and falls back to a T-table core
(L_AES_PPC32_te). WOLFSSL_RISCV_ASM stays on the list only with the
scalar or vector crypto extension: the base RISC-V assembly uses a
T-table AES (L_AES_base_te) and a GHASH that branches on each bit, while
the extensions use the AES instructions and clmul or vghsh, and run all
of GCM in assembly, so the C GHASH check is skipped for them.

FIPS. The aes.c in FIPS v2 (WCv4-stable) and v5 (WCv5.0-RC12) has
neither WC_AES_BITSLICED nor WOLFSSL_AES_TOUCH_LINES, so before v6 those
macros no longer satisfy the check. FIPS v5 masks GHASH only under
AES_GCM_GMULT_CT, and v2 never does.

WOLFPSA_AES_FAST waives all of it, as it already did for the AES core.
It stays a single waiver, so a build refused only for its GHASH gives up
the constant-time AES core with it; the Zephyr README says so. On
Zephyr, the wolfSSL module's default GHASH is already the masked one, so
only a build that selects a GHASH table or CONFIG_WOLFCRYPT_ASM now
needs CONFIG_WOLFPSA_AES_FAST.

build-test/psa-config-policy.sh preprocesses psa_config.h against the
sibling wolfSSL and checks which configurations it accepts and which
wolfPSA error each refused one stops on, with WOLFPSA_AES_FAST as the
control for every refusal. It uses build-test/user_settings.h, so it can
take the bitsliced core away and reach the generic AES refusal, and it
covers the FIPS cases through an empty wolfcrypt/fips.h stub, since a
plain wolfSSL tree has none. The build matrix runs it against both
wolfSSL refs, and a new aes-fast-gcm-table lane builds the waiver with a
GHASH table.
@Frauschi Frauschi self-assigned this Oct 9, 2026
Copilot AI balanced review requested due to automatic review settings October 9, 2026 14:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The policy changes are internally consistent and comprehensively covered by targeted configuration tests.

0 open findings

What changed in this PR

Strengthens wolfPSA’s constant-time AES policy to reject unsafe GHASH and assembly configurations.

Changes:

  • Adds GHASH, assembly-backend, RISC-V, and FIPS policy checks.
  • Documents the expanded WOLFPSA_AES_FAST waiver.
  • Adds policy tests and CI coverage across supported wolfSSL versions.
File Description
src/​psa_config.h Enforces constant-time AES and GHASH configurations.
build-test/​psa-config-policy.sh Tests accepted, rejected, and waived configurations.
.github/​workflows/​build-config-matrix.yml Runs policy tests and adds a table-GHASH waiver build.
zephyr/​Kconfig Updates waiver guidance for GHASH and Arm assembly.
zephyr/​README.md Documents the expanded policy and trade-offs.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Frauschi

Frauschi commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

@wolfSSL-Fenrir-bot review balanced

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #34

No scan targets match the changed files in this PR. Review skipped.

@Frauschi Frauschi assigned wolfSSL-Bot and unassigned Frauschi Oct 9, 2026
@Frauschi
Frauschi requested a review from danielinux October 9, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants