mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 04:18:10 +00:00
feat(legacy): Strict path validation for Ethereum.
This commit is contained in:
parent
054ab48689
commit
0af5cd18c2
1
legacy/firmware/.changelog.d/noissue.security
Normal file
1
legacy/firmware/.changelog.d/noissue.security
Normal file
@ -0,0 +1 @@
|
|||||||
|
Strict path validations for altcoins.
|
@ -1040,3 +1040,52 @@ bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n,
|
||||||
|
bool pubkey_export, uint64_t chain) {
|
||||||
|
bool valid = (address_n_count >= 3);
|
||||||
|
valid = valid && (address_n[0] == (PATH_HARDENED | 44));
|
||||||
|
valid = valid && (address_n[1] & PATH_HARDENED);
|
||||||
|
valid = valid && (address_n[2] & PATH_HARDENED);
|
||||||
|
valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
|
||||||
|
|
||||||
|
uint32_t path_slip44 = address_n[1] & PATH_UNHARDEN_MASK;
|
||||||
|
if (chain == CHAIN_ID_UNKNOWN) {
|
||||||
|
valid = valid && (is_ethereum_slip44(path_slip44));
|
||||||
|
} else {
|
||||||
|
uint32_t chain_slip44 = ethereum_slip44_by_chain_id(chain);
|
||||||
|
if (chain_slip44 == SLIP44_UNKNOWN) {
|
||||||
|
// Allow Ethereum or testnet paths for unknown networks.
|
||||||
|
valid = valid && (path_slip44 == 60 || path_slip44 == 1);
|
||||||
|
} else if (chain_slip44 != 60 && chain_slip44 != 1) {
|
||||||
|
// Allow cross-signing with Ethereum unless it's testnet.
|
||||||
|
valid = valid && (path_slip44 == chain_slip44 || path_slip44 == 60);
|
||||||
|
} else {
|
||||||
|
valid = valid && (path_slip44 == chain_slip44);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pubkey_export) {
|
||||||
|
// m/44'/coin_type'/account'/*
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address_n_count == 3) {
|
||||||
|
// SEP-0005 for non-UTXO-based currencies, defined by Stellar:
|
||||||
|
// https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md
|
||||||
|
// m/44'/coin_type'/account'
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We believe Ethereum should use the SEP-0005 scheme for everything, because
|
||||||
|
// it is account-based, rather than UTXO-based. Unfortunately, a lot of
|
||||||
|
// Ethereum tools (MEW, Metamask) do not use such scheme and set account = 0
|
||||||
|
// and then iterate the address index. For compatibility, we allow this scheme
|
||||||
|
// as well.
|
||||||
|
// m/44'/coin_type'/account'/change/address_index
|
||||||
|
valid = valid && (address_n_count == 5);
|
||||||
|
valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
|
||||||
|
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include "bip32.h"
|
#include "bip32.h"
|
||||||
#include "messages-ethereum.pb.h"
|
#include "messages-ethereum.pb.h"
|
||||||
|
|
||||||
|
#define CHAIN_ID_UNKNOWN UINT64_MAX
|
||||||
|
|
||||||
void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node);
|
void ethereum_signing_init(const EthereumSignTx *msg, const HDNode *node);
|
||||||
void ethereum_signing_init_eip1559(const EthereumSignTxEIP1559 *msg,
|
void ethereum_signing_init_eip1559(const EthereumSignTxEIP1559 *msg,
|
||||||
const HDNode *node);
|
const HDNode *node);
|
||||||
@ -39,4 +41,6 @@ void ethereum_typed_hash_sign(const EthereumSignTypedHash *msg,
|
|||||||
EthereumTypedDataSignature *resp);
|
EthereumTypedDataSignature *resp);
|
||||||
bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]);
|
bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]);
|
||||||
|
|
||||||
|
bool ethereum_path_check(uint32_t address_n_count, const uint32_t *address_n,
|
||||||
|
bool pubkey_export, uint64_t chain);
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,14 +3,19 @@ BKSL = "\\"
|
|||||||
|
|
||||||
networks = list(supported_on("trezor1", eth))
|
networks = list(supported_on("trezor1", eth))
|
||||||
max_chain_id_length = 0
|
max_chain_id_length = 0
|
||||||
|
max_slip44_length = 0
|
||||||
max_suffix_length = 0
|
max_suffix_length = 0
|
||||||
for n in networks:
|
for n in networks:
|
||||||
max_chain_id_length = max(len(str(n.chain_id)), max_chain_id_length)
|
max_chain_id_length = max(len(str(n.chain_id)), max_chain_id_length)
|
||||||
|
max_slip44_length = max(len(str(n.slip44)), max_slip44_length)
|
||||||
max_suffix_length = max(len(n.shortcut), max_suffix_length)
|
max_suffix_length = max(len(n.shortcut), max_suffix_length)
|
||||||
|
|
||||||
def align_chain_id(n):
|
def align_chain_id(n):
|
||||||
return "{:>{w}}".format(n.chain_id, w=max_chain_id_length)
|
return "{:>{w}}".format(n.chain_id, w=max_chain_id_length)
|
||||||
|
|
||||||
|
def align_slip44(n):
|
||||||
|
return "{:>{w}}".format(n.slip44, w=max_slip44_length)
|
||||||
|
|
||||||
def align_suffix(n):
|
def align_suffix(n):
|
||||||
cstr = c_str(" " + n.shortcut) + ";"
|
cstr = c_str(" " + n.shortcut) + ";"
|
||||||
# we add two quotes, a space and a semicolon. hence +4 chars
|
# we add two quotes, a space and a semicolon. hence +4 chars
|
||||||
@ -23,12 +28,34 @@ def align_suffix(n):
|
|||||||
#ifndef __ETHEREUM_NETWORKS_H__
|
#ifndef __ETHEREUM_NETWORKS_H__
|
||||||
#define __ETHEREUM_NETWORKS_H__
|
#define __ETHEREUM_NETWORKS_H__
|
||||||
|
|
||||||
|
#define SLIP44_UNKNOWN UINT32_MAX
|
||||||
|
|
||||||
#define ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id) ${BKSL}
|
#define ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id) ${BKSL}
|
||||||
switch (chain_id) { ${BKSL}
|
switch (chain_id) { ${BKSL}
|
||||||
% for n in networks:
|
% for n in networks:
|
||||||
case ${align_chain_id(n)}: suffix = ${align_suffix(n)} break; /* ${n.name} */ ${BKSL}
|
case ${align_chain_id(n)}: suffix = ${align_suffix(n)} break; /* ${n.name} */ ${BKSL}
|
||||||
% endfor
|
% endfor
|
||||||
default: suffix = " UNKN"; break; /* unknown chain */ ${BKSL}
|
default: suffix = " UNKN"; break; /* unknown chain */ ${BKSL}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_ethereum_slip44(uint32_t slip44) {
|
||||||
|
switch (slip44) {
|
||||||
|
% for slip44 in sorted(set(n.slip44 for n in networks)):
|
||||||
|
case ${slip44}:
|
||||||
|
% endfor
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t ethereum_slip44_by_chain_id(uint64_t chain_id) {
|
||||||
|
switch (chain_id) {
|
||||||
|
% for n in networks:
|
||||||
|
case ${align_chain_id(n)}: return ${align_slip44(n)}; /* ${n.name} */
|
||||||
|
% endfor
|
||||||
|
default: return SLIP44_UNKNOWN; /* unknown chain */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,6 +17,22 @@
|
|||||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static bool fsm_ethereumCheckPath(uint32_t address_n_count,
|
||||||
|
const uint32_t *address_n, bool pubkey_export,
|
||||||
|
uint64_t chain_id) {
|
||||||
|
if (ethereum_path_check(address_n_count, address_n, pubkey_export,
|
||||||
|
chain_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fsm_layoutPathWarning();
|
||||||
|
}
|
||||||
|
|
||||||
void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
|
void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
|
||||||
RESP_INIT(EthereumPublicKey);
|
RESP_INIT(EthereumPublicKey);
|
||||||
|
|
||||||
@ -28,6 +44,12 @@ void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
|
|||||||
const CoinInfo *coin = fsm_getCoin(true, "Bitcoin");
|
const CoinInfo *coin = fsm_getCoin(true, "Bitcoin");
|
||||||
if (!coin) return;
|
if (!coin) return;
|
||||||
|
|
||||||
|
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, true,
|
||||||
|
CHAIN_ID_UNKNOWN)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const char *curve = coin->curve_name;
|
const char *curve = coin->curve_name;
|
||||||
uint32_t fingerprint;
|
uint32_t fingerprint;
|
||||||
HDNode *node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count,
|
HDNode *node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count,
|
||||||
@ -71,6 +93,12 @@ void fsm_msgEthereumSignTx(const EthereumSignTx *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
|
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
||||||
|
msg->chain_id)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
||||||
msg->address_n_count, NULL);
|
msg->address_n_count, NULL);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
@ -83,6 +111,12 @@ void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
|
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
||||||
|
msg->chain_id)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
||||||
msg->address_n_count, NULL);
|
msg->address_n_count, NULL);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
@ -101,13 +135,22 @@ void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
|
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
||||||
|
CHAIN_ID_UNKNOWN)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
||||||
msg->address_n_count, NULL);
|
msg->address_n_count, NULL);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
uint8_t pubkeyhash[20];
|
uint8_t pubkeyhash[20];
|
||||||
|
|
||||||
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) return;
|
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t slip44 =
|
uint32_t slip44 =
|
||||||
(msg->address_n_count > 1) ? (msg->address_n[1] & PATH_UNHARDEN_MASK) : 0;
|
(msg->address_n_count > 1) ? (msg->address_n[1] & PATH_UNHARDEN_MASK) : 0;
|
||||||
@ -150,12 +193,19 @@ void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg) {
|
|||||||
|
|
||||||
CHECK_PIN
|
CHECK_PIN
|
||||||
|
|
||||||
|
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
||||||
|
CHAIN_ID_UNKNOWN)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
|
||||||
msg->address_n_count, NULL);
|
msg->address_n_count, NULL);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
|
|
||||||
uint8_t pubkeyhash[20] = {0};
|
uint8_t pubkeyhash[20] = {0};
|
||||||
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) {
|
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) {
|
||||||
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +280,12 @@ void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fsm_ethereumCheckPath(msg->address_n_count, msg->address_n, false,
|
||||||
|
CHAIN_ID_UNKNOWN)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL,
|
layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL,
|
||||||
_("Unable to show"), _("EIP-712 data."), NULL,
|
_("Unable to show"), _("EIP-712 data."), NULL,
|
||||||
_("Sign at your own risk."), NULL, NULL);
|
_("Sign at your own risk."), NULL, NULL);
|
||||||
@ -245,6 +301,7 @@ void fsm_msgEthereumSignTypedHash(const EthereumSignTypedHash *msg) {
|
|||||||
|
|
||||||
uint8_t pubkeyhash[20] = {0};
|
uint8_t pubkeyhash[20] = {0};
|
||||||
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) {
|
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) {
|
||||||
|
layoutHome();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user