Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions Docs/plans/2026-07-25-aes-ni.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Package C — AES-NI acceleration with mandatory portable fallback

**Branch:** `package/aes-ni`
**Base:** package B (`package/chacha-poly1305`)
**Donor:** git ref `pr-90` AES assembler in `Source/DECCiphers.pas` + `UseAESAsm`

## Goal

Accelerate AES/Rijndael on x86/x64 CPUs that support AES-NI, without ever
requiring AES-NI for correctness. Pure Pascal remains the default portable path
and always compiles.

## Cross-platform policy

| Environment | AES-NI machine code | Runtime path |
|-------------|---------------------|--------------|
| Delphi Win32/Win64 (X86ASM/X64ASM) + AES-NI CPU | Compiled | AES-NI when `UseAESAsm` |
| Same build, CPU without AES-NI | Compiled | Pure Pascal |
| PUREPASCAL / NO_ASM / FPC / non-x86 / ARM / mobile | **Not compiled** | Pure Pascal only |

Correctness never depends on AES-NI. Hardware is an optional speedup.

## Compile-time vs runtime gates

1. **Compile-time:** all AES-NI procedures (`BuildAsmKey128/192/256`,
`BuildAsmDecodeKey`, `AESEncode`, `AESDecode`) are wrapped in
`{$IF defined(X86ASM) or defined(X64ASM)}`.
2. **Runtime (init):** AES-NI key schedule only if
`UseAESAsm and TDEC_CPUSupport.AES and (Size in [16,24,32])`.
3. **Runtime (encode/decode):** use AES-NI only when instance flag
`FAESAsmActive` was set by `DoInit` (avoids mismatch if key size was
non-standard or `UseAESAsm` changed after init).

Default at unit init:

```pascal
TCipher_Rijndael.UseAESAsm :=
{$IF defined(X86ASM) or defined(X64ASM)}
TDEC_CPUSupport.AES
{$ELSE}
False
{$IFEND};
```

## Force pure Pascal

```pascal
TCipher_Rijndael.UseAESAsm := False; // before Init; also covers TCipher_AES*
```

Tests force PAS and AES-NI independently and require identical ciphertext.

## Key schedule notes

- Donor name `BuildAsmKey196` renamed to **`BuildAsmKey192`** (Intel 192-bit schedule).
- Decode schedule uses **AESIMC** (`BuildAsmDecodeKey`), reverse-ordered for sequential `AESDEC`.
- `AdditionalBufferSize` gains `+$20` so `AlignPtr32(FAdditionalBuffer)` plus the
max AES-NI schedule fits. PAS path still uses `FAdditionalBufferSize shr 1`
for encode/decode halves.
- Non-standard Rijndael key lengths (not 16/24/32) always use the PAS schedule.

## Intel reference

AES-NI instruction whitepaper:
https://www.intel.com/content/dam/develop/external/us/en/documents/aes-wp-2012-09-22-v01-165683.pdf

## Files

- `Source/DECCiphers.pas` — `UseAESAsm`, AES-NI kernels, DoInit/DoEncode/DoDecode dispatch
- `Unit Tests/Tests/TestDECAESNI.pas` — FIPS-197 ECB KATs + PAS vs AES-NI
- Relies on existing `DECCPUSupport.AES`, `AlignPtr32` (no change required)

## Out of scope

- ARM Crypto Extensions / ARMv8 AES (future)
- ChaCha / Poly1305 / GCM changes
- Auto-enable on non-Windows x86 unless DEC already defines X86ASM there

## Residual risks

- Non-16/24/32 key sizes never use AES-NI (intentional; PAS only).
- Changing `UseAESAsm` after `Init` does not switch path until re-`Init`
(`FAESAsmActive` is set at init time).
Loading