Skip to content

Caliptra FW does not check for OTP error status after programming Field Entropy #4024

Description

@calebofearth

Mode: Subsystem

Concern: Caliptra does not detect errors associated with multiple repeated attempts to program Field Entropy.

Hardware-level lock (real protection exists): program()'s last step always triggers a digest calculation, which locks the partition in the fuse_ctrl/OTP controller. Per the OTP controller's own register docs:

ERR_CODE = 0x5 ACCESS_ERROR: "This error indicates that a locked memory region has been accessed. The corresponding controller automatically recovers from this error when issuing a new command." otp_ctrl_registers

So once a partition is locked, the OTP macro itself rejects further writes to it — the underlying secret bits are not overwritten or corrupted by a repeat attempt.

But Caliptra firmware doesn't check for this. Reading uds_fe_programming.rs (program()) (the code behind the FE_PROG mailbox command) closely:

  • It never reads ERR_CODE or any partition-digest/lock status before or after issuing the write+digest DAI commands.
  • Its only polling loop (is_dai_idle()) checks the DAI_IDLE readiness bit, not error bits — and ACCESS_ERROR "auto-recovers" the DAI back to idle, so a rejected write looks identical to a successful one from firmware's point of view.
  • There is no dedicated error code for this: the only FE-specific errors defined are RUNTIME_FE_PROG_ILLEGAL_LIFECYCLE_STATE and RUNTIME_FE_PROG_INVALID_PARTITION (error/src/lib.rs:1652,1657) — nothing like ..._ALREADY_PROGRAMMED.
  • Contrast this with the sibling zeroize() function in the same file, which explicitly reads back and verifies each step (verify_cleared) — that defensive pattern was not applied to program().
  • Test coverage (test_fe_programming.rs) only covers the happy path and an out-of-range partition index — no test exercises calling FE_PROG twice on the same already-locked partition.

Practical consequence: If an authenticated caller invokes FE_PROG twice for the same partition, firmware will attempt the write+digest sequence again each time. The OTP hardware will reject the second attempt (data stays safe), but since firmware doesn't check for that rejection, it will likely still return a mailbox success response — a false-success/observability gap rather than a security hole.

One more nuance: there are 4 independent FE partitions (0–3), and the caller explicitly picks the partition index in the request — Caliptra does not auto-select "the first available slot" the way the abstract architecture doc describes (doc/Caliptra.md:1297). So requesting different partitions across separate legitimate events (e.g., successive ownership transfers) is normal intended reuse, not "multiple programming" of the same fuse — tracking which slot is next is left to the SoC/MCU caller, which the spec explicitly requires to "prohibit... re-burning of already burned Field Entropy entries" (CaliptraIntegrationSpecification.md:930) but which isn't currently enforced in Caliptra's own FE_PROG handler.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions