qr: fix error handling for qr export
What changed, and why it matters
This commit fixes error handling in the QR-code backup feature of a hardware wallet. Previously, if generating QR fragments or scanning the verification QR failed, the code could continue with invalid data and wrongly tell the user the backup was verified. The patch now aborts on generation failure and only marks the backup as verified when the camera scan actually succeeds and matches the expected secret.
Review the broader mnemonic export and verification flow for any other unchecked return values, and confirm that JADE_ASSERT behavior in production firmware safely halts the device rather than exposing sensitive state.
Security signals we found
Unchecked return value from qrcode_toFragmentsIcons() could lead to use of uninitialized/invalid icon data
Unchecked return value from jade_camera_scan_qr() could allow a failed scan to be treated as a mismatch rather than an error
Verification flag could be set inconsistently if error paths were not clearly separated
Evidence from the diff
In main/process/mnemonic.c, two unchecked return values are now handled. qrcode_toFragmentsIcons() is wrapped in JADE_ASSERT(), so a fragment-generation failure triggers an assertion/abort instead of leaving icons/num_icons in an undefined state. jade_camera_scan_qr()’s bool return is now stored and checked; without this, a failed scan could leave qr_data.len == 0, causing the subsequent entropy comparison to fail and the function to return with export_qr_verified still false—but the user-facing message flow could still be confusing, and the prior code did not distinguish ‘scan failed’ from ‘scan succeeded but data mismatch’. The fix ensures verification is recorded only when the scan succeeds and the entropy matches.
Changed components
main/process/mnemonic.cQR-code mnemonic export flowQR-code scan verification flowInspect captured patch +4 / −3
diff --git a/main/process/mnemonic.c b/main/process/mnemonic.c
index fcb6edd..db726f9 100644
--- a/main/process/mnemonic.c
+++ b/main/process/mnemonic.c
@@ -112,7 +112,7 @@ static bool mnemonic_export_qr(const char* mnemonic, bool* export_qr_verified)
size_t num_icons = 0;
const bool show_grid = true;
const uint8_t expected_grid_size = (qrcode_version == 1) ? 3 : 5;
- qrcode_toFragmentsIcons(&qrcode, fragment_target_size, show_grid, &icons, &num_icons);
+ JADE_ASSERT(qrcode_toFragmentsIcons(&qrcode, fragment_target_size, show_grid, &icons, &num_icons));
JADE_ASSERT(num_icons == expected_grid_size * expected_grid_size);
// Show the overview and magnified fragments, and when the user
@@ -201,8 +201,9 @@ static bool mnemonic_export_qr(const char* mnemonic, bool* export_qr_verified)
// Verify QR by scanning it back
qr_data_t qr_data = { .len = 0 };
- jade_camera_scan_qr(&qr_data, "Scan QR to verify", QR_GUIDE_SHOW, "blkstrm.com/seedqr");
- if (qr_data.len == entropy_len && !memcmp(qr_data.data, entropy, entropy_len)) {
+ const bool scan_success
+ = jade_camera_scan_qr(&qr_data, "Scan QR to verify", QR_GUIDE_SHOW, "blkstrm.com/seedqr");
+ if (scan_success && qr_data.len == entropy_len && !memcmp(qr_data.data, entropy, entropy_len)) {
// QR Code scanned, and it matched expected entropy
await_message("QR Code Verified");
*export_qr_verified = true;
Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.