Skip to content

tbor(sd): bring the Security Domain command family up on hardware - #688

Merged
Vishal Soni (vsonims) merged 7 commits into
mainfrom
user/radutta/tbor-sd-sealing-key
Sep 8, 2026
Merged

tbor(sd): bring the Security Domain command family up on hardware#688
Vishal Soni (vsonims) merged 7 commits into
mainfrom
user/radutta/tbor-sd-sealing-key

Conversation

@radutta99

@radutta99 Rajib Dutta (radutta99) commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Brings the TBOR Security Domain command family up on real hardware. All 7 SD commands — 32
tests — now run and pass on silicon; they were emulator-only before. KeyReport (6 tests) comes
with them.

Why these were gated

Two real firmware bugs, both invisible on the emulator because the std PAL models neither PKA
operand addressing nor persistent device state.

1. ecc_pub_from_priv faulted the PKA

It called the driver's ecc_gen_pub_key, which issues ECC_POINT_MUL with the operands in the
wrong slots:

ecc_point_mul     arg1 = point_xy   arg2 = scalar     <- correct
ecc_gen_pub_key   arg1 = priv_key   arg2 = 0          <- scalar in the point slot, null scalar

The engine reads the scalar from address 0 rather than GSRAM and faults with AXI BUS_ERROR
(0x08f0c003).

The PKA has no base-point-multiply command at allECC_KEY_GEN_* makes a fresh keypair,
and ECC_POINT_MUL always needs the point supplied explicitly. So the fix delegates to
ecc_priv_pub_key, which already does the multiply with every operand in a GSRAM DMA buffer and
additionally range-checks the scalar against the curve order.

This was already known: the deterministic-keygen path hit it and left a NOTE in ecc_det.rs. This
call site kept using the broken primitive because key_report, its main consumer, had zero
hardware coverage.

The now-dead ecc_gen_pub_key is deliberately left in place — removing a public driver API is
out of scope here, and is filed as a follow-up.

2. sd_initialized outlived its key

clear_state cleared the SD one-shot only under PartResetKind::Disable, so it survived an NSSR
(Migrate). But the same function drops the SDMK handle for every reset kind, and the caller
wipes the vault — so after an NSSR the partition claimed a security domain whose masking key no
longer existed, and SdCreateRemoteBackup's one-shot gate then refused to mint a replacement,
permanently.

This diverges from the std PAL, which clears the flag in both part_enable and
clear_enabled_state — exactly why the emulator never saw it.

bk3_initialized is deliberately left in the Disable block: its sealed_bk3 blob is preserved
by Migrate, so that flag stays consistent with its material.

Commits

925314cb SdSealingKeyGen on hardware (test-gating only)
e82b1a50 PKA fix + un-gate key_report
9e1eb367 sd_initialized fix + un-gate the remaining 6 SD files
4c2fdf9e drop a stale "why these stay emulator-only" note (docs)
54e53585 validate the curve explicitly in ecc_pub_from_priv

This branch was previously stacked on #666. Now that #666 has merged it is rebased onto main,
and the history above is only the SD work — the superseded #666 commits it used to carry are gone,
along with a stale emulator OOB page builder that #666's final form made wrong. Net diff is
141 insertions across 11 files.

Coverage

Command Tests
SdSealingKeyGen 6
SdCreateRemoteBackup 8
SdCreatePeerBackup 3
SdResealRemoteBackup 4
SdRestoreLocalBackup 4
SdRestorePeerBackup 4
SdRestoreRemoteBackup 3
Total 32

Plus KeyReport 6/6, which was faulting before and is what proves the PKA fix.

Validation

Firmware 080C5974, rebased onto current main.

suite result
SD family (hardware) 32/32
KeyReport (hardware) 6/6
Full TBOR (hardware) 264/264
TBOR emu (nextest, ci-tbor-emu) 389/389
MBOR DDI emu (nextest, ci-emu-ddi) 602/602
root clippy --workspace --all-targets -D warnings clean
cargo fmt --all --check, both workspaces clean
uno firmware clippy -D warnings clean

