What changed, and why it matters
This commit adds support for the Cardano (ADA) cryptocurrency to the Keystone hardware wallet's 'Keystone Connect' feature. It introduces a new derivation path and a helper function to format ADA extended public keys for wallet connection. There is no indication in the commit of a security bug or fix; it appears to be a routine feature addition.
No security action required. Treat as a normal feature commit. If reviewing for completeness, verify that XPUB_TYPE_ADA_0 is properly defined elsewhere and that the 64-byte key assumption matches the rest of the Cardano implementation.
Security signals we found
No security-related signals observed in the diff
Feature addition for new blockchain support (Cardano/ADA)
Existing input validation preserved (key length check in generate_ada_key)
Evidence from the diff
The patch extends the Keystone Connect wallet integration to include Cardano (ADA). In rust/apps/wallets/src/keystone_connect.rs it adds the ADA_PREFIX constant (1852’/1815’/0’), includes it in path matching, and adds a generate_ada_key() function that constructs a CryptoHDKey from a 64-byte extended public key (split into 32-byte chain code and 32-byte public key). In src/ui/gui_wallet/multi/web3/gui_wallet.c it adds XPUB_TYPE_ADA_0 to the list of xpubs exported for the wallet connection data. No security-relevant changes such as bounds checks, cryptographic fixes, or permission changes are evident beyond the existing key-length validation.
Changed components
rust/apps/wallets/src/keystone_connect.rssrc/ui/gui_wallet/multi/web3/gui_wallet.cKeystone Connect wallet integrationCardano (ADA) key derivation and exportInspect captured patch +36 / −0
diff --git a/rust/apps/wallets/src/keystone_connect.rs b/rust/apps/wallets/src/keystone_connect.rs
index eb5ecdf..d06732e 100644
--- a/rust/apps/wallets/src/keystone_connect.rs
+++ b/rust/apps/wallets/src/keystone_connect.rs
@@ -36,6 +36,7 @@ const XRP_PREFIX: &str = "44'/144'/0'";
const LTC_PREFIX: &str = "49'/2'/0'";
const LTC_NATIVE_SEGWIT_PREFIX: &str = "84'/2'/0'";
const ZEC_PREFIX: &str = "44'/133'/0'";
+const ADA_PREFIX: &str = "1852'/1815'/0'";
pub fn generate_crypto_multi_accounts(
master_fingerprint: [u8; 4],
@@ -59,6 +60,7 @@ pub fn generate_crypto_multi_accounts(
LTC_NATIVE_SEGWIT_PREFIX.to_string(),
ZEC_PREFIX.to_string(),
];
+ let ada_keys = [ADA_PREFIX.to_string()];
for ele in extended_public_keys {
match ele.get_path() {
_path if _path.to_string().to_lowercase().eq(ZEC_PREFIX) => {
@@ -87,6 +89,9 @@ pub fn generate_crypto_multi_accounts(
false,
)?);
}
+ _path if ada_keys.contains(&_path.to_string().to_lowercase()) => {
+ keys.push(generate_ada_key(master_fingerprint, ele)?);
+ }
_path if _path.to_string().to_lowercase().eq(ETH_STANDARD_PREFIX) => {
keys.push(generate_k1_normal_key(
master_fingerprint,
@@ -220,6 +225,36 @@ fn generate_k1_normal_key(
))
}
+fn generate_ada_key(mfp: [u8; 4], key: ExtendedPublicKey) -> URResult<CryptoHDKey> {
+ if key.get_key().len() != 64 {
+ return Err(URError::UrEncodeError("Invalid ADA key length".to_string()));
+ }
+ let chain_code = key.get_key()[..32].to_vec();
+ let public_key = key.get_key()[32..].to_vec();
+ let path = key.get_path();
+ let key_path = CryptoKeyPath::new(
+ path.into_iter()
+ .map(|v| match v {
+ ChildNumber::Normal { index } => get_path_component(Some(*index), false),
+ ChildNumber::Hardened { index } => get_path_component(Some(*index), true),
+ })
+ .collect::<URResult<Vec<PathComponent>>>()?,
+ Some(mfp),
+ Some(3),
+ );
+ Ok(CryptoHDKey::new_extended_key(
+ Some(false),
+ public_key,
+ Some(chain_code),
+ None,
+ Some(key_path),
+ None,
+ None,
+ Some("Keystone".to_string()),
+ None,
+ ))
+}
+
fn generate_eth_ledger_live_key(
mfp: [u8; 4],
key: ExtendedPublicKey,
diff --git a/src/ui/gui_wallet/multi/web3/gui_wallet.c b/src/ui/gui_wallet/multi/web3/gui_wallet.c
index a5aa224..cd44599 100644
--- a/src/ui/gui_wallet/multi/web3/gui_wallet.c
+++ b/src/ui/gui_wallet/multi/web3/gui_wallet.c
@@ -471,6 +471,7 @@ UREncodeResult *GuiGetKeystoneConnectWalletDataSlip39(void)
{.path = GetXPubPath(XPUB_TYPE_XRP), .chainType = XPUB_TYPE_XRP},
{.path = GetXPubPath(XPUB_TYPE_LTC), .chainType = XPUB_TYPE_LTC},
{.path = GetXPubPath(XPUB_TYPE_LTC_NATIVE_SEGWIT), .chainType = XPUB_TYPE_LTC_NATIVE_SEGWIT},
+ {.path = GetXPubPath(XPUB_TYPE_ADA_0), .chainType = XPUB_TYPE_ADA_0},
};
ExtendedPublicKey keys[NUMBER_OF_ARRAYS(chainPaths)];
uint8_t mfp[4] = {0};
Why this scored 17/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.