feat(legacy): Make Bitcoin path checks same as in core.

Andrew Kozlik 2 years ago committed by Martin Milata
parent f0a48c2f18
commit befd38217e

@ -0,0 +1 @@
Make Bitcoin path checks as strict as in Trezor T.

@ -487,231 +487,204 @@ static bool check_cointype(const CoinInfo *coin, uint32_t slip44, bool full) {
bool coin_path_check(const CoinInfo *coin, InputScriptType script_type, bool coin_path_check(const CoinInfo *coin, InputScriptType script_type,
uint32_t address_n_count, const uint32_t *address_n, uint32_t address_n_count, const uint32_t *address_n,
bool has_multisig, CoinPathCheckLevel level) { bool has_multisig, bool full_check) {
// For level BASIC this function checks that a coin without strong replay // This function checks that the path is a recognized path for the given coin.
// protection doesn't access paths that are known to be used by another coin. // Used by GetAddress to prevent ransom attacks where a user could be coerced
// Used by SignTx to ensure that a user cannot be coerced into signing a // to use an address with an unenumerable path and used by SignTx to ensure
// testnet transaction or a Litecoin transaction which in fact spends Bitcoin. // that a user cannot be coerced into signing a testnet transaction or a
// For level KNOWN this function checks that the path is a recognized path for // Litecoin transaction which in fact spends Bitcoin. If full_check is true,
// the given coin. Used by GetAddress to prevent ransom attacks where a user // then this function also checks that the path fully matches the script type
// could be coerced to use an address with an unenumerable path. // and coin type. This is used to determine whether a warning should be shown.
// For level SCRIPT_TYPE this function makes the same checks as in level
// KNOWN, but includes script type checks. if (address_n_count == 0) {
return false;
const bool check_known = (level >= CoinPathCheckLevel_KNOWN); }
const bool check_script_type = (level >= CoinPathCheckLevel_SCRIPT_TYPE);
bool valid = true; bool valid = true;
// m/44' : BIP44 Legacy // m/44' : BIP44 Legacy
// m / purpose' / coin_type' / account' / change / address_index // m / purpose' / coin_type' / account' / change / address_index
if (address_n_count > 0 && address_n[0] == (PATH_HARDENED + 44)) { if (address_n[0] == PATH_HARDENED + 44) {
if (check_known) { valid = valid && (address_n_count == 5);
valid = valid && (address_n_count == 5); valid = valid && check_cointype(coin, address_n[1], full_check);
} else { valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && (address_n_count >= 2); valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
} valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && check_cointype(coin, address_n[1], check_known); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (check_script_type) { if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDADDRESS); valid = valid && (script_type == InputScriptType_SPENDADDRESS);
valid = valid && (!has_multisig); valid = valid && (!has_multisig);
} }
if (check_known) {
valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
}
return valid; return valid;
} }
if (address_n_count > 0 && address_n[0] == (PATH_HARDENED + 45)) { if (address_n[0] == PATH_HARDENED + 45) {
if (check_script_type) {
valid = valid && has_multisig;
}
if (address_n_count == 4) { if (address_n_count == 4) {
// m/45' - BIP45 Copay Abandoned Multisig P2SH // m/45' - BIP45 Copay Abandoned Multisig P2SH
// m / purpose' / cosigner_index / change / address_index // m / purpose' / cosigner_index / change / address_index
// Patterns without a coin_type field must be treated as Bitcoin paths. // Patterns without a coin_type field must be treated as Bitcoin paths.
valid = valid && check_cointype(coin, SLIP44_BITCOIN, check_known); valid = valid && check_cointype(coin, SLIP44_BITCOIN, false);
if (check_script_type) { valid = valid && (address_n[1] <= 100);
valid = valid && (script_type == InputScriptType_SPENDMULTISIG); valid = valid && (address_n[2] <= PATH_MAX_CHANGE);
} valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX);
if (check_known) {
valid = valid && (address_n[1] <= 100);
valid = valid && (address_n[2] <= PATH_MAX_CHANGE);
valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX);
}
} else if (address_n_count == 5) { } else if (address_n_count == 5) {
// Unchained Capital compatibility pattern. Will be removed in the // Unchained Capital compatibility pattern. Will be removed in the
// future. // future.
// m / 45' / coin_type' / account' / [0-1000000] / address_index // m / 45' / coin_type' / account' / [0-1000000] / address_index
valid = valid && check_cointype(coin, address_n[1], check_known); valid = valid && check_cointype(coin, address_n[1], full_check);
if (check_script_type) { valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && (script_type == InputScriptType_SPENDADDRESS || valid =
script_type == InputScriptType_SPENDMULTISIG); valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
} valid = valid && (address_n[3] <= 1000000);
if (check_known) { valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= 1000000);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
}
} else if (address_n_count == 6) { } else if (address_n_count == 6) {
// Unchained Capital compatibility pattern. Will be removed in the // Unchained Capital compatibility pattern. Will be removed in the
// future. // future.
// m/45'/coin_type'/account'/[0-1000000]/change/address_index // m/45'/coin_type'/account'/[0-1000000]/change/address_index
// m/45'/coin_type/account/[0-1000000]/change/address_index // m/45'/coin_type/account/[0-1000000]/change/address_index
valid = valid && valid = valid &&
check_cointype(coin, PATH_HARDENED | address_n[1], check_known); check_cointype(coin, PATH_HARDENED | address_n[1], full_check);
if (check_script_type) { valid = valid && ((address_n[1] & PATH_HARDENED) ==
valid = valid && (script_type == InputScriptType_SPENDADDRESS || (address_n[2] & PATH_HARDENED));
script_type == InputScriptType_SPENDMULTISIG); valid =
} valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
if (check_known) { valid = valid && (address_n[3] <= 1000000);
valid = valid && ((address_n[1] & PATH_HARDENED) == valid = valid && (address_n[4] <= PATH_MAX_CHANGE);
(address_n[2] & PATH_HARDENED)); valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= 1000000);
valid = valid && (address_n[4] <= PATH_MAX_CHANGE);
valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX);
}
} else { } else {
if (check_known) { return false;
return false; }
}
if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDADDRESS ||
script_type == InputScriptType_SPENDMULTISIG);
valid = valid && has_multisig;
} }
return valid; return valid;
} }
// m/48' - BIP48 Copay Multisig P2SH if (address_n[0] == PATH_HARDENED + 48) {
// m / purpose' / coin_type' / account' / change / address_index valid = valid && (address_n_count == 5 || address_n_count == 6);
// Electrum: valid = valid && check_cointype(coin, address_n[1], full_check);
// m / purpose' / coin_type' / account' / type' / change / address_index valid = valid && (address_n[2] & PATH_HARDENED);
if (address_n_count > 0 && address_n[0] == (PATH_HARDENED + 48)) { valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
if (check_known) { if (address_n_count == 5) {
valid = valid && (address_n_count == 5 || address_n_count == 6); // [OBSOLETE] m/48' Copay Multisig P2SH
} else { // m / purpose' / coin_type' / account' / change / address_index
valid = valid && (address_n_count >= 2); // NOTE: this pattern is not recognized by trezor-core
} valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && check_cointype(coin, address_n[1], check_known); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (check_script_type) { if (full_check) {
valid = valid && has_multisig; valid = valid && has_multisig;
// we do not support Multisig with Taproot yet valid = valid && (script_type == InputScriptType_SPENDMULTISIG);
valid = valid && (script_type == InputScriptType_SPENDMULTISIG || }
script_type == InputScriptType_SPENDP2SHWITNESS || } else if (address_n_count == 6) {
script_type == InputScriptType_SPENDWITNESS); // BIP-48:
} // m / purpose' / coin_type' / account' / type' / change / address_index
if (check_known) { valid = valid && (address_n[3] & PATH_HARDENED);
valid = valid && (address_n[2] & PATH_HARDENED); uint32_t type = address_n[3] & PATH_UNHARDEN_MASK;
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT); valid = valid && (type <= 2);
if (address_n_count == 5) { valid = valid && (type == 0 || coin->has_segwit);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE); valid = valid && (address_n[4] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX);
} else if (address_n_count == 6) { if (full_check) {
valid = valid && (address_n[3] & PATH_HARDENED); valid = valid && has_multisig;
valid = valid && ((address_n[3] & 0x7fffffff) <= 3); switch (type) {
valid = valid && (address_n[4] <= PATH_MAX_CHANGE); case 0:
valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (script_type == InputScriptType_SPENDMULTISIG ||
} else { script_type == InputScriptType_SPENDADDRESS);
return false; break;
case 1:
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
break;
case 2:
valid = valid && (script_type == InputScriptType_SPENDWITNESS);
break;
default:
return false;
}
} }
} else {
return false;
} }
return valid; return valid;
} }
// m/49' : BIP49 SegWit // m/49' : BIP49 SegWit
// m / purpose' / coin_type' / account' / change / address_index // m / purpose' / coin_type' / account' / change / address_index
if (address_n_count > 0 && address_n[0] == (PATH_HARDENED + 49)) { if (address_n[0] == PATH_HARDENED + 49) {
valid = valid && coin->has_segwit; valid = valid && coin->has_segwit;
if (check_known) { valid = valid && (address_n_count == 5);
valid = valid && (address_n_count == 5); valid = valid && check_cointype(coin, address_n[1], full_check);
} else { valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && (address_n_count >= 2); valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
} valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && check_cointype(coin, address_n[1], check_known); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (check_script_type) { if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS); valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
} }
if (check_known) {
valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
}
return valid; return valid;
} }
// m/84' : BIP84 Native SegWit // m/84' : BIP84 Native SegWit
// m / purpose' / coin_type' / account' / change / address_index // m / purpose' / coin_type' / account' / change / address_index
if (address_n_count > 0 && address_n[0] == (PATH_HARDENED + 84)) { if (address_n[0] == PATH_HARDENED + 84) {
valid = valid && coin->has_segwit; valid = valid && coin->has_segwit;
valid = valid && (coin->bech32_prefix != NULL); valid = valid && (coin->bech32_prefix != NULL);
if (check_known) { valid = valid && (address_n_count == 5);
valid = valid && (address_n_count == 5); valid = valid && check_cointype(coin, address_n[1], full_check);
} else { valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && (address_n_count >= 2); valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
} valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && check_cointype(coin, address_n[1], check_known); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (check_script_type) { if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDWITNESS); valid = valid && (script_type == InputScriptType_SPENDWITNESS);
} }
if (check_known) {
valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
}
return valid; return valid;
} }
// m/86' : BIP86 Taproot // m/86' : BIP86 Taproot
// m / purpose' / coin_type' / account' / change / address_index // m / purpose' / coin_type' / account' / change / address_index
if (address_n_count > 0 && address_n[0] == (PATH_HARDENED + 86)) { if (address_n[0] == PATH_HARDENED + 86) {
valid = valid && coin->has_taproot; valid = valid && coin->has_taproot;
valid = valid && (coin->bech32_prefix != NULL); valid = valid && (coin->bech32_prefix != NULL);
if (check_known) { valid = valid && (address_n_count == 5);
valid = valid && (address_n_count == 5); valid = valid && check_cointype(coin, address_n[1], full_check);
} else { valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && (address_n_count >= 2); valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
} valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && check_cointype(coin, address_n[1], check_known); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (check_script_type) { if (full_check) {
// we do not support Multisig with Taproot yet // we do not support Multisig with Taproot yet
valid = valid && !has_multisig; valid = valid && !has_multisig;
valid = valid && (script_type == InputScriptType_SPENDTAPROOT); valid = valid && (script_type == InputScriptType_SPENDTAPROOT);
} }
if (check_known) {
valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
}
return valid; return valid;
} }
// Green Address compatibility pattern. Will be removed in the future. // Green Address compatibility pattern. Will be removed in the future.
// m / [1,4] / address_index // m / [1,4] / address_index
if (address_n_count > 0 && (address_n[0] == 1 || address_n[0] == 4)) { if (address_n[0] == 1 || address_n[0] == 4) {
valid = valid && (coin->coin_type == SLIP44_BITCOIN); valid = valid && (coin->coin_type == SLIP44_BITCOIN);
if (check_known) { valid = valid && (address_n_count == 2);
valid = valid && (address_n_count == 2); valid = valid && (address_n[1] <= PATH_MAX_ADDRESS_INDEX);
valid = valid && (address_n[1] <= PATH_MAX_ADDRESS_INDEX); if (full_check) {
valid = valid && (script_type != InputScriptType_SPENDTAPROOT);
} }
return valid; return valid;
} }
// Green Address compatibility pattern. Will be removed in the future. // Green Address compatibility pattern. Will be removed in the future.
// m / 3' / [1-100]' / [1,4] / address_index // m / 3' / [1-100]' / [1,4] / address_index
if (address_n_count > 0 && address_n[0] == (PATH_HARDENED + 3)) { if (address_n[0] == PATH_HARDENED + 3) {
valid = valid && (coin->coin_type == SLIP44_BITCOIN); valid = valid && (coin->coin_type == SLIP44_BITCOIN);
if (check_known) { valid = valid && (address_n_count == 4);
valid = valid && (address_n_count == 4); valid = valid && (address_n[1] & PATH_HARDENED);
valid = valid && (address_n[1] & PATH_HARDENED); valid = valid && ((address_n[1] & PATH_UNHARDEN_MASK) <= 100);
valid = valid && ((address_n[1] & 0x7fffffff) <= 100); valid = valid && (address_n[2] == 1 || address_n[2] == 4);
valid = valid && (address_n[2] == 1 || address_n[2] == 4); valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX);
valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX); if (full_check) {
valid = valid && (script_type != InputScriptType_SPENDTAPROOT);
} }
return valid; return valid;
} }
@ -719,41 +692,36 @@ bool coin_path_check(const CoinInfo *coin, InputScriptType script_type,
// Green Address compatibility patterns. Will be removed in the future. // Green Address compatibility patterns. Will be removed in the future.
// m / 1195487518 // m / 1195487518
// m / 1195487518 / 6 / address_index // m / 1195487518 / 6 / address_index
if (address_n_count > 0 && address_n[0] == 1195487518) { if (address_n[0] == 1195487518) {
valid = valid && (coin->coin_type == SLIP44_BITCOIN); valid = valid && (coin->coin_type == SLIP44_BITCOIN);
if (check_known) { if (address_n_count == 3) {
if (address_n_count == 3) { valid = valid && (address_n[1] == 6);
valid = valid && (address_n[1] == 6); valid = valid && (address_n[2] <= PATH_MAX_ADDRESS_INDEX);
valid = valid && (address_n[2] <= PATH_MAX_ADDRESS_INDEX); } else if (address_n_count != 1) {
} else if (address_n_count != 1) { return false;
return false; }
} if (full_check) {
return false;
} }
return valid; return valid;
} }
// Casa compatibility pattern. Will be removed in the future. // Casa compatibility pattern. Will be removed in the future.
// m / 49 / coin_type / account / change / address_index // m / 49 / coin_type / account / change / address_index
if (address_n_count > 0 && address_n[0] == 49) { if (address_n[0] == 49) {
if (check_known) { valid = valid && (address_n_count == 5);
valid = valid && (address_n_count == 5); valid =
} else { valid && check_cointype(coin, PATH_HARDENED | address_n[1], full_check);
valid = valid && (address_n_count >= 2); valid = valid && ((address_n[1] & PATH_HARDENED) == 0);
} valid = valid && (address_n[2] <= PATH_MAX_ACCOUNT);
valid = valid && valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
check_cointype(coin, PATH_HARDENED | address_n[1], check_known); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (check_script_type) { if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS); valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
} }
if (check_known) {
valid = valid && ((address_n[1] & PATH_HARDENED) == 0);
valid = valid && (address_n[2] <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
}
return valid; return valid;
} }
// we allow unknown paths only when a full check is not required // unknown path
return level == CoinPathCheckLevel_BASIC; return false;
} }

