Skip to content

LMS/XMSS: don't skip the software reload when a read callback is set - #11059

Merged
dgarske merged 1 commit into
wolfSSL:masterfrom
Frauschi:sfhb_reload_fix
Aug 6, 2026
Merged

LMS/XMSS: don't skip the software reload when a read callback is set#11059
dgarske merged 1 commit into
wolfSSL:masterfrom
Frauschi:sfhb_reload_fix

Conversation

@Frauschi

@Frauschi Frauschi commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Description

wc_LmsKey_Reload and wc_XmssKey_Reload return success without doing any work whenever key->devId != INVALID_DEVID, assuming a device-bound key keeps its private state inside the device. That does not hold for a caller that sets a devId only to route primitives to a hardware accelerator while keeping the key state in its own storage - a wolfHSM server configures a server-wide devId for AES/ECC/RSA, then installs LMS/XMSS read/write callbacks and calls Reload before signing.

With such a devId configured, Reload returned 0 immediately and left the key unusable for the first sign that follows:

  • LMS: priv_data stays NULL, wc_hss_sign finds priv.inited clear and calls wc_hss_init_auth_path, which derives its first read from a NULL priv pointer - a bus fault on a target with nothing mapped at low addresses.
  • XMSS: key->sk is never allocated (sk_len stays 0), so the sign fails with IO_FAILED_E for a read callback that honours the length it is given, and faults for one that writes a fixed-size record.

Key generation hid the problem: wc_LmsKey_MakeKey already treats devId as "offer the operation to the callback, fall back to software on CRYPTOCB_UNAVAILABLE". Reload had no equivalent fall-through - and no cryptocb reload op exists to offer it to - so the same key and the same devId were interpreted two different ways by the same API.

The fix qualifies the short-circuit with key->read_private_key == NULL: a caller that installed a read callback is asking for the software reload to fetch state through it, while a genuinely device-backed key installs no such callback. Key generation still offers the operation to the crypto callback, so a port with real stateful-hash-signature hardware is unaffected.

Testing

test_wc_LmsKey_reload_devid and test_wc_XmssKey_reload_devid cover both arms of the new condition, driven by a crypto callback that declines every operation with CRYPTOCB_UNAVAILABLE (the accelerator this fix is about):

  • read callback set: Reload must come back with the private key expanded (priv_data / sk), and sign/verify must succeed;
  • no read callback: Reload must still short-circuit and leave the key state in the device.

Asserting on the expanded key means the old behaviour fails the assertion rather than the NULL dereference it leads to. Verified against ./configure --enable-lms --enable-xmss --enable-cryptocb --enable-debug: both tests fail before the fix and pass after it, with the rest of the lms and xmss groups unaffected (every other key there uses INVALID_DEVID). Also builds clean in an --enable-all tree, where the whole group compiles out.

The public Doxygen for both Reload functions now documents that the read callback, not the devId, decides whether the software reload runs, and that a 0 return can mean nothing was loaded.

@Frauschi Frauschi self-assigned this Aug 5, 2026

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #11059

Scan targets checked: wolfcrypt-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src

No new issues found in the changed files. ✅

@Frauschi

Frauschi commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Jenkins retest this please

@Frauschi Frauschi assigned wolfSSL-Bot and unassigned Frauschi Aug 5, 2026
@dgarske

dgarske commented Aug 5, 2026

Copy link
Copy Markdown
Member

Jenkins retest this please "PRB-fips-ready-config'"

@dgarske

dgarske commented Aug 5, 2026

Copy link
Copy Markdown
Member

Jenkins retest this please

Comment thread doc/dox_comments/header_files/wc_lms.h Outdated
wc_LmsKey_Reload and wc_XmssKey_Reload return success without doing any
work whenever key->devId != INVALID_DEVID, on the assumption that a
device-bound key has its private state inside that device. That assumption
does not hold for a caller that sets devId only to route primitives to a
hardware accelerator while keeping the key state in its own storage.

A wolfHSM server is exactly that caller. It configures a server-wide devId
so AES, ECC and RSA reach the platform accelerator, then for LMS/XMSS it
installs read/write callbacks and calls Reload to rebuild the expanded
private key before signing. With a hardware devId configured, Reload
returned 0 immediately and the key was left unusable for the first sign
that follows. The failure needs no invalid input, only a server built with
an accelerator.

For LMS that first sign is a crash: key->priv_data stays NULL, wc_hss_sign
finds priv.inited clear and calls wc_hss_init_auth_path, which derives its
first read from a NULL priv pointer. On a target without a mapping at low
addresses that is a bus fault. For XMSS the skipped reload never allocates
key->sk, leaving both the pointer NULL and sk_len 0, so the outcome depends
on the caller's read callback: one that honours the length it is given
returns nothing and the sign fails with IO_FAILED_E, while one that writes
a fixed-size record faults on the NULL destination.

Key generation was unaffected and hid the problem: wc_LmsKey_MakeKey
already treats devId as "offer the operation to the callback, fall back to
software on CRYPTOCB_UNAVAILABLE", so it populates the key correctly when
the accelerator declines. Reload had no equivalent fall-through, so the
same key and the same devId were interpreted two different ways by the
same API.

Qualify the short-circuit with key->read_private_key == NULL. A caller that
has installed a read callback is asking for the software reload to fetch
the state through it, whereas a genuinely device-backed key installs no
such callback. Key generation keeps offering the operation to the crypto
callback, so a port with real stateful-hash-signature hardware is not
prevented from using it.

The reference POSIX wolfHSM server runs with INVALID_DEVID, which is why
this was not caught by existing tests. test_wc_LmsKey_reload_devid and
test_wc_XmssKey_reload_devid cover both arms of the new condition: a key
whose read callback is set must come back from Reload with its private key
expanded (priv_data for LMS, sk for XMSS) and able to sign, while a key on
the same devId with no read callback must still short-circuit. Both tests
register a crypto callback that declines every operation with
CRYPTOCB_UNAVAILABLE, which is the accelerator this fix is about. Asserting
on the expanded key means the old behaviour fails the assertion rather than
the NULL dereference it leads to. The rest of the LMS and XMSS suite is
unaffected, as every other key there uses INVALID_DEVID.

The XMSS test needs the H10 SHA-256 parameter set, which is only in the
algorithm table when both the hash and the height are compiled in, so it
carries a guard for that. The crypto callback's own guard is the exact
union of the two test guards, or a build with only one of the two
algorithms would emit it with no caller and fail -Werror.

Both the Reload implementation comments and the published Doxygen now state
that the read callback, not the devId, decides whether the software reload
runs.
@Frauschi
Frauschi requested a review from dgarske August 5, 2026 20:25
@Frauschi Frauschi removed their assignment Aug 5, 2026
@dgarske
dgarske merged commit a0da766 into wolfSSL:master Aug 6, 2026
369 of 370 checks passed
@Frauschi
Frauschi deleted the sfhb_reload_fix branch August 6, 2026 06:09
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.

4 participants