Summary
In mnemonic_export_qr() (main/process/mnemonic.c), the raw mnemonic entropy (i.e. the seed) is converted into a CompactSeedQR and displayed as on-screen icons. Unlike every other seed-handling path in the codebase, none of this data is zeroized:
uint8_t entropy[BIP32_ENTROPY_LEN_256] (line 83) and qrbuffer[96] (line 91) are stack buffers holding the raw seed, and are never wrapped in SENSITIVE_PUSH/SENSITIVE_POP.
- The QR icons (
icons[i].data, qr_overview.data, allocated with JADE_CALLOC_PREFER_SPIRAM in qrcode.c) encode the full seed, but are released with plain free() — qrcode_freeIcon() (main/qrcode.c:1076) performs no zeroization.
After the export flow the seed therefore remains readable in freed heap and on the stack. This contradicts the hardening added in 1.0.40 ("Clear secrets from memory more diligently after UI display") and the SENSITIVE_PUSH/SENSITIVE_POP discipline used everywhere else (e.g. mnemonic_new(), get_bip85_mnemonic(), import_compactseedqr()).
Related: #180
Suggested fix
- Wrap
entropy and qrbuffer with SENSITIVE_PUSH/SENSITIVE_POP.
- Zeroize icon data before freeing — e.g.
wally_bzero(icon->data, qrcode_get_icon_data_size(icon->width, icon->height)) inside qrcode_freeIcon() (would need #include <wally_crypto.h> added to qrcode.c), and the same for icons[i].data before free() in the cleanup path (mnemonic.c:225-229).
Verification
Confirmed still present on current master (as of 2026-08-04): main/process/mnemonic.c:83 and main/qrcode.c:1076 unchanged. qrcode_freeIcon() has a single caller, so the fix has no impact outside the QR-export flow.
Summary
In
mnemonic_export_qr()(main/process/mnemonic.c), the raw mnemonic entropy (i.e. the seed) is converted into a CompactSeedQR and displayed as on-screen icons. Unlike every other seed-handling path in the codebase, none of this data is zeroized:uint8_t entropy[BIP32_ENTROPY_LEN_256](line 83) andqrbuffer[96](line 91) are stack buffers holding the raw seed, and are never wrapped inSENSITIVE_PUSH/SENSITIVE_POP.icons[i].data,qr_overview.data, allocated withJADE_CALLOC_PREFER_SPIRAMinqrcode.c) encode the full seed, but are released with plainfree()—qrcode_freeIcon()(main/qrcode.c:1076) performs no zeroization.After the export flow the seed therefore remains readable in freed heap and on the stack. This contradicts the hardening added in 1.0.40 ("Clear secrets from memory more diligently after UI display") and the
SENSITIVE_PUSH/SENSITIVE_POPdiscipline used everywhere else (e.g.mnemonic_new(),get_bip85_mnemonic(),import_compactseedqr()).Related: #180
Suggested fix
entropyandqrbufferwithSENSITIVE_PUSH/SENSITIVE_POP.wally_bzero(icon->data, qrcode_get_icon_data_size(icon->width, icon->height))insideqrcode_freeIcon()(would need#include <wally_crypto.h>added toqrcode.c), and the same foricons[i].databeforefree()in the cleanup path (mnemonic.c:225-229).Verification
Confirmed still present on current
master(as of 2026-08-04):main/process/mnemonic.c:83andmain/qrcode.c:1076unchanged.qrcode_freeIcon()has a single caller, so the fix has no impact outside the QR-export flow.