@ -32,12 +32,6 @@
#include "messages-bitcoin.pb.h" #include "messages-bitcoin.pb.h"
#include "messages-crypto.pb.h" #include "messages-crypto.pb.h"
typedef enum _CoinPathCheckLevel {
CoinPathCheckLevel_BASIC = 0,
CoinPathCheckLevel_KNOWN = 1,
CoinPathCheckLevel_SCRIPT_TYPE = 2,
} CoinPathCheckLevel;
#define PATH_HARDENED 0x80000000 #define PATH_HARDENED 0x80000000
#define PATH_UNHARDEN_MASK 0x7fffffff #define PATH_UNHARDEN_MASK 0x7fffffff
#define PATH_MAX_ACCOUNT 100 #define PATH_MAX_ACCOUNT 100
@ -85,6 +79,6 @@ int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash);
bool coin_path_check(const CoinInfo *coin, InputScriptType script_type, bool coin_path_check(const CoinInfo *coin, InputScriptType script_type,
uint32_t address_n_count, const uint32_t *address_n, uint32_t address_n_count, const uint32_t *address_n,
bool has_multisig, CoinPathCheckLevel level); bool has_multisig, bool full_check);
#endif #endif

@ -20,6 +20,7 @@
#ifndef __FSM_H__ #ifndef __FSM_H__
#define __FSM_H__ #define __FSM_H__
#include "coins.h"
#include "messages-bitcoin.pb.h" #include "messages-bitcoin.pb.h"
#include "messages-crypto.pb.h" #include "messages-crypto.pb.h"
#include "messages-debug.pb.h" #include "messages-debug.pb.h"
@ -141,5 +142,8 @@ bool fsm_layoutSignMessage(const uint8_t *msg, uint32_t len);
bool fsm_layoutVerifyMessage(const uint8_t *msg, uint32_t len); bool fsm_layoutVerifyMessage(const uint8_t *msg, uint32_t len);
bool fsm_layoutPathWarning(void); bool fsm_layoutPathWarning(void);
bool fsm_checkCoinPath(const CoinInfo *coin, InputScriptType script_type,
uint32_t address_n_count, const uint32_t *address_n,
bool has_multisig, bool show_warning);
#endif #endif