The SD suite was previously re-verified in 3 consecutive runs with no reset and no driver reload
between them — it is genuinely self-isolating, which is the point of the sd_initialized fix.

Not in scope

Non-SD emulator gates remain, with 36 genuine hardware failures triaged but unfixed: AES
CMD_ERROR on 5 crypto paths, PendingKeyGeneration on 20, and UnsupportedKeyScope on 7.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Enables the TBOR Security Domain (SD) command family to run on real hardware by fixing a Uno PKA ECC public-key derivation fault and aligning partition SD one-shot state handling with key lifetime, then removing emulator-only gating from the SD + KeyReport integration tests.

Changes:

  • Firmware: route ecc_pub_from_priv through the proven ecc_priv_pub_key path to avoid the PKA operand-slot fault on silicon.
  • Firmware: clear sd_initialized for all partition reset kinds so the SD one-shot flag cannot outlive the SDMK handle/material.
  • Tests: remove #[cfg(feature = "emu")] gating and rename SD/KeyReport tests so they run on hardware-capable backends.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
fw/plat/uno/fw/pal/src/crypto/ecc.rs Switch public-key derivation to ecc_priv_pub_key to avoid hardware PKA operand addressing fault and add scalar validation.
fw/plat/uno/fw/drivers/part_store/src/part_store.rs Clear sd_initialized in the shared reset path so SD one-shot state follows SDMK lifetime across reset kinds.
ddi/tbor/types/tests/commands/sd_sealing_key_gen.rs Remove emu-only gating; clarify OOB requirements are in setup, not the SD command itself; rename tests.
ddi/tbor/types/tests/commands/sd_create_remote_backup.rs Remove emu-only gating; document SD one-shot behavior and reset interactions; rename tests.
ddi/tbor/types/tests/commands/sd_create_peer_backup.rs Remove emu-only gating and rename tests to run on hardware.
ddi/tbor/types/tests/commands/sd_reseal_remote_backup.rs Remove emu-only gating and rename tests to run on hardware.
ddi/tbor/types/tests/commands/sd_restore_local_backup.rs Remove emu-only gating and rename tests to run on hardware.
ddi/tbor/types/tests/commands/sd_restore_peer_backup.rs Remove emu-only gating and rename tests to run on hardware.
ddi/tbor/types/tests/commands/sd_restore_remote_backup.rs Remove emu-only gating and rename tests to run on hardware.
ddi/tbor/types/tests/commands/key_report.rs Remove emu-only gating and rename tests to validate KeyReport on hardware.
Suppressed comments (1)

fw/plat/uno/fw/pal/src/crypto/ecc.rs:767

  • ecc_pub_from_priv used to call map_ecc_curve(curve)? to fail gracefully if HsmEccCurve gains new variants that this PAL/PKA driver doesn’t support. After switching to ecc_priv_pub_key, the function no longer validates curve beyond length checks, which could silently accept a future curve variant that happens to share a wire length with an existing curve and compute the public key on the wrong curve.
        let wire_pub_len = curve.wire_pub_key_len();
        if priv_key.len() != curve.wire_priv_key_len() || pub_key.len() < wire_pub_len {
            return Err(HsmError::InvalidArg);
        }

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It changes hardware-backed cryptographic execution and persistent partition state semantics, which warrants final human review alongside silicon validation evidence.

Review details
  • Files reviewed: 10/10 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread fw/plat/uno/fw/pal/src/crypto/ecc.rs
@radutta99
Rajib Dutta (radutta99) changed the base branch from user/radutta/tbor-part-final to main September 8, 2026 17:29
The handler has been implemented and dispatched at opcode 0x09 for a
while, and the host types existed, but all 13 tests sat behind
`#![cfg(feature = "emu")]`, so the command had zero silicon coverage.

Un-gate the file and drop the now-misleading `_emu` suffixes. No
production code changes -- this is purely test gating. It compiles for
the hardware profile with no further un-gating because the part_final
work already exposed `x509_fixture` and `part_policy_with_pota`.

