fix: regenerate missing zcash public info
What changed, and why it matters
This commit adds a safety check for the Keystone 3 hardware wallet's Zcash account setup. If the stored Zcash public key information is missing or not a valid hex string, the wallet now regenerates it. This prevents the wallet from trying to use corrupted or incomplete Zcash key data, which could cause errors or display wrong addresses. It is a defensive fix, not a clear exploit patch.
Treat as a hardening/fix commit. Review whether other coin public key entries need similar validation, and verify that regeneration does not overwrite user data or introduce side effects. No immediate incident response is indicated by the diff alone.
Security signals we found
Defensive validation of stored cryptographic public key material
Detection of missing or malformed Zcash UFVK encrypted data
Triggering regeneration when key material fails format validation
Use of bounded string length check (`strnlen_s`) to avoid overflow
Evidence from the diff
In AccountPublicInfoSwitch(), after the existing public-key regeneration logic, a new block (under CYPHERPUNK_VERSION) checks whether Zcash is supported for the current mnemonic. If so, it fetches ZCASH_UFVK_ENCRYPTED_0 and validates it with a new IsHexString() helper. If the value is NULL, empty, odd-length, or contains non-hex characters, it sets regeneratePubKey = true, causing the public key info to be regenerated. The fix addresses a state where Zcash’s encrypted unified full viewing key (UFVK) could be absent or malformed without triggering regeneration.
Changed components
src/crypto/account_public_info.cZcash account public key derivation (CYPHERPUNK_VERSION build)Account public info switch / regeneration flowInspect captured patch +29 / −0
diff --git a/src/crypto/account_public_info.c b/src/crypto/account_public_info.c
index 68203fc..af2f017 100644
--- a/src/crypto/account_public_info.c
+++ b/src/crypto/account_public_info.c
@@ -58,6 +58,7 @@ static uint32_t GetTemplateWalletValue(const char* walletName, const char* key);
static void SetTemplateWalletValue(const char* walletName, const char* key, uint32_t value);
static void CleanupJson(cJSON* json);
static void FreePublicKeyRam(void);
+static bool IsHexString(const char *value);
static void PrintInfo(void);
static void SetIsTempAccount(bool isTemp);
@@ -978,6 +979,14 @@ int32_t AccountPublicInfoSwitch(uint8_t accountIndex, const char *password, bool
} else if (ret == ERR_GENERAL_FAIL) {
regeneratePubKey = true;
}
+#ifdef CYPHERPUNK_VERSION
+ if (!regeneratePubKey && IsZcashSupportedForCurrentMnemonic()) {
+ char *zcashEncrypted = GetCurrentAccountPublicKey(ZCASH_UFVK_ENCRYPTED_0);
+ if (!IsHexString(zcashEncrypted)) {
+ regeneratePubKey = true;
+ }
+ }
+#endif
}
if (regeneratePubKey) {
@@ -995,6 +1004,26 @@ int32_t AccountPublicInfoSwitch(uint8_t accountIndex, const char *password, bool
return ret;
}
+static bool IsHexString(const char *value)
+{
+ if (value == NULL) {
+ return false;
+ }
+ size_t len = strnlen_s(value, PUB_KEY_MAX_LENGTH);
+ if (len == 0 || (len % 2) != 0) {
+ return false;
+ }
+ for (size_t i = 0; i < len; i++) {
+ char c = value[i];
+ if (!((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F'))) {
+ return false;
+ }
+ }
+ return true;
+}
+
static void SetIsTempAccount(bool isTemp)
{
g_isTempAccount = isTemp;
Why this scored 42/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.