Edited 2026-06-10: BSI fuse readback added + new test results sharpened the diagnosis — see Update reply below.
Setup
- Module: Jetson Orin Nano Super (P3767, T234, A01)
- L4T: R36.5.0
- Host: Ubuntu 24.04 + Docker (BSP container)
- Auth chain: SBKPKC (PKC + SBK both fused). Production Mode NOT yet enabled. ODM_K1/K2 unprovisioned.
BOOT_SECURITY_INFO_0bit [3] burned. - SBK source: YubiHSM 2 (FIPS) opaque object. AES-256, 32 raw bytes. Same 32 bytes (sha256-verified) were used both for
--encrypt_keytoflash.shand for the SBK fuse burn (viaodmfuse.sh -X <xml>with<fuse name="SecureBootKey" size="32" value="0x…" />). - PKC source: YubiHSM RSA-3072 private key (same root used for both the PKC fuse hash burn and for tegrasign PKC signing).
Symptom
flash.sh --no-flash --hsm -u <PKC.pubkey> -v <SBK.hex> -c bootloader/generic/cfg/flash_t234_qspi.xml jetson-orin-nano-devkit-super internal produces signed+encrypted artifacts cleanly, then fails at the RCM boot-rom communication step:
[ 8.0816 ] tegrarcm_v2 --new_session --chip 0x23 0 --uid
--download bct_br br_bct_BR.bct
--download mb1 mb1_t234_prod_aligned_sigheader_encrypt.bin.signed
--download psc_bl1 …
--download bct_mb1 …
[ 8.0822 ] BR_CID: 0x…
[ 8.1053 ] Sending bct_br
[ 8.1063 ] Sending mb1
[ 8.1068 ] ERROR: might be timeout in USB write.
Error: Return value 3
Sending bct_br does not report an error; Sending mb1 does. The device then warm-resets (FC REC jumper is sampled only at cold power-on, so warm-reset path goes coldboot from QSPI). On UART we observe zero bootrom-side output during the entire RCM session — the only UART content captured is from the post-rejection QSPI coldboot fallback (MB1-and-onwards print; bootrom itself is silent).
The QSPI on this module has a working PKC-only-signed boot chain from a pre-SBK-fuse-burn flash (which coldboots fine), so the module is not bricked.
What we’ve verified before posting
Listing this so we don’t double-cover ground:
-
HSM SBK bytes.
sha256(HSM opaque)matches the ceremony-recorded anchor. Same 32 bytes are written to the file we hand toflash.sh’s--encrypt_keyand were the literal bytes in the<fuse name="SecureBootKey" value="0x…" />XML when we burned the fuse viaodmfuse.sh -X. -
KDF chain spec. Walked
bootloader/tegrasign_v3_oemkey_t234.yamlfor0x230. For MB1: SBK → KDF(label=“MB1B”, ctx=“”) → SBK_MB1_KDK, then SBK_MB1_KDK → KDF(label=DERSTR, ctx=VER) → SBK_MB1_DK, AES-GCM(SBK_MB1_DK, IV, AAD, payload). -
KDF message construction. Implemented per
bootloader/tegrasign_v3_util.py:get_composed_msg:counter:4BE || label_b || 0x00 || (ctx_b if non-empty else 0x00) || L:4BE. Verified byte-for-byte against NVIDIA’s reference across 5 vectors. -
Hook invocation. Diagnostic prints from a recent attempt show our HSM hook called with the correct labels — e.g. for MB1:
oem_hsm_kdf ENTRY: in_key='SBK' out_key='SBK_MB1_KDK_…' label='4d423142' ctx='' oem_hsm_kdf ENTRY: in_key='SBK_MB1_KDK_…' out_key='SBK_MB1_DK_…' label='a560ff402f4e6445b260091ed663f43e' ctx='01040100' oem_hsm_aes_gcm ENTRY: key_id='SBK_MB1_DK_…' plain_len=274928 -
Self-consistency. Standalone Python that mirrors the chain end-to-end (using the captured
kdf_args_*.yamlfrom the actual attempt) encrypts and then re-derives DK + AES-GCM-decrypts. Round-trip succeeds for both BR-BCT and MB1 → our crypto code is internally consistent. -
PKC path is healthy. Same HSM-backed PKC signing produced a PKC-only-signed boot chain that flashes and boots to userspace on the same module (pre-SBK fuse-burn). So PKC-RSA-PSS sig +
oem-rsaupdatesigheaderflow is working. -
tegraopenssl BR-BCT note.
tegraopenssl --oem_encrypton BR-BCT appears to be a no-op stub in R36.5 (emitsERROR: 0to stdout, exits 0, leaves the file unmodified). Confirmed via standalone repro on captured inputs.do_kdf_oem_encpost-processes via HSM hooks, so the artifact is still produced correctly via the HSM path — but flagging in case it’s relevant.
Similar problem:
This post: ERROR: might be timeout in USB write after Fuse burning
Same ERROR: might be timeout in USB write at Sending mb1, but the marked solution there is about using the correct --auth flag for the FIRST fuse burn on an unfused board (--auth NS, not --auth SBKPKC). That doesn’t apply to us — our SBK burn used --auth PKC correctly because PKC was already burned by then, per our incremental burn order.
Our setup also differs in three meaningful ways:
- BSI: ours
0x1e9(bit 9 clear, ODM keys not enforced); theirs0x20b(bit 9 set, ODM Key Valid) - ODM K1/K2: unprovisioned for us (intentional, planned for a later phase); they burned
OemK1 - Production Mode: NOT yet enabled on our side; theirs was
0x1(locked) - L4T: we’re on R36.5; they were on R35.4.1
So we may be hitting the same error message for a different root cause. Flagging in case the bit-9-clear / ODM-unprovisioned / file-key vs HSM-key path on R36.5 has its own quirks the linked thread didn’t surface.
Questions
-
Byte-order during
odmfuse.sh -XSBK burn. We pass the 32 SBK bytes as a0x…hex string in<fuse name="SecureBootKey" value="…" />. Doesburnfuses(viategraflash.py --cmd burnfuses <xml>) burn those bytes in the same order the bootrom reads them for AES-key use, or is there a word-level / register-level swap? SinceFUSE_KEYS_SBK_0_0..7are PSC fuses (per Note 6 / page 8 of “Platform Security Controller Fuses”) and can’t be read back, we have no post-burn way to verify byte order. -
Bootrom-side rejection observability. Is there any way to get bootrom to emit a rejection reason on UART or some other channel on Orin Nano R36.5? (We have UART connected,
FUSE_DEBUG_AUTHENTICATION_0andFUSE_OPT_NVJTAG_PROTECTION_ENABLE_0are NOT burned on this module — but bootrom still appears silent during the failed RCM session.) Even a single error code would localize this. -
Anything else worth checking? Given the chain spec + crypto are verified self-consistent, is there a structural field in BR-BCT or mb1 sigheader that bootrom enforces on SBKPKC-fused silicon that doesn’t get populated correctly by the default
flash.sh --hsmflow when SBK is HSM-resident? (E.g., a PSC-specific field, or something in theBR-BCT customer inforegion.) -
R36.4.4/R36.5 HSM support — does it cover SBK encryption, or signing only? Two relevant pieces of official documentation:
- R36.4.4 release notes, under Features: “Added support for using Hardware Security Module (HSM) to sign boot images.” R36.5 ships on top of 36.4.4, so this support should be present. But the wording “to sign” is precise — it suggests the PKC RSA signing path was the new HSM-validated capability, not the SBK AES-GCM encryption path.
- R36.5 Package Manifest entry for
bootloader/tegrasign_v3_hsm.py: “A TegraFlash component that can directly communicate with OEM’s HSM server.” — generic, doesn’t pin down scope.
SBKPKC requires both: PKC signing (✓ working for us, validated PKC-only boot on the same module) AND SBK-driven KDF + AES-GCM encryption of boot blobs (✗ failing). Concrete asks:
- Is the R36.4.4/R36.5 “HSM to sign boot images” capability strictly for the PKC signing step, or does it also cover the SBK-driven KDF chain + AES-GCM encryption of the boot blobs?
- If it covers both: is there a NVIDIA-validated end-to-end SBKPKC boot test on Orin Nano R36.5 that exercises the HSM hook path (not just file keys)? Even a
softhsmreference run we could diff against would help. - If it covers signing only: that’s our exact gap. The shipped
tegrasign_v3_hsm.pyis documented in its own header as a reference template inviting OEM replacement. Our YubiHSM PKCS#11 implementation matches NVIDIA’sget_composed_msgbyte-for-byte (5 vectors) and encrypt→decrypt round-trips self-consistently for BR-BCT and MB1, but if there’s no NVIDIA-side end-to-end test of the SBK-encrypt-via-HSM path, our artifacts may be subtly incompatible in a way only the SBKPKC-fused bootrom catches.
Artifacts available privately
Happy to share via NVIDIA’s preferred private channel:
- Full host flash log
- Diag log showing the entire HSM-hook KDF chain walk (every label + context + derived-key-id for every artifact on the flash list)
- Captured
kdf_args_*.yamloutputs fromtegraopenssl --oem_encryptfor BR-BCT, MB1, MB1-cold-boot-BCT, MB1-recovery-BCT, MB2, PSC_BL1 - Module ECID + fuse map readback
- Our
tegrasign_v3_hsm.py(YubiHSM PKCS#11 implementation) for cross-checking against NVIDIA’s softhsm reference
Not posting publicly because it includes ECID and HSM-handle details.
Any pointers appreciated. We’re stuck after exhausting the on-our-side diagnostics and the bootrom silence makes further isolation hard.
Update (2026-06-10): authoritative fuse readback + sharpened diagnosis
Since posting, we ran three more tests on the same module. Two results materially change the framing of the original questions.
What we did
-
Hybrid file-key test. Patched
do_kdf_oemin the BSP to honor an env var that skips the HSM branch for SBK only (PKC signing still HSM-routed). This forced the SBK encryption side through NVIDIA’s stock file-key path (tegraopenssl--kdfoem) using the same 32-byte SBK that lives in our HSM. Result: sameSending mb1 → ERROR: might be timeout in USB write. Confirms our HSM hook is not the bug — both code paths produce equivalent rejected ciphertext. -
Per-4-byte-word swapped SBK test. Same flow, but we reversed every 4-byte word of the SBK before handing it to tegrasign. Result: same rejection, same 5ms timing. Rules out the most common byte-order transform.
-
Literal zero-SBK test. Provided 32 bytes of zeros as the SBK file. Result: same rejection. Rules out “fuse SBK is still all zeros / burn never took.”
-
PKC-only flash on the SBKPKC-fused module. No
--encrypt_key, no SBK encryption applied to any artifact. Result:Sending bct_br → Sending mb1 → Sending psc_bl1 → Sending bct_mb1 → Boot Rom communication completed. The flow went all the way to MB2 Applet (BL: version 1.4.0.7-t234-…) and successfully read every fuse viareadfuses.
What the fuse readback shows
BootSecurityInfo: 000001e9
PublicKeyHash: de5adb342a828f4938a14fc271842bfda644c1713442696c00cdca7aa8d245493a48d145e2e16bf767c6c71f1abd588d5b6a700cc803a9f7d5e28858dbba45a4
PkcPubkeyHash1: <all zeros>
PkcPubkeyHash2: <all zeros>
SecurityMode: 00000000
ArmJtagDisable: 00000000
DebugAuthentication: 00000000
SwReserved: 00000000
OdmInfo: 00000000
ReservedOdm0..7: <all zeros>
Sku: 000000d5
BootSecurityInfo = 0x1e9 decodes per Orin Fuse Specification (DA-10877-001 v1.5), Table 1, page 7:
- bit [0] = 1 → Secure Boot Authentication Scheme = 3072-bit RSA ✓
- bit [3] = 1 → “secure boot encryption scheme (SBK) enable” ✓
- bits [8:4] = 1 → “Reserved (may not be logic 0 by default)” — matches the spec note
- bit [9] = 0 → ODM Key Valid — intentionally not provisioned
So BSI is correctly burned to the value we intended.
What this changes about the diagnosis
The key word in the spec is “enable”, not “enforce”. On our SBKPKC-fused unit, BSI[3]=1 means bootrom can decrypt SBK-encrypted artifacts, but it does not require them — plaintext PKC-signed artifacts boot just fine. The failure pathway is now precisely:
- We hand bootrom an mb1 sigheader that flags the payload as SBK-encrypted.
- Bootrom validates the PKC RSA-3072 signature → passes.
- Bootrom derives the per-image AES-GCM key (SBK_MB1_DK) from the SBK in
FUSE_KEYS_SBK_0_0..7via the chain integrasign_v3_oemkey_t234.yaml. - Bootrom AES-GCM-decrypts the payload → tag verification fails → reject within ~5 ms.
The only way that tag-verification can fail with all of our other plumbing verified is: the SBK bytes the PSCROM is using do not match the SBK bytes we used at encrypt time. No byte order we’ve tried matches. The fuse SBK is non-zero (zero-SBK encrypt also rejects).
Two specific new questions (in addition to the original four)
Q5. Does PSCROM combine FUSE_KEYS_SBK with the per-chip HUK (or any other per-chip secret) before use as the AES-256 key for boot-image decryption?
This is the critical question for our OEM workflow. If FUSE_KEYS_SBK is used directly as a fleet-wide key (as we’ve assumed), then once we discover the byte transform that burnfuses applies, a single SBKPKC firmware works across every module that was burned the same way. If instead the effective key is KDF(SBK_fuse, HUK_chip, …), then every module has a unique effective SBK, and SBKPKC requires per-device firmware builds — fundamentally different operational model.
Q6. How does burnfuses choose between cleartext mode and encrypted-form mode for FUSE_KEYS_SBK?
The spec (Table 1, FUSE_KEYS_SBK_0_*, page 5) says:
“This key may be programmed in encrypted form, with decryption performed by PSCROM”
There are sibling fuses FUSE_KEYS_OEM_FUSE_IV_0 (“Initialization vector used as part of the OEM fused keys encryption”) and FUSE_KEYS_KDK0_0_0 (“Key Decryption Key, these keys can be programmed in encrypted form”). On 2026-05-14 we burned via odmfuse.sh -X <xml> with:
<genericfuse MagicId="0x45535546" version="1.0.0">
<fuse name="SecureBootKey" size="32" value="0x<32 raw bytes hex>" />
</genericfuse>
— bare hex bytes, no IV / KDK fuse provisioning beforehand. If burnfuses interpreted that as encrypted-form input and PSCROM is now “decrypting” our plaintext bytes with a default/zero KDK to obtain the effective SBK, that would explain the consistent ~5 ms tag-verification failure across every byte permutation we’ve tried — because no permutation of the post-decryption garbage is what we encrypt with.
Concretely: is there an XML attribute (or separate odmfuse flag) that selects cleartext-mode SBK burn for the 3072-RSA + SBK-enable BSI configuration? Or is FUSE_KEYS_OEM_FUSE_IV_0 / FUSE_KEYS_KDK0_0_0 a required precursor we missed?
Updated verification matrix
| Check | Result |
|---|---|
sha256(HSM SBK bytes) matches ceremony anchor |
✓ |
Chain spec walked against tegrasign_v3_oemkey_t234.yaml |
✓ |
_composed_kdf_msg byte-equals NVIDIA’s get_composed_msg |
✓ |
| HSM hook invoked with correct labels per chain spec | ✓ |
| BR-BCT encrypt → decrypt round-trip (offline) | ✓ |
| MB1 encrypt → decrypt round-trip (offline) | ✓ |
| BSI fuse readback = 0x1e9 (bit [0] + bit [3] set per intent) | ✓ |
| PKC-only flash on the SBKPKC-fused module | ✓ boots through MB2 Applet, full readfuses succeeds |
| File-key SBK encrypt (bypasses our HSM hook) | ✗ rejected at Sending mb1 |
| Per-4-byte-word swapped SBK encrypt | ✗ rejected at Sending mb1, same 5 ms timing |
| Zero-SBK encrypt | ✗ rejected at Sending mb1, same 5 ms timing |
| Real-HSM-SBK encrypt (original test) | ✗ rejected at Sending mb1, same 5 ms timing |
odmfuseread of FUSE_KEYS_SBK_0_* |
✗ PSC fuse, unreadable by design |
The four Sending mb1 → ERROR rejections all have ~5 ms host-side timing and bootrom is silent on UART throughout. Consistent with one specific check inside PSCROM (most likely the AES-GCM tag check on the first SBK-encrypted artifact).
Pragmatic question for OEM workflow
Given that the module boots cleanly under PKC-only, is it expected NVIDIA practice to ship pilot units in this “SBK fuse burned but not currently used for encryption” mode and add SBK-encrypted artifacts in a later firmware update, or is that an unsupported configuration? The behavior we’re observing (BSI[3] enables but does not enforce) suggests it’s a supported posture, but explicit confirmation would help us scope our deployment plan.