Hack@CHES 2026 - Phase 1 Bug Submissions

Team KattangalSec

View on GitHub

Bug 04 - Production ASIC Allows Software to Disable AES First-Order DPA Masking (SecAesAllowForcingMasks=1)

Legacy reference: Bug #15 in the working set


Security feature bypassed

AES DPA/SCA masking countermeasure - the pseudo-random masking PRNG used for first-order differential power analysis protection

Attack type

Type 1 - unprivileged software

Finding

The production ASIC chip-level template (chiplevel.sv.tpl) sets SecAesAllowForcingMasks=1'b1 for the ASIC / CW310 / CW340 / CW305 targets, overriding the secure parameter default of 1'b0 (set in top_earlgrey.sv:85). This enables a software-writable CSR bit (CTRL_AUX_SHADOWED.FORCE_MASKS) that forces the AES masking PRNG (Bivium stream cipher) into an all-zero lockup state, completely disabling the first-order DPA countermeasure while the AES core keeps producing correct ciphertext.

Data flow:

  1. chiplevel.sv.tpl (lines 997, 1142, 1156): .SecAesAllowForcingMasks(1'b1) - the bug
  2. top_earlgrey.sv (autogen, line 85): secure default parameter bit SecAesAllowForcingMasks = 1'b0
  3. aes_prng_masking.sv (line 17): parameter bit SecAllowForcingMasks = 0 - overridden at instantiation. Line 68 has a static lint assertion (AesSecAllowForcingMasksNonDefault) that fires when non-zero - lint only, not a synthesis gate
  4. aes_prng_masking.sv (lines 102-103):
    .allow_lockup_i(SecAllowForcingMasks & force_masks_i),
    .StrictLockupProtection(!SecAllowForcingMasks),
    

    With SecAllowForcingMasks=1 AND software setting force_masks_i=1, the PRNG enters and stays in all-zero lockup, producing constant-zero masks.

Location or code reference

New Tools

Yes - custom HWF-AFL pipeline. A minimal Verilator model replicated the exact aes_prng_masking lockup logic; the AFL input is the force_masks control bit plus PRNG seed; the property monitored is “mask output must not freeze to constant zero”. AFL mutated the seed to all-zeros while setting force_masks=1 - PRNG stays locked at zero - abort(). Crash found within seconds from a safe seed (logs/afl-fuzzer_stats, logs/afl-crash_input.bin).

AI Tools

No.

LLM

No.

LLM Details

PLACEHOLDER - to be completed (model name/version, parameters, download link or API endpoint).

Online LLM Details

PLACEHOLDER - to be completed (input/output/total token counts, verification script).

LLM Prompts

PLACEHOLDER - to be completed (complete prompt, full model response, step-by-step explanation).

Detection method

Automated detection with the HWF-AFL pipeline (property: PRNG masks must not stay constant), then a static parameter audit of every SecAesAllowForcingMasks instantiation site vs. the secure default, and finally confirmation on the actual OpenTitan RTL - aes_prng_masking_tb.sv instantiates the real module and shows mask=0x0000… constant under force_masks=1 with a zero seed (see testbench/logs/rtl-test-simulation.log).

Security impact

Any software with TL-UL write access to CTRL_AUX_SHADOWED sets FORCE_MASKS=1, zeroing the masking stream. Masked AES degenerates to effectively unmasked logic - all intermediate values become linear functions of key and plaintext, and standard CPA/DPA with a few hundred traces recovers the full AES key. The side-channel countermeasure is silently disabled.

Adversary profile

Type 1 - unprivileged software on the Ibex core with MMIO access to the AES peripheral. Fully software-driven; no physical access, no probing.

Proposed mitigation

.SecAesAllowForcingMasks(1'b0),   // production ASIC config

CVSSv3.1 score and severity

7.1 - HIGH

CVSSv3.1 Details

CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N

Located in this repository (GitHub is the cloud storage for the submission):