ui: use uint32_t as the digit entry number type
What changed, and why it matters
This commit changes how a hardware wallet (Blockstream Jade) turns on-screen digit entries into a numeric value. It replaces a floating-point math function (pow()) with simple integer arithmetic and switches the number type from size_t to uint32_t. The change is defensive: using pow() with floating-point numbers for integer digit values can introduce tiny rounding errors, and size_t is unnecessarily large/wide for this purpose. The commit does not by itself fix a known exploitable bug, but it removes a risky pattern in security-sensitive code that handles BIP85 child mnemonic indexes.
Treat as a hardening/correctness improvement rather than an active vulnerability fix. Review whether get_entry_as_number() and its callers enforce upper bounds on acceptable BIP85 index values, and consider adding explicit range checks and unit tests for multi-digit entries. No urgent patch deployment is indicated solely from this diff.
Security signals we found
Removal of floating-point math (pow()) from integer parsing path
Type narrowing from size_t to uint32_t for digit-entry numeric result
Use of integer-only arithmetic for user-entered BIP85 index
No explicit overflow/range validation added in this patch
Evidence from the diff
The patch modifies get_entry_as_number() in main/ui/digit_entry.c to compute the entered numeric value by iterative base-10 accumulation (val = val * 10 + digit) instead of digit * pow(10, exponent). It also changes the return type and related variables from size_t to uint32_t, updates the header declaration, and adjusts format strings in handle_bip85_mnemonic() to PRIu32. The old pow()-based approach used double arithmetic for what is logically an integer conversion; on some platforms/toolchains this could produce off-by-one values due to floating-point rounding, especially for larger digit counts. The new approach is deterministic, integer-only, and matches the intended 32-bit index space for BIP85. The change is small and partial: it does not add explicit range validation or overflow checks beyond the existing digit loop.
Changed components
main/ui/digit_entry.cmain/ui.hmain/process/mnemonic.cInspect captured patch +8 / −13
diff --git a/main/process/mnemonic.c b/main/process/mnemonic.c
index 4690b0f..8048c2e 100644
--- a/main/process/mnemonic.c
+++ b/main/process/mnemonic.c
@@ -1524,7 +1524,7 @@ void handle_bip85_mnemonic()
make_digit_entry_activity(&digit_entry, "BIP85", "Index #:");
JADE_ASSERT(digit_entry.activity);
- size_t index = 0;
+ uint32_t index = 0;
while (true) {
reset_digit_entry(&digit_entry, "BIP85");
gui_set_current_activity(digit_entry.activity);
@@ -1540,11 +1540,11 @@ void handle_bip85_mnemonic()
// User to confirm
char buf[8];
- const int ret = snprintf(buf, sizeof(buf), "%u", index);
+ const int ret = snprintf(buf, sizeof(buf), "%" PRIu32, index);
JADE_ASSERT(ret > 0 && ret < sizeof(buf));
const char* message[] = { "BIP85 index selected:", buf };
if (await_continueback_activity("BIP85", message, 2, true, "blkstrm.com/bip85")) {
- JADE_LOGI("BIP85 index number selected: %u", index);
+ JADE_LOGI("BIP85 index number selected: %" PRIu32, index);
break;
}
}
diff --git a/main/ui.h b/main/ui.h
index 09f5ee8..150d7b0 100644
--- a/main/ui.h
+++ b/main/ui.h
@@ -169,7 +169,7 @@ void run_keyboard_entry_loop(keyboard_entry_t* kb_entry);
void make_digit_entry_activity(digit_entry_t* digit_entry, const char* title, const char* message);
bool run_digit_entry_loop(digit_entry_t* digit_entry);
void reset_digit_entry(digit_entry_t* digit_entry, const char* title);
-size_t get_entry_as_number(const digit_entry_t* digit_entry);
+uint32_t get_entry_as_number(const digit_entry_t* digit_entry);
// Generic progress-bar
void make_progress_bar(gui_view_node_t* parent, progress_bar_t* progress_bar);
diff --git a/main/ui/digit_entry.c b/main/ui/digit_entry.c
index 81dcbfe..ad024a1 100644
--- a/main/ui/digit_entry.c
+++ b/main/ui/digit_entry.c
@@ -4,8 +4,6 @@
#include "../random.h"
#include "../ui.h"
-#include <math.h>
-
#define CHAR_BACKSPACE '|'
#define CHAR_ENTER '~'
static const char ENTRY_CHARS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', CHAR_BACKSPACE, CHAR_ENTER };
@@ -287,7 +285,7 @@ void reset_digit_entry(digit_entry_t* digit_entry, const char* title)
}
}
-size_t get_entry_as_number(const digit_entry_t* digit_entry)
+uint32_t get_entry_as_number(const digit_entry_t* digit_entry)
{
JADE_ASSERT(digit_entry);
if (digit_entry->entry_type == DIGIT_ENTRY_INDEX) {
@@ -296,14 +294,11 @@ size_t get_entry_as_number(const digit_entry_t* digit_entry)
JADE_ASSERT(digit_entry->selected_digit == DIGIT_ENTRY_SIZE); // entry complete
}
- size_t val = 0;
+ uint32_t val = 0;
for (uint8_t i = 0; i < digit_entry->selected_digit; ++i) {
JADE_ASSERT(digit_entry->digit_status[i] == SET);
- JADE_ASSERT(digit_entry->digit[i] < get_num_digit_entry_chars(digit_entry));
-
- const size_t digit = digit_entry->digit[i];
- const uint8_t exponent = digit_entry->selected_digit - i - 1;
- val += (digit * pow(10, exponent));
+ JADE_ASSERT(digit_entry->digit[i] < NUM_ENTRY_DIGITS);
+ val = val * 10 + digit_entry->digit[i];
}
return val;
Why this scored 29/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.