@ -151,24 +151,25 @@ void fsm_msgTxAck(TxAck *msg) {
signing_txack(&(msg->tx)); signing_txack(&(msg->tx));
} }
static bool fsm_checkCoinPath(const CoinInfo *coin, InputScriptType script_type, bool fsm_checkCoinPath(const CoinInfo *coin, InputScriptType script_type,
uint32_t address_n_count, uint32_t address_n_count, const uint32_t *address_n,
const uint32_t *address_n, bool has_multisig) { bool has_multisig, bool show_warning) {
if (!coin_path_check(coin, script_type, address_n_count, address_n, if (coin_path_check(coin, script_type, address_n_count, address_n,
has_multisig, CoinPathCheckLevel_SCRIPT_TYPE)) { has_multisig, true)) {
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict && return true;
!coin_path_check(coin, script_type, address_n_count, address_n, }
has_multisig, CoinPathCheckLevel_KNOWN)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
layoutHome();
return false;
}
if (!fsm_layoutPathWarning()) { if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict &&
layoutHome(); !coin_path_check(coin, script_type, address_n_count, address_n,
return false; has_multisig, false)) {
} fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
return false;
} }
if (show_warning) {
return fsm_layoutPathWarning();
}
return true; return true;
} }
@ -181,6 +182,14 @@ void fsm_msgGetAddress(const GetAddress *msg) {
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
if (!coin) return; if (!coin) return;
if (!fsm_checkCoinPath(coin, msg->script_type, msg->address_n_count,
msg->address_n, msg->has_multisig,
msg->show_display)) {
layoutHome();
return;
}
HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n,
msg->address_n_count, NULL); msg->address_n_count, NULL);
if (!node) return; if (!node) return;
@ -220,11 +229,6 @@ void fsm_msgGetAddress(const GetAddress *msg) {
strlcpy(desc, _("Address:"), sizeof(desc)); strlcpy(desc, _("Address:"), sizeof(desc));
} }
if (!fsm_checkCoinPath(coin, msg->script_type, msg->address_n_count,
msg->address_n, msg->has_multisig)) {
return;
}
uint32_t multisig_xpub_magic = coin->xpub_magic; uint32_t multisig_xpub_magic = coin->xpub_magic;
if (msg->has_multisig && coin->has_segwit) { if (msg->has_multisig && coin->has_segwit) {
if (!msg->has_ignore_xpub_magic || !msg->ignore_xpub_magic) { if (!msg->has_ignore_xpub_magic || !msg->ignore_xpub_magic) {
@ -269,7 +273,8 @@ void fsm_msgSignMessage(const SignMessage *msg) {
if (!coin) return; if (!coin) return;
if (!fsm_checkCoinPath(coin, msg->script_type, msg->address_n_count, if (!fsm_checkCoinPath(coin, msg->script_type, msg->address_n_count,
msg->address_n, false)) { msg->address_n, false, true)) {
layoutHome();
return; return;
} }

@ -746,13 +746,12 @@ static bool fill_input_script_sig(TxInputType *tinput) {
static bool derive_node(TxInputType *tinput) { static bool derive_node(TxInputType *tinput) {
if (!coin_path_check(coin, tinput->script_type, tinput->address_n_count, if (!coin_path_check(coin, tinput->script_type, tinput->address_n_count,
tinput->address_n, tinput->has_multisig, tinput->address_n, tinput->has_multisig, false)) {
CoinPathCheckLevel_BASIC)) {
if (is_replacement) { if (is_replacement) {
fsm_sendFailure( fsm_sendFailure(
FailureType_Failure_ProcessError, FailureType_Failure_ProcessError,
_("Non-standard paths not allowed in replacement transactions.")); _("Non-standard paths not allowed in replacement transactions."));
layoutHome(); signing_abort();
return false; return false;
} }
@ -1324,6 +1323,12 @@ static bool signing_add_input(TxInputType *txinput) {
// hash all input data to check it later (relevant for fee computation) // hash all input data to check it later (relevant for fee computation)
tx_input_check_hash(&hasher_check, txinput); tx_input_check_hash(&hasher_check, txinput);
if (!fsm_checkCoinPath(coin, txinput->script_type, txinput->address_n_count,
txinput->address_n, txinput->has_multisig, true)) {
signing_abort();
return false;
}
if (!fill_input_script_pubkey(coin, &root, txinput)) { if (!fill_input_script_pubkey(coin, &root, txinput)) {
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive scriptPubKey")); _("Failed to derive scriptPubKey"));

@ -174,7 +174,6 @@ def test_attack_path_segwit(client: Client):
) )
@pytest.mark.skip_t1(reason="T1 only prevents using paths known to be altcoins")
def test_invalid_path_fail_asap(client: Client): def test_invalid_path_fail_asap(client: Client):
inp1 = messages.TxInputType( inp1 = messages.TxInputType(
address_n=parse_path("m/0"), address_n=parse_path("m/0"),

Loading…
Cancel
Save