Worth recording for the rest of the SD family: `SdSealingKeyGen` carries
no out-of-band data. The request is a session id plus a 1-byte scope and
the response is a 180-byte masked key plus a 96-byte public key, so the
command itself runs on any transport. What needs OOB is the *setup* --
`finalized_co_session` drives `PartFinal`, whose PTA chain travels out of
band -- so the two roundtrip tests need the driver's data-transfer path
while the four reject tests do not.

Hardware: SdSealingKeyGen 6/6 (both roundtrips included), full TBOR
199/199 (was 193, +6 newly enabled), part_final still 10/10. Emu 6/6 and
full emu TBOR 302/302. clippy `-D warnings` and fmt clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b6e9403d-692c-4903-97ee-87f2606151c3
(cherry picked from commit 59d8d22)
`ecc_pub_from_priv` called the PKA driver's `ecc_gen_pub_key`, which
issues the point-mul opcode with a null (0) scalar operand. The engine
then reads the scalar from address 0 rather than GSRAM and faults with an
AXI BUS_ERROR (0x08f0c003).

This was already known: the deterministic-keygen path hit it, worked
around it in `ecc_det.rs`, and left a NOTE at line 872 naming the exact
status. This call site kept using the broken primitive, and nothing
caught it because `key_report` -- its main consumer -- was emulator-only,
and the std PAL does not model PKA operand addressing. Green emu, faulting
silicon; the same blind spot as the PRP/SGL descriptor bug in part_final.

Delegate to `ecc_priv_pub_key`, which already performs the multiply with
every operand in a GSRAM DMA buffer. The curve argument is redundant
rather than lost: that helper infers the curve from the scalar length,
which is `wire_priv_key_len()` -- defined as `wire_coord_len()` -- so the
mapping is identical, and it additionally range-checks the scalar against
the curve order.

Un-gate the `key_report` tests, which had zero hardware coverage and are
what proves the fix.

`sd_create_remote_backup` is left emulator-only, with the reason recorded
in its module docs: it provisions the SDMK behind a one-shot gate that
resets only on partition free / NSSR, so on hardware the 8 tests are
mutually exclusive -- the first create after a boot passes and the rest
return SdAlreadyInitialized (0x08700108), even across separate test runs.
The firmware path itself is fine: the roundtrip passes on silicon after a
power cycle. Un-gating needs a harness-driven way to reset the one-shot.

Hardware: KeyReport 6/6 (was faulting), full TBOR 205/205, SdSealingKeyGen
6/6, part_final 10/10. Emu 302/302. clippy `-D warnings` and fmt clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b6e9403d-692c-4903-97ee-87f2606151c3
(cherry picked from commit a68ab27)
…amily on hw

`clear_state` cleared the SD one-shot only under `PartResetKind::Disable`,
so it survived an NSSR (`Migrate`). But the same function drops the SDMK
handle (`set_sd_mk_key_id(None)`) for *every* reset kind, and the caller
wipes the vault material, so after an NSSR the partition claimed a
security domain whose masking key no longer existed -- and
`SdCreateRemoteBackup`'s one-shot gate then refused to mint a
replacement. The flag outlived its key, permanently.

This is a divergence from the std PAL, which clears `sd_initialized` in
both `part_enable` and `clear_enabled_state`. That is exactly why it was
invisible: on emu each reset clears the flag and the tests isolate
cleanly, while on uno the state persisted across NSSR -- and across
separate `cargo test` runs -- so only silicon could show it.

Move the clear into the unconditional section, next to the key-handle
drop it has to follow. `bk3_initialized` is deliberately left in the
`Disable` block: its `sealed_bk3` blob *is* preserved by `Migrate`, so
that flag stays consistent with its material.

With the flag fixed the whole SD family becomes self-isolating on
hardware, so un-gate all six remaining files.

Hardware: SD family 32/32 in a single ordinary suite run with no
per-test reset, and repeatable back-to-back. Full TBOR 231/231 (was 205,
+26). Emu 302/302. clippy `-D warnings` and fmt clean.

Found by a discovery run that lifted every emu gate and re-ran each
failure behind a PCI FLR; triage for the remaining non-SD gates is in the
session notes (AES CMD_ERROR on 5 crypto paths, PendingKeyGeneration on
20, UnsupportedKeyScope on 7).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b6e9403d-692c-4903-97ee-87f2606151c3
(cherry picked from commit 6713fb2)
`sd_create_remote_backup`'s module docs still claimed the file had to
stay emulator-only because the SD one-shot survived the harness factory
reset. That stopped being true in the same series: clearing
`sd_initialized` on every reset kind made the file self-isolating, and it
was un-gated and now passes 8/8 on hardware.

Replace it with what a reader actually needs — that the one-shot exists
and is asserted by `sd_create_remote_backup_is_one_shot`, that
`clear_state` clears it on the `Migrate` the harness reset drives, and
why the old behaviour was invisible on emu.

Docs only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b6e9403d-692c-4903-97ee-87f2606151c3
(cherry picked from commit 879c4ed)
Review feedback. `ecc_pub_from_priv` takes a `curve` but delegates to
`ecc_priv_pub_key`, which re-derives the curve from the scalar length, so
`curve` was only ever checked implicitly via `wire_priv_key_len()`. Its
docs promise `InvalidArg` for an unsupported curve, and that contract was
not actually enforced.

Call `map_ecc_curve(curve)?` up front to restore it.

That closes the stated contract, but not the deeper worry: the delegation
is only sound while no two curves share a wire scalar length. Today they
are 32 / 48 / 68, pairwise distinct. Rather than leave that as a comment,
pin it with `wire_priv_key_len_is_unambiguous` in the traits crate, which
fails if a future variant collides -- so the failure lands in CI instead
of the firmware quietly computing on the wrong curve.

Hardware (firmware checksum 07F18538): SD family 32/32, key_report 6/6,
full TBOR 231/231. Emu 302/302, traits unit tests 5/5, clippy
`-D warnings` and fmt clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b6e9403d-692c-4903-97ee-87f2606151c3
(cherry picked from commit 6e6aaf0)
@radutta99
Rajib Dutta (radutta99) force-pushed the user/radutta/tbor-sd-sealing-key branch from 6de3e0a to 54e5358 Compare September 8, 2026 17:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It changes hardware-facing ECC/PKA behavior and partition persistent reset semantics in security-critical paths, which warrants final human review despite strong test validation.

Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread fw/pal/traits/src/crypto/ecc.rs Outdated
Copilot AI review requested due to automatic review settings September 8, 2026 17:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes address concrete silicon-only faults with clear invariants (GSRAM-backed PKA operands, reset-state consistency) and are supported by expanded test coverage now running on hardware.

Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

The ALL list claimed that adding an HsmEccCurve variant would force it to
be updated, but nothing enforced that: a new variant could be added
without touching ALL, and wire_priv_key_len_is_unambiguous would still
pass while silently skipping the new curve -- exactly the case the test
exists to catch.

Add index_in_all, a wildcard-free match, so a new variant stops the
module compiling until it is given an arm; all_lists_every_curve then
fails unless it is also appended to ALL at the matching index.

Verified by temporarily adding a fourth variant: the build fails in
check_match on index_in_all (E0004).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b6e9403d-692c-4903-97ee-87f2606151c3
Copilot AI review requested due to automatic review settings September 8, 2026 17:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes are narrowly scoped to hardware-enablement fixes with corresponding test un-gating, and no correctness issues were found in the updated logic or test harness interactions.

Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Preserve the SD sealing-key transport documentation while incorporating
main's independent hardware enablement in #692 and subsequent changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b6e9403d-692c-4903-97ee-87f2606151c3

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes are narrowly scoped, address confirmed silicon-only failures, and are backed by expanded unit/integration test coverage with no additional issues found in the reviewed diffs.

Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@vsonims
Vishal Soni (vsonims) added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit bd127c4 Sep 8, 2026
30 checks passed
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.

3 participants