i-RISCV Cryptographic Support
The i-RISCV CPU provides a number of cryptographic support.
The cryptographic implementations are tested with the NIST ACVP framework using simulation.
The testing was conducted using the ACVP Parser.
The algorithm implementations are standalone, reusable RTL modules which are connected to the respective RISC-V ISA extensions. Thus apart from the RISC-V ISA, the algorithms can be instantiated in separate use cases or even extracted from the RTL code to a separate project; each core also ships with a documented C API for driving it from Verilator or an external project.
The overarching goal with the hardware crypographic implementations are:
-
256 bits of security in general (ignoring the collision security strength of message digests)
-
availability of hardware-stored keys usable by software where software yet cannot access the key
FUSE Keys
The RTL provides a one-time-programmable (OTP) e-fuse controller holding a synthesis-time defined number of banks – three by default – each storing two 256 bit keys. Every key carries one additional guard/“blown” fuse bit: a blown key is permanently unreadable, and an atomic zeroize operation blows the selected key (or all keys) in a single step.
The fuses are structurally one-way – every write path can only ever set
bits, never clear them – and the fuse storage is the one module in this
design that does not reset on rst_n, so provisioned keys survive power
cycles and resets exactly like real e-fuse silicon. In simulation, the
array’s power-on content is preloaded from a hex file (INIT_FILE parameter
or +FUSEHEX= plusarg) instead of a programming pulse.
The keys are selectable by the caller including software for the cryptographic operations as indicated below. In this case, the caller does not have access to the key data itself, but can only instruct the cryptographic operation to use the key. Inside the hardware, the cryptographic operation fetches the key prior to performing the requested operation.
The core concept is that the caller is not able to access the key.
Creation of FUSE Keys
Two independent paths provision key material:
-
Manufacturing-time provisioning. A dedicated,
prov_enable-gated out-of-band port writes a key’s value exactly once after manufacturing and before deployment; in real siliconprov_enableis tied to a physically restricted signal (a factory-test strap or a manufacturing-only debug credential) that is permanently de-asserted afterwards, so no code path can ever provision new key material in the field. -
Hardware-generated keys. A trigger in M-mode instructs the hardware Random Number Generator to generate a bank of 256 bit keys and write them directly into the FUSE controller – targeting bank 1, which is this mechanism’s exclusive consumer. The generated bits never pass through any register or instruction result software could read; neither software / firmware, nor other parts of the hardware can interfere with this operation, nor can they access the generated bits in any form. This link is one-shot: a non-volatile “provisioned” status bit latches permanently after the single run (even across a full chip reset), so generation can never be re-triggered and OR fresh material into an already-programmed key.
For each generated key, the RNG performs the following steps individually:
-
Reseed itself with 256 bits from its Entropy Source
-
Generate a 256 bit random bit string (each key is its own independent generate call, not two halves of one longer output)
-
Write the 256 bit random bit string directly into the respective FUSE key location
SHA-2
Compliant to FIPS 180-4, the following algorithms are available:
-
SHA2-256
-
SHA2-384
-
SHA2-512
Exposed to software through the Zvknhb vector
instructions (vsha2ms.vv/vsha2ch.vv/vsha2cl.vv; SEW=32 selects
SHA-256, SEW=64 selects SHA-384/512).
SHA-3 Family
Compliant to FIPS 202, the following algorithms are available:
-
SHA3-256
-
SHA3-384
-
SHA3-512
-
SHAKE128
-
SHAKE256
The implementation is particularly small as a central Keccak mechanism is implemented where the SHA-3 family members only define the rate/capacity and the padding bit. The leancrypto’s SHA-3 C implementation is conceptual identical to the RTL implementation.
KMAC
Following SP800-185, KMAC256 and KMAC256XOF are implemented on a real, separate Keccak core instance (cSHAKE256 as the sixth algorithm of that shared core). The caller is allowed to either provide a key or to point to one of the FUSE keys; an explicit key must be 128-256 bits, enforcing SP 800-185’s own minimum-security-strength floor.
This module is not instantiated anywhere in the SoC – it exists for unit testing and external use through its documented C API; a self-test status bit is reserved for it on the self-test interface.
AES
The RTL implements the raw AES block operation for AES-256. It is equally connected to the FUSE keys: bank 0 of the FUSE controller is the AES hardware’s exclusive consumer, selected through a machine-mode CSR that names one of the two bank-0 keys and declares whether the upcoming instruction sequence encrypts or decrypts. While a FUSE key is active, every round instruction’s software-supplied round key is transparently replaced by the hardware-expanded schedule – the round keys themselves are never exposed on any port, register, or CSR.
In addition, GCM support is provided along with GMAC: one 128-bit data block (plus one optional AAD block) per call under a fixed 96-bit IV, built on a constant-time GF(2^128) GHASH multiply shared with the Zvkg extension.
Random Number Generator
The random number generator is an XDRBG-256 which uses SHAKE-256 as a core. It instantiates the Keccak Core.
The reason for choosing the XDRBG-256 is:
-
availability of Keccak
-
very lean
-
about to be standardized in NIST SP800-90A
The XDRBG-256 is seeded by the hardware entropy sources; in this design, its only instantiations are the entropy source’s own private DRBG pipelines.
The XDRBG-256 implements the following aspects:
-
A multi-staged seeding process is supported where a caller can insert multiple individual seed blocks before the XDRBG re-generates its own internal state. This support is mathematically equivalent to inserting all seed blocks concatenated as one seed block. Yet, one seed block is allowed to be at most 512 bits.
-
All call sizes are hardware-enforced: a finalizing instantiate/reseed accepts at most 2176 bits of combined seed + alpha + control byte (alpha itself at most 84 bytes), and the generate function will at most return 512 bits.
Vector Crypto Extensions
The algorithm cores above are exposed to software through the RISC-V vector-crypto instruction set, all decoded under a dedicated vector-crypto major opcode:
| Extension | Instructions | Execution unit |
|---|---|---|
Zvkb |
VANDN / VROL / VROR / VBREV8 / VREV8 / VWSLL (crypto bit-manipulation) | rides the existing vector engine – no new datapath |
Zvkned |
vaesdf / vaesdm / vaesef / vaesem / vaeskf1 / vaeskf2 / vaesz – AES-256 one round at a time, state in the vector register file | dedicated unit; every instruction commits in exactly one cycle, independent of data or key |
Zvkg |
vghsh.vv / vgmul.vv – GF(2^128) GHASH multiply for GCM/GMAC | dedicated unit on the shared constant-time GHASH primitive |
Zvknhb |
vsha2ms.vv / vsha2ch.vv / vsha2cl.vv; SEW=32 selects SHA-256, SEW=64 selects SHA-384/512 | dedicated unit; strict superset of Zvknha, so no separate Zvknha exists |
Zvknhk |
vkeccak.vi – in-place Keccak-p[1600] permutation across four consecutive vector registers | draft proposal, not a ratified extension – deliberately not advertised in the device tree |
In addition, Zvkt is a timing guarantee rather than an instruction set:
the vector engine’s add/subtract, logical, shift, compare, copy, extend,
merge, multiply/multiply-add, permute, and slide instructions (plus all of
Zvkb) execute with data-independent latency – satisfied by construction on
this core’s serial one-element-per-cycle engine and proved by a
cycle-count-equality unit test.
Self-Test Infrastructure
Every cryptographic execution unit carries a built-in known-answer self-test, delegated to its own standalone algorithm core rather than re-implemented:
-
Auto-triggered on first use. The very first instruction a unit ever executes after reset transparently runs the self-test first; software only sees that one instruction take longer.
-
Software-re-runnable. Writing the mechanism’s bit of
IRISCV_CRYPTO_SELFTEST_NOT_EXECUTED(0x7C0) triggers another run – the write is a trigger line, never a store: it changes nothing the CSR reads back. -
Sticky lock with recoverable failure. Once passed, the self-test never re-runs (a later trigger write is a permanent no-op). While failed, the block stays disabled for ordinary requests – every subsequent instruction of that unit traps as an illegal instruction, a hardware-enforced error return to software, not merely a status flag – until a software re-trigger through
NOT_EXECUTEDruns it again and passes, which clearsFAILEDand setsPASSEDon the same cycle.
Results are reported through four custom CSRs, all readable from every
privilege mode: IRISCV_CRYPTO_SELFTEST_NOT_EXECUTED (0x7C0, resets
all-1s), IRISCV_CRYPTO_SELFTEST_PASSED (0xFC0),
IRISCV_CRYPTO_SELFTEST_FAILED (0xFC1), and
IRISCV_CRYPTO_FIPS_APPROVED (0xFC2). Only NOT_EXECUTED accepts a
software write at all – and only from M or HS mode; writing any of the other
three traps as an illegal instruction even in M-mode. Eight real mechanisms
are wired to this interface – AES/Zvkned, AES-GCM/Zvkg, SHA-256/SHA-512
(Zvknhb’s two independent per-width tests), Keccak/Zvknhk, and the entropy
source’s own DRBG/Keccak self-test bits (Entropy Source)
– each owning its own bit position; one further bit is reserved for KMAC256.