Fix Zcash wallet preparation on unlock
What changed, and why it matters
This commit fixes a bug in a helper function that checks whether a text string is a valid hexadecimal value of the expected length. The changelog frames the user-visible fix as 'preventing Zcash public information from being regenerated on every unlock.' The code change tightens a length check so the function no longer accepts an expected length equal to the maximum buffer size, and it correctly detects strings that are exactly one character longer than the requested length. The security relevance is indirect: a malformed or over-long hex string could previously pass validation in edge cases, which might affect Zcash wallet preparation or other callers.
Treat as a routine bug-fix/hardening commit. Review all callers of IsHexStringWithLen to confirm the new length semantics do not break legitimate inputs, and verify the Zcash unlock path now avoids redundant regeneration of public information. No urgent security response is indicated by the diff alone.
Security signals we found
Input validation hardening in a shared utility function
Changelog describes a Zcash wallet preparation/unlock behavior fix
Potential off-by-one length check corrected
No explicit CVE, advisory, or researcher attribution in commit materials
Evidence from the diff
IsHexStringWithLen() validates hex strings. Previously it computed maxLen = expectedLen ? expectedLen : HEX_STRING_MAX_LENGTH, then scanned up to maxLen+1 and rejected if len > maxLen. This had two issues: (1) if expectedLen == HEX_STRING_MAX_LENGTH, maxLen equaled the max and the scan length was maxLen+1, so a string of exactly maxLen+1 characters could be accepted and then compared against expectedLen; (2) the logic was slightly confusing about whether expectedLen==0 means ‘any length up to max’ or ‘exact length check disabled.’ The patch rejects expectedLen >= HEX_STRING_MAX_LENGTH outright, sets scanLen = expectedLen ? expectedLen+1 : HEX_STRING_MAX_LENGTH, and treats len == scanLen as ‘too long.’ This is a defensive hardening of input validation. The connection to ‘Zcash public information regenerated on every unlock’ is described only in the changelog, not visible in the diff itself.
Changed components
src/utils/user_utils.csrc/utils/user_utils.hZcash wallet unlock/preparation flow (per changelog)Inspect captured patch +8 / −4
diff --git a/CHANGELOG-ZH.md b/CHANGELOG-ZH.md
index 75d8e8a..42fe8c4 100644
--- a/CHANGELOG-ZH.md
+++ b/CHANGELOG-ZH.md
@@ -11,6 +11,7 @@
3. 修复 Zcash 屏蔽签名可能使用非所选账户的问题
4. 拒绝显示金额总计溢出或不平衡的 Zcash PCZT
5. 为不支持 PCZT v2 的钱包保留 PCZT v1 签名响应格式
+6. 修复每次解锁时重复生成 Zcash 公共信息的问题
## 3.0.0 (2026-7-20)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f0196a..227da32 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@
3. Restricted Zcash shielded signing to the selected account
4. Rejected Zcash PCZTs whose display amount totals overflow or do not balance
5. Preserved PCZT v1 signing responses for wallets that do not support PCZT v2
+6. Prevented Zcash public information from being regenerated on every unlock
## 3.0.0 (2026-07-20)
diff --git a/src/utils/user_utils.c b/src/utils/user_utils.c
index 7426fdb..470f917 100644
--- a/src/utils/user_utils.c
+++ b/src/utils/user_utils.c
@@ -85,12 +85,12 @@ bool IsHexStringWithLen(const char *value, size_t expectedLen)
if (value == NULL) {
return false;
}
- size_t maxLen = expectedLen == 0 ? HEX_STRING_MAX_LENGTH : expectedLen;
- size_t len = strnlen_s(value, maxLen + 1);
- if (len == 0 || (len % 2) != 0) {
+ if (expectedLen >= HEX_STRING_MAX_LENGTH) {
return false;
}
- if (len > maxLen) {
+ size_t scanLen = expectedLen == 0 ? HEX_STRING_MAX_LENGTH : expectedLen + 1;
+ size_t len = strnlen_s(value, scanLen);
+ if (len == 0 || len == scanLen || (len % 2) != 0) {
return false;
}
if (expectedLen != 0 && len != expectedLen) {
diff --git a/src/utils/user_utils.h b/src/utils/user_utils.h
index ceb1233..31bece5 100644
--- a/src/utils/user_utils.h
+++ b/src/utils/user_utils.h
@@ -19,6 +19,8 @@ void ByteArrayToHexStr(uint8_t *array, uint32_t len, char *hex);
bool CheckEntropy(const uint8_t *array, uint32_t len);
bool CheckAllFF(const uint8_t *array, uint32_t len);
bool CheckAllZero(const uint8_t *array, uint32_t len);
+/// @brief Check whether value is an even-length hexadecimal string.
+/// @param expectedLen Required length, or zero to accept any supported length.
bool IsHexStringWithLen(const char *value, size_t expectedLen);
void RemoveFormatChar(char *str);
void ArrayRandom(char *words, char *out, int count);
Why this scored 37/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.