feat: support zcash for keystone nexus
What changed, and why it matters
This commit adds Zcash cryptocurrency support to the Keystone 3 hardware wallet firmware. It registers a new Zcash unified full viewing key type, enables encryption of that key, wires up the user-interface handlers for Zcash transactions, and adjusts build flags so Zcash support is available in non-Bitcoin-only firmware builds. There is no direct evidence in the diff of a security vulnerability; it reads as a feature addition.
No security action required based on this commit alone. Treat as a normal feature review: verify the Zcash key derivation path (M/32'/133'/0') and encrypted UFKV storage logic are correct, and ensure the widened #ifndef BTC_ONLY guard does not expose Zcash code paths in builds that should not include them.
Security signals we found
Feature addition for Zcash support, not a vulnerability fix
Encrypted storage path for Zcash unified full viewing key is introduced
Build configuration guard widened from CYPHERPUNK_VERSION to #ifndef BTC_ONLY
No memory-safety, cryptographic, or authorization flaws visible in the diff
Evidence from the diff
The patch extends the firmware’s chain table with ZCASH_UFVK_ENCRYPTED_0 and a derivation path of M/32’/133’/0’. It changes the guard around Zcash unified full viewing key encryption from CYPHERPUNK_VERSION to the broader #ifndef BTC_ONLY, and restructures the conditional key-processing logic in AccountPublicSavePublicInfo and TempAccountPublicInfo so the encrypted UFVK branch is taken for non-Bitcoin builds. UI code adds REMAPVIEW_ZCASH entries, a Zcash transaction handler, and includes gui_zcash.h under #ifndef BTC_ONLY instead of only under CYPHERPUNK_VERSION. The Rust zcash app imports transparent key types. A trailing whitespace fix is made in a Bitcoin address constants file.
Changed components
rust/apps/zcash/src/lib.rssrc/crypto/account_public_info.csrc/ui/gui_analyze/gui_resolve_ur.csrc/ui/gui_analyze/multi/web3/gui_general_analyze.hsrc/ui/gui_chain/gui_chain.csrc/ui/gui_chain/gui_chain.hui_simulator/simulator_model.hrust/apps/bitcoin/src/addresses/constants.rsInspect captured patch +33 / −13
diff --git a/rust/apps/bitcoin/src/addresses/constants.rs b/rust/apps/bitcoin/src/addresses/constants.rs
index 020afb4..aa2391b 100644
--- a/rust/apps/bitcoin/src/addresses/constants.rs
+++ b/rust/apps/bitcoin/src/addresses/constants.rs
@@ -3,7 +3,7 @@ pub const PUBKEY_ADDRESS_PREFIX_DASH: u8 = 76; // 0x4C
pub const PUBKEY_ADDRESS_PREFIX_DASH_P2SH: u8 = 16; // 0x10
pub const PUBKEY_ADDRESS_PREFIX_BCH: u8 = 0; // 0x00
pub const PUBKEY_ADDRESS_PREFIX_DOGE: u8 = 30; // 0x1E
-pub const PUBKEY_ADDRESS_PREFIX_ZEC_BYTE0: u8 = 0x1C; // 28
+pub const PUBKEY_ADDRESS_PREFIX_ZEC_BYTE0: u8 = 0x1C; // 28
pub const PUBKEY_ADDRESS_PREFIX_ZEC_BYTE1: u8 = 0xB8; // 184
pub const SCRIPT_ADDRESS_PREFIX_BTC: u8 = 5; // 0x05
diff --git a/rust/apps/zcash/src/lib.rs b/rust/apps/zcash/src/lib.rs
index 5107d22..59b0d23 100644
--- a/rust/apps/zcash/src/lib.rs
+++ b/rust/apps/zcash/src/lib.rs
@@ -13,6 +13,7 @@ use alloc::{
use pczt::structs::ParsedPczt;
use zcash_vendor::{
pczt::Pczt,
+ transparent::keys::{NonHardenedChildIndex, TransparentKeyScope},
zcash_keys::keys::{UnifiedAddressRequest, UnifiedFullViewingKey},
zcash_protocol::consensus::{self},
zip32,
diff --git a/src/crypto/account_public_info.c b/src/crypto/account_public_info.c
index abb3b0e..133074e 100644
--- a/src/crypto/account_public_info.c
+++ b/src/crypto/account_public_info.c
@@ -524,6 +524,7 @@ static const ChainItem_t g_chainTable[] = {
{XPUB_TYPE_TON_NATIVE, TON_NATIVE, "ton", "" },
{PUBLIC_INFO_TON_CHECKSUM, TON_CHECKSUM, "ton_checksum", "" },
{XPUB_TYPE_ZEC_TRANSPARENT_LEGACY,SECP256K1, "zec_transparent_legacy", "M/44'/133'/0'" },
+ {ZCASH_UFVK_ENCRYPTED_0, ZCASH_UFVK_ENCRYPTED, "zcash_ufvk_0", "M/32'/133'/0'" },
#endif
#ifdef CYPHERPUNK_VERSION
@@ -919,7 +920,7 @@ int32_t AccountPublicSavePublicInfo(uint8_t accountIndex, const char *password,
if (g_chainTable[i].cryptoKey == TON_CHECKSUM || g_chainTable[i].cryptoKey == TON_NATIVE) {
continue;
}
-#ifdef CYPHERPUNK_VERSION
+#ifndef BTC_ONLY
//encrypt zcash ufvk
if (g_chainTable[i].cryptoKey == ZCASH_UFVK_ENCRYPTED) {
char* zcashUfvk = NULL;
@@ -933,14 +934,12 @@ int32_t AccountPublicSavePublicInfo(uint8_t accountIndex, const char *password,
memcpy_s(iv_bytes, 16, iv_response->data, 16);
free_simple_response_u8(iv_response);
xPubResult = rust_aes256_cbc_encrypt(zcashUfvk, password, iv_bytes, 16);
- } else {
- xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
- }
-#endif
-#ifdef WEB3_VERSION
- if (g_chainTable[i].cryptoKey == BIP32_ED25519 && isSlip39) {
+#ifdef WEB3_VERSION
+ } else if (g_chainTable[i].cryptoKey == BIP32_ED25519 && isSlip39) {
xPubResult = cardano_get_pubkey_by_slip23(seed, seedLen, g_chainTable[i].path);
- } else {
+#endif
+ }
+ else {
xPubResult = ProcessKeyType(seed, seedLen, g_chainTable[i].cryptoKey, g_chainTable[i].path, icarusMasterKey, ledgerBitbox02Key);
}
#endif
@@ -1086,7 +1085,7 @@ int32_t TempAccountPublicInfo(uint8_t accountIndex, const char *password, bool s
if (g_chainTable[i].cryptoKey == TON_CHECKSUM || g_chainTable[i].cryptoKey == TON_NATIVE) {
continue;
}
-#ifdef CYPHERPUNK_VERSION
+#ifndef BTC_ONLY
//encrypt zcash ufvk
if (g_chainTable[i].cryptoKey == ZCASH_UFVK_ENCRYPTED) {
char* zcashUfvk = NULL;
diff --git a/src/ui/gui_analyze/gui_resolve_ur.c b/src/ui/gui_analyze/gui_resolve_ur.c
index d9232fc..fb627c3 100644
--- a/src/ui/gui_analyze/gui_resolve_ur.c
+++ b/src/ui/gui_analyze/gui_resolve_ur.c
@@ -56,6 +56,7 @@ static SetChainData_t g_chainViewArray[] = {
{REMAPVIEW_AVAX, (SetChainDataFunc)GuiSetAvaxUrData},
{REMAPVIEW_IOTA, (SetChainDataFunc)GuiSetIotaUrData},
{REMAPVIEW_IOTA_SIGN_MESSAGE_HASH, (SetChainDataFunc)GuiSetIotaUrData},
+ {REMAPVIEW_ZCASH, (SetChainDataFunc)GuiSetZcashUrData},
#endif
};
diff --git a/src/ui/gui_analyze/multi/web3/gui_general_analyze.h b/src/ui/gui_analyze/multi/web3/gui_general_analyze.h
index c508a4e..583019d 100644
--- a/src/ui/gui_analyze/multi/web3/gui_general_analyze.h
+++ b/src/ui/gui_analyze/multi/web3/gui_general_analyze.h
@@ -173,6 +173,13 @@
GuiGetAvaxGUIData,\
NULL,\
FreeAvaxMemory,\
+ },\
+ { \
+ REMAPVIEW_ZCASH, \
+ "{\"name\":\"zcash_page\",\"type\":\"custom_container\",\"pos\":[36,0],\"size\":[408,900],\"bg_color\":0,\"custom_show_func\":\"GuiZcashOverview\"}", \
+ GuiGetZcashGUIData, \
+ NULL, \
+ FreeZcashMemory, \
}
#endif
#endif
diff --git a/src/ui/gui_chain/gui_chain.c b/src/ui/gui_chain/gui_chain.c
index 0194bd3..ad79f99 100644
--- a/src/ui/gui_chain/gui_chain.c
+++ b/src/ui/gui_chain/gui_chain.c
@@ -100,6 +100,7 @@ static const ViewHandlerEntry g_viewHandlerMap[] = {
{TonTx, GuiGetTonSignQrCodeData, NULL, GuiGetTonCheckResult, CHAIN_TON, REMAPVIEW_TON},
{TonSignProof, GuiGetTonProofSignQrCodeData, NULL, GuiGetTonCheckResult, CHAIN_TON, REMAPVIEW_TON_SIGNPROOF},
+ {ZcashTx, GuiGetZcashSignQrCodeData, NULL, GuiGetZcashCheckResult, CHAIN_ZCASH, REMAPVIEW_ZCASH},
#endif
#ifdef CYPHERPUNK_VERSION
diff --git a/src/ui/gui_chain/gui_chain.h b/src/ui/gui_chain/gui_chain.h
index 3ebfd2a..19201b9 100644
--- a/src/ui/gui_chain/gui_chain.h
+++ b/src/ui/gui_chain/gui_chain.h
@@ -4,6 +4,7 @@
#include "gui_animating_qrcode.h"
#include "gui_btc.h"
#ifndef BTC_ONLY
+#include "gui_zcash.h"
#ifdef WEB3_VERSION
#include "gui_eth.h"
#include "gui_eth_batch_tx_widgets.h"
@@ -20,7 +21,6 @@
#include "gui_avax.h"
#include "gui_iota.h"
#else
-#include "gui_zcash.h"
#include "gui_monero.h"
#endif
#endif
@@ -92,8 +92,11 @@ typedef enum {
// cosmos end
#endif
-#ifdef CYPHERPUNK_VERSION
+#ifndef BTC_ONLY
CHAIN_ZCASH,
+#endif
+
+#ifdef CYPHERPUNK_VERSION
CHAIN_XMR,
#endif
CHAIN_BUTT,
@@ -130,6 +133,7 @@ typedef enum {
REMAPVIEW_TON,
REMAPVIEW_TON_SIGNPROOF,
REMAPVIEW_AVAX,
+ REMAPVIEW_ZCASH,
#endif
#ifdef CYPHERPUNK_VERSION
diff --git a/ui_simulator/simulator_model.h b/ui_simulator/simulator_model.h
index ba74f25..a412900 100644
--- a/ui_simulator/simulator_model.h
+++ b/ui_simulator/simulator_model.h
@@ -247,7 +247,14 @@ extern bool g_reboot;
GuiGetAvaxGUIData, \
NULL, \
FreeAvaxMemory, \
- },
+ },\
+ { \
+ REMAPVIEW_ZCASH, \
+ PC_SIMULATOR_PATH "/page_zcash.json", \
+ GuiGetZcashGUIData, \
+ NULL, \
+ FreeZcashMemory, \
+ }
#endif
#endif
\ No newline at end of file
Why this scored 12/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.