1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-05-07 17:39:03 +00:00

Merge branch 'release/22.05'

This commit is contained in:
Martin Milata 2022-05-18 12:43:53 +02:00
commit c5f1bec474
100 changed files with 3918 additions and 1713 deletions

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,9 @@
"erc20:eth:LINK (Chainlink)": { "erc20:eth:LINK (Chainlink)": {
"name": "Chainlink" "name": "Chainlink"
}, },
"erc20:eth:SOL": {
"shortcut": "SOLA"
},
"eth:CLO": { "eth:CLO": {
"coinmarketcap_alias": "callisto-network" "coinmarketcap_alias": "callisto-network"
}, },

@ -1 +1 @@
Subproject commit c2dcfd82e737d894044635f9cb4f0a6d8cf0d158 Subproject commit 427b0628145b86911ffe571bb1ced0ca883c48f5

@ -1 +1 @@
Subproject commit 502ed91029c4b5c89f5059f561cc98fa346fdda2 Subproject commit 9045282fce6e3b40f6bbed29ce9bf8a86d415cf3

View File

@ -25,7 +25,8 @@
}, },
"wallet": { "wallet": {
"Account Viewer": "https://accountviewer.stellar.org/", "Account Viewer": "https://accountviewer.stellar.org/",
"Exodus": "https://www.exodus.io" "Exodus": "https://www.exodus.io",
"Stellarport": "https://stellarport.io/"
} }
}, },
{ {

File diff suppressed because it is too large Load Diff

View File

@ -82,8 +82,7 @@
}, },
"bitcoin:RVN": { "bitcoin:RVN": {
"Chaintek": "https://wallet.chaintek.net/", "Chaintek": "https://wallet.chaintek.net/",
"Electrum-RVN": "https://github.com/traysi/electrum-raven", "Electrum-RVN": "https://github.com/traysi/electrum-raven"
"Mango Farm": "https://mangofarmassets.com/mangowallet"
}, },
"bitcoin:SMART": { "bitcoin:SMART": {
"Electrum-SMART": "https://github.com/smartcash/electrum-smart" "Electrum-SMART": "https://github.com/smartcash/electrum-smart"

View File

@ -235,7 +235,8 @@ def _load_ethereum_networks():
chain_data = load_json(chain) chain_data = load_json(chain)
shortcut = chain_data["nativeCurrency"]["symbol"] shortcut = chain_data["nativeCurrency"]["symbol"]
name = chain_data["name"] name = chain_data["name"]
is_testnet = "testnet" in name.lower() title = chain_data.get("title", "")
is_testnet = "testnet" in name.lower() or "testnet" in title.lower()
if is_testnet: if is_testnet:
slip44 = 1 slip44 = 1
else: else:

View File

@ -1 +0,0 @@
Ensure input's script type and path match the scriptPubKey.

View File

@ -1 +0,0 @@
Trezor will refuse to sign UTXOs that do not match the provided derivation path (e.g., transactions belonging to a different wallet, or synthetic transaction inputs).

View File

@ -1 +0,0 @@
Support Bitcoin payment requests.

View File

@ -1 +0,0 @@
Automatically choose best size and encoding for QR codes.

View File

@ -1 +0,0 @@
Bitcoin bech32 addresses are encoded in lower-case for QR codes.

View File

@ -1 +0,0 @@
Show "signature is valid" dialog when VerifyMessage succeeds.

View File

@ -1 +0,0 @@
Full type-checking for Python code (except Monero app)

View File

@ -1 +0,0 @@
Support ownership proofs for Taproot addresses.

View File

@ -1 +0,0 @@
EIP-1559 transaction correctly show final Hold to Confirm screen.

View File

@ -1 +0,0 @@
Fix sighash computation in proofs of ownership.

View File

@ -1 +0,0 @@
Fix domain-only EIP-712 hashes (i.e. when `primaryType`=`EIP712Domain`)

View File

@ -1 +0,0 @@
Add extra check for Taproot scripts validity

View File

@ -1 +0,0 @@
Support Electrum signatures in VerifyMessage.

View File

@ -1 +0,0 @@
Support Cardano Alonzo-era transactions (Plutus)

View File

@ -1 +0,0 @@
\[debuglink] Do not wait for screen refresh when _disabling_ layout watching.

View File

@ -1 +0,0 @@
Support unverified external inputs.

View File

@ -1 +0,0 @@
Support Zcash version 5 transaction format

View File

@ -1 +0,0 @@
Support EIP-712 messages where a struct type is only used as an array element.

View File

@ -1 +0,0 @@
GAME, NIX and POLIS support

View File

@ -1 +0,0 @@
Add firmware hashing functionality.

View File

@ -4,6 +4,43 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.5.1 [18th May 2022]
### Added
- Support Bitcoin payment requests. [#1430]
- Show "signature is valid" dialog when VerifyMessage succeeds. [#1880]
- Support ownership proofs for Taproot addresses. [#1944]
- Add extra check for Taproot scripts validity. [#2077]
- Support Electrum signatures in VerifyMessage. [#2100]
- Support Cardano Alonzo-era transactions (Plutus). [#2114]
- Support unverified external inputs. [#2144]
- Support Zcash version 5 transaction format [#2166]
- Add firmware hashing functionality. [#2239]
### Changed
- Ensure input's script type and path match the scriptPubKey. [#1018]
- Automatically choose best size and encoding for QR codes. [#1751]
- Bitcoin bech32 addresses are encoded in lower-case for QR codes. [#1751]
- Full type-checking for Python code (except Monero app). [#1939]
- \[debuglink] Do not wait for screen refresh when _disabling_ layout watching. [#2135]
### Removed
- GAME, NIX and POLIS support. [#2181]
### Fixed
- EIP-1559 transaction correctly show final Hold to Confirm screen. [#2020]
- Fix sighash computation in proofs of ownership. [#2034]
- Fix domain-only EIP-712 hashes (i.e. when `primaryType`=`EIP712Domain`). [#2036]
- Support EIP-712 messages where a struct type is only used as an array element. [#2167]
### Security
- Fix a coin loss vulnerability related to replacement transactions with multisig inputs and unverified external inputs.
### Incompatible changes
- Trezor will refuse to sign UTXOs that do not match the provided derivation path (e.g., transactions belonging to a different wallet, or synthetic transaction inputs). [#1018]
## 2.4.3 [8th December 2021] ## 2.4.3 [8th December 2021]
### Added ### Added
@ -451,6 +488,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#948]: https://github.com/trezor/trezor-firmware/pull/948 [#948]: https://github.com/trezor/trezor-firmware/pull/948
[#958]: https://github.com/trezor/trezor-firmware/pull/958 [#958]: https://github.com/trezor/trezor-firmware/pull/958
[#982]: https://github.com/trezor/trezor-firmware/pull/982 [#982]: https://github.com/trezor/trezor-firmware/pull/982
[#1018]: https://github.com/trezor/trezor-firmware/pull/1018
[#1027]: https://github.com/trezor/trezor-firmware/pull/1027 [#1027]: https://github.com/trezor/trezor-firmware/pull/1027
[#1030]: https://github.com/trezor/trezor-firmware/pull/1030 [#1030]: https://github.com/trezor/trezor-firmware/pull/1030
[#1042]: https://github.com/trezor/trezor-firmware/pull/1042 [#1042]: https://github.com/trezor/trezor-firmware/pull/1042
@ -494,6 +532,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#1402]: https://github.com/trezor/trezor-firmware/pull/1402 [#1402]: https://github.com/trezor/trezor-firmware/pull/1402
[#1404]: https://github.com/trezor/trezor-firmware/pull/1404 [#1404]: https://github.com/trezor/trezor-firmware/pull/1404
[#1415]: https://github.com/trezor/trezor-firmware/pull/1415 [#1415]: https://github.com/trezor/trezor-firmware/pull/1415
[#1430]: https://github.com/trezor/trezor-firmware/pull/1430
[#1431]: https://github.com/trezor/trezor-firmware/pull/1431 [#1431]: https://github.com/trezor/trezor-firmware/pull/1431
[#1456]: https://github.com/trezor/trezor-firmware/pull/1456 [#1456]: https://github.com/trezor/trezor-firmware/pull/1456
[#1467]: https://github.com/trezor/trezor-firmware/pull/1467 [#1467]: https://github.com/trezor/trezor-firmware/pull/1467
@ -531,6 +570,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#1708]: https://github.com/trezor/trezor-firmware/pull/1708 [#1708]: https://github.com/trezor/trezor-firmware/pull/1708
[#1710]: https://github.com/trezor/trezor-firmware/pull/1710 [#1710]: https://github.com/trezor/trezor-firmware/pull/1710
[#1744]: https://github.com/trezor/trezor-firmware/pull/1744 [#1744]: https://github.com/trezor/trezor-firmware/pull/1744
[#1751]: https://github.com/trezor/trezor-firmware/pull/1751
[#1755]: https://github.com/trezor/trezor-firmware/pull/1755 [#1755]: https://github.com/trezor/trezor-firmware/pull/1755
[#1765]: https://github.com/trezor/trezor-firmware/pull/1765 [#1765]: https://github.com/trezor/trezor-firmware/pull/1765
[#1767]: https://github.com/trezor/trezor-firmware/pull/1767 [#1767]: https://github.com/trezor/trezor-firmware/pull/1767
@ -545,3 +585,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#1838]: https://github.com/trezor/trezor-firmware/pull/1838 [#1838]: https://github.com/trezor/trezor-firmware/pull/1838
[#1857]: https://github.com/trezor/trezor-firmware/pull/1857 [#1857]: https://github.com/trezor/trezor-firmware/pull/1857
[#1872]: https://github.com/trezor/trezor-firmware/pull/1872 [#1872]: https://github.com/trezor/trezor-firmware/pull/1872
[#1880]: https://github.com/trezor/trezor-firmware/pull/1880
[#1939]: https://github.com/trezor/trezor-firmware/pull/1939
[#1944]: https://github.com/trezor/trezor-firmware/pull/1944
[#2020]: https://github.com/trezor/trezor-firmware/pull/2020
[#2034]: https://github.com/trezor/trezor-firmware/pull/2034
[#2036]: https://github.com/trezor/trezor-firmware/pull/2036
[#2077]: https://github.com/trezor/trezor-firmware/pull/2077
[#2100]: https://github.com/trezor/trezor-firmware/pull/2100
[#2114]: https://github.com/trezor/trezor-firmware/pull/2114
[#2135]: https://github.com/trezor/trezor-firmware/pull/2135
[#2144]: https://github.com/trezor/trezor-firmware/pull/2144
[#2166]: https://github.com/trezor/trezor-firmware/pull/2166
[#2167]: https://github.com/trezor/trezor-firmware/pull/2167
[#2181]: https://github.com/trezor/trezor-firmware/pull/2181
[#2239]: https://github.com/trezor/trezor-firmware/pull/2239

View File

@ -50,6 +50,7 @@ class Approver:
self.orig_change_out = 0 # sum of original change output amounts self.orig_change_out = 0 # sum of original change output amounts
self.amount_unit = tx.amount_unit self.amount_unit = tx.amount_unit
self.has_unverified_external_input = False
def is_payjoin(self) -> bool: def is_payjoin(self) -> bool:
# A PayJoin is a replacement transaction which manipulates the external inputs of the # A PayJoin is a replacement transaction which manipulates the external inputs of the
@ -73,6 +74,7 @@ class Approver:
self.orig_total_in += txi.amount self.orig_total_in += txi.amount
if input_is_external_unverified(txi): if input_is_external_unverified(txi):
self.has_unverified_external_input = True
if safety_checks.is_strict(): if safety_checks.is_strict():
raise wire.ProcessError("Unverifiable external input.") raise wire.ProcessError("Unverifiable external input.")
else: else:
@ -235,6 +237,9 @@ class BasicApprover(Approver):
async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None: async def approve_tx(self, tx_info: TxInfo, orig_txs: list[OriginalTxInfo]) -> None:
await super().approve_tx(tx_info, orig_txs) await super().approve_tx(tx_info, orig_txs)
if self.has_unverified_external_input:
await helpers.confirm_unverified_external_input()
fee = self.total_in - self.total_out fee = self.total_in - self.total_out
# some coins require negative fees for reward TX # some coins require negative fees for reward TX

View File

@ -122,9 +122,9 @@ class Bitcoin:
# stable device tests. # stable device tests.
self.orig_txs: list[OriginalTxInfo] = [] self.orig_txs: list[OriginalTxInfo] = []
# The digests of the inputs streamed for approval in Step 1. These are used to ensure that # The digests of the external inputs streamed for approval in Step 1. These are used
# the inputs streamed for verification in Step 3 are the same as those in Step 1. # to ensure that the inputs streamed for verification in Step 3 are the same as
self.h_inputs: bytes | None = None # those in Step 1.
self.h_external_inputs: bytes | None = None self.h_external_inputs: bytes | None = None
# The index of the payment request being processed. # The index of the payment request being processed.
@ -165,11 +165,12 @@ class Bitcoin:
if txi.orig_hash: if txi.orig_hash:
await self.process_original_input(txi, script_pubkey) await self.process_original_input(txi, script_pubkey)
self.h_inputs = self.tx_info.get_tx_check_digest() self.tx_info.h_inputs_check = self.tx_info.get_tx_check_digest()
self.h_external_inputs = h_external_inputs_check.get_digest() self.h_external_inputs = h_external_inputs_check.get_digest()
# Finalize original inputs. # Finalize original inputs.
for orig in self.orig_txs: for orig in self.orig_txs:
orig.h_inputs_check = orig.get_tx_check_digest()
if orig.index != orig.tx.inputs_count: if orig.index != orig.tx.inputs_count:
raise wire.ProcessError("Removal of original inputs is not supported.") raise wire.ProcessError("Removal of original inputs is not supported.")
@ -194,7 +195,7 @@ class Bitcoin:
await orig.finalize_tx_hash() await orig.finalize_tx_hash()
async def step3_verify_inputs(self) -> None: async def step3_verify_inputs(self) -> None:
# should come out the same as h_inputs, checked before continuing # should come out the same as h_inputs_check, checked before continuing
h_check = HashWriter(sha256()) h_check = HashWriter(sha256())
if self.taproot_only: if self.taproot_only:
@ -220,7 +221,7 @@ class Bitcoin:
# inputs or to falsely claim that a transaction is a replacement of an already approved # inputs or to falsely claim that a transaction is a replacement of an already approved
# transaction or to construct a valid transaction by combining signatures obtained in # transaction or to construct a valid transaction by combining signatures obtained in
# multiple rounds of the attack. # multiple rounds of the attack.
expected_digest = self.h_inputs expected_digest = self.tx_info.h_inputs_check
for i in range(self.tx_info.tx.inputs_count): for i in range(self.tx_info.tx.inputs_count):
progress.advance() progress.advance()
txi = await helpers.request_tx_input(self.tx_req, i, self.coin) txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
@ -406,13 +407,14 @@ class Bitcoin:
async def verify_original_txs(self) -> None: async def verify_original_txs(self) -> None:
for orig in self.orig_txs: for orig in self.orig_txs:
if orig.verification_input is None: # should come out the same as h_inputs_check, checked before continuing
raise wire.ProcessError( h_check = HashWriter(sha256())
"Each original transaction must specify address_n for at least one input."
)
assert orig.verification_index is not None for i in range(orig.tx.inputs_count):
txi = orig.verification_input txi = await helpers.request_tx_input(
self.tx_req, i, self.coin, orig.orig_hash
)
writers.write_tx_input_check(h_check, txi)
script_pubkey = self.input_derive_script(txi) script_pubkey = self.input_derive_script(txi)
verifier = SignatureVerifier( verifier = SignatureVerifier(
script_pubkey, txi.script_sig, txi.witness, self.coin script_pubkey, txi.script_sig, txi.witness, self.coin
@ -421,7 +423,7 @@ class Bitcoin:
(SigHashType.SIGHASH_ALL_TAPROOT, self.get_sighash_type(txi)) (SigHashType.SIGHASH_ALL_TAPROOT, self.get_sighash_type(txi))
) )
tx_digest = await self.get_tx_digest( tx_digest = await self.get_tx_digest(
orig.verification_index, i,
txi, txi,
orig, orig,
verifier.public_keys, verifier.public_keys,
@ -430,6 +432,10 @@ class Bitcoin:
) )
verifier.verify(tx_digest) verifier.verify(tx_digest)
# check that the inputs were the same as those streamed for approval
if h_check.get_digest() != orig.h_inputs_check:
raise wire.ProcessError("Transaction has changed during signing")
async def approve_output( async def approve_output(
self, self,
txo: TxOutput, txo: TxOutput,

View File

@ -180,6 +180,11 @@ class UiConfirmChangeCountOverThreshold(UiConfirm):
return layout.confirm_change_count_over_threshold(ctx, self.change_count) return layout.confirm_change_count_over_threshold(ctx, self.change_count)
class UiConfirmUnverifiedExternalInput(UiConfirm):
def confirm_dialog(self, ctx: wire.Context) -> Awaitable[Any]:
return layout.confirm_unverified_external_input(ctx)
class UiConfirmForeignAddress(UiConfirm): class UiConfirmForeignAddress(UiConfirm):
def __init__(self, address_n: list): def __init__(self, address_n: list):
self.address_n = address_n self.address_n = address_n
@ -239,6 +244,10 @@ def confirm_change_count_over_threshold(change_count: int) -> Awaitable[Any]: #
return (yield UiConfirmChangeCountOverThreshold(change_count)) return (yield UiConfirmChangeCountOverThreshold(change_count))
def confirm_unverified_external_input() -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmUnverifiedExternalInput())
def confirm_foreign_address(address_n: list) -> Awaitable[Any]: # type: ignore [awaitable-is-generator] def confirm_foreign_address(address_n: list) -> Awaitable[Any]: # type: ignore [awaitable-is-generator]
return (yield UiConfirmForeignAddress(address_n)) return (yield UiConfirmForeignAddress(address_n))

View File

@ -220,6 +220,16 @@ async def confirm_change_count_over_threshold(
) )
async def confirm_unverified_external_input(ctx: wire.Context) -> None:
await layouts.confirm_metadata(
ctx,
"unverified_external_input",
"Warning",
"The transaction contains unverified external inputs.",
br_code=ButtonRequestType.SignTx,
)
async def confirm_nondefault_locktime( async def confirm_nondefault_locktime(
ctx: wire.Context, lock_time: int, lock_time_disabled: bool ctx: wire.Context, lock_time: int, lock_time_disabled: bool
) -> None: ) -> None:

View File

@ -72,6 +72,11 @@ class TxInfoBase:
# in Steps 1 and 2 and the ones streamed for signing legacy inputs in Step 4. # in Steps 1 and 2 and the ones streamed for signing legacy inputs in Step 4.
self.h_tx_check = HashWriter(sha256()) # not a real tx hash self.h_tx_check = HashWriter(sha256()) # not a real tx hash
# The digests of the inputs streamed for approval in Step 1. These are used to
# ensure that the inputs streamed for verification in Step 3 are the same as
# those in Step 1.
self.h_inputs_check: bytes | None = None
# BIP-0143 transaction hashing. # BIP-0143 transaction hashing.
self.sig_hasher = signer.create_sig_hasher(tx) self.sig_hasher = signer.create_sig_hasher(tx)
@ -145,19 +150,10 @@ class OriginalTxInfo(TxInfoBase):
signer.write_tx_header(self.h_tx, tx, witness_marker=False) signer.write_tx_header(self.h_tx, tx, witness_marker=False)
writers.write_compact_size(self.h_tx, tx.inputs_count) writers.write_compact_size(self.h_tx, tx.inputs_count)
# The input which will be used for verification and its index in the original transaction.
self.verification_input: TxInput | None = None
self.verification_index: int | None = None
def add_input(self, txi: TxInput, script_pubkey: bytes) -> None: def add_input(self, txi: TxInput, script_pubkey: bytes) -> None:
super().add_input(txi, script_pubkey) super().add_input(txi, script_pubkey)
writers.write_tx_input(self.h_tx, txi, txi.script_sig or bytes()) writers.write_tx_input(self.h_tx, txi, txi.script_sig or bytes())
# For verification use the first original input that specifies address_n.
if not self.verification_input and txi.address_n:
self.verification_input = txi
self.verification_index = self.index
def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None: def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None:
super().add_output(txo, script_pubkey) super().add_output(txo, script_pubkey)

View File

@ -15,8 +15,6 @@ from apps.common.writers import ( # noqa: F401
write_uint64_le, write_uint64_le,
) )
from .common import input_is_external_unverified
if TYPE_CHECKING: if TYPE_CHECKING:
from trezor.messages import ( from trezor.messages import (
PrevInput, PrevInput,
@ -48,15 +46,23 @@ def write_tx_input(w: Writer, i: TxInput | PrevInput, script: bytes) -> None:
def write_tx_input_check(w: Writer, i: TxInput) -> None: def write_tx_input_check(w: Writer, i: TxInput) -> None:
write_bytes_fixed(w, i.prev_hash, TX_HASH_SIZE) from .multisig import multisig_fingerprint
write_uint32(w, i.prev_index)
write_uint32(w, i.script_type)
write_uint8(w, input_is_external_unverified(i))
write_uint32(w, len(i.address_n)) write_uint32(w, len(i.address_n))
for n in i.address_n: for n in i.address_n:
write_uint32(w, n) write_uint32(w, n)
write_bytes_fixed(w, i.prev_hash, TX_HASH_SIZE)
write_uint32(w, i.prev_index)
write_bytes_prefixed(w, i.script_sig or b"")
write_uint32(w, i.sequence) write_uint32(w, i.sequence)
write_uint32(w, i.script_type)
multisig_fp = multisig_fingerprint(i.multisig) if i.multisig else b""
write_bytes_prefixed(w, multisig_fp)
write_uint64(w, i.amount or 0) write_uint64(w, i.amount or 0)
write_bytes_prefixed(w, i.witness or b"")
write_bytes_prefixed(w, i.ownership_proof or b"")
write_bytes_prefixed(w, i.orig_hash or b"")
write_uint32(w, i.orig_index or 0)
write_bytes_prefixed(w, i.script_pubkey or b"") write_bytes_prefixed(w, i.script_pubkey or b"")

View File

@ -60,21 +60,21 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
chain_id=3, chain_id=3,
slip44=1, slip44=1,
shortcut="tROP", shortcut="tROP",
name="Ethereum Testnet Ropsten", name="Ropsten",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
chain_id=4, chain_id=4,
slip44=1, slip44=1,
shortcut="tRIN", shortcut="tRIN",
name="Ethereum Testnet Rinkeby", name="Rinkeby",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
chain_id=5, chain_id=5,
slip44=1, slip44=1,
shortcut="tGOR", shortcut="tGOR",
name="Ethereum Testnet Görli", name="Görli",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -105,13 +105,6 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Ubiq Network Testnet", name="Ubiq Network Testnet",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=10,
slip44=60,
shortcut="OETH",
name="Optimistic Ethereum",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=11, chain_id=11,
slip44=916, slip44=916,
@ -179,7 +172,14 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
chain_id=20, chain_id=20,
slip44=60, slip44=60,
shortcut="ELA", shortcut="ELA",
name="ELA-ETH-Sidechain", name="Elastos Smart Chain",
rskip60=False,
)
yield NetworkInfo(
chain_id=24,
slip44=60,
shortcut="DTH",
name="Dithereum",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -249,7 +249,7 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
chain_id=42, chain_id=42,
slip44=1, slip44=1,
shortcut="tKOV", shortcut="tKOV",
name="Ethereum Testnet Kovan", name="Kovan",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -340,7 +340,7 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
chain_id=66, chain_id=66,
slip44=60, slip44=60,
shortcut="OKT", shortcut="OKT",
name="OKExChain", name="OKXChain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -361,7 +361,14 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
chain_id=69, chain_id=69,
slip44=1, slip44=1,
shortcut="tKOR", shortcut="tKOR",
name="Optimistic Ethereum Testnet Kovan", name="Optimism Kovan",
rskip60=False,
)
yield NetworkInfo(
chain_id=74,
slip44=60,
shortcut="EIDI",
name="IDChain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -406,6 +413,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="GateChain", name="GateChain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=87,
slip44=60,
shortcut="SNT",
name="Nova Network",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=88, chain_id=88,
slip44=889, slip44=889,
@ -413,6 +427,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="TomoChain", name="TomoChain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=90,
slip44=60,
shortcut="GAR",
name="Garizon Stage0",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=96, chain_id=96,
slip44=60, slip44=60,
@ -448,6 +469,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="EtherInc", name="EtherInc",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=105,
slip44=60,
shortcut="W3G",
name="Web3Games Devnet",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=106, chain_id=106,
slip44=60, slip44=60,
@ -490,6 +518,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Decentralized Web", name="Decentralized Web",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=126,
slip44=126,
shortcut="OY",
name="OYchain",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=127, chain_id=127,
slip44=127, slip44=127,
@ -567,6 +602,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Permission", name="Permission",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=225,
slip44=60,
shortcut="LA",
name="LACHAIN",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=246, chain_id=246,
slip44=246, slip44=246,
@ -588,6 +630,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Huobi ECO Chain Testnet", name="Huobi ECO Chain Testnet",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=258,
slip44=60,
shortcut="SETM",
name="Setheum",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=262, chain_id=262,
slip44=60, slip44=60,
@ -609,6 +658,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="KCC", name="KCC",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=333,
slip44=60,
shortcut="W3Q",
name="Web3Q",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=336, chain_id=336,
slip44=60, slip44=60,
@ -644,6 +700,20 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Rupaya", name="Rupaya",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=512,
slip44=1512,
shortcut="AAC",
name="Double-A Chain",
rskip60=False,
)
yield NetworkInfo(
chain_id=555,
slip44=60,
shortcut="CLASS",
name="Vela1 Chain",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=558, chain_id=558,
slip44=60, slip44=60,
@ -665,6 +735,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Karura Network", name="Karura Network",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=707,
slip44=60,
shortcut="BCS",
name="BlockChain Station",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=777, chain_id=777,
slip44=60, slip44=60,
@ -770,6 +847,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Clover", name="Clover",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=1030,
slip44=60,
shortcut="CFX",
name="Conflux eSpace",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=1088, chain_id=1088,
slip44=60, slip44=60,
@ -791,6 +875,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="MathChain Testnet", name="MathChain Testnet",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=1197,
slip44=60,
shortcut="IORA",
name="Iora Chain",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=1202, chain_id=1202,
slip44=60, slip44=60,
@ -805,6 +896,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Popcateum", name="Popcateum",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=1214,
slip44=60,
shortcut="ENTER",
name="EnterChain",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=1280, chain_id=1280,
slip44=60, slip44=60,
@ -826,13 +924,6 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Moonriver", name="Moonriver",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=1286,
slip44=60,
shortcut="ROC",
name="Moonrock",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=1287, chain_id=1287,
slip44=60, slip44=60,
@ -843,8 +934,8 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
yield NetworkInfo( yield NetworkInfo(
chain_id=1288, chain_id=1288,
slip44=60, slip44=60,
shortcut="MSHD", shortcut="ROC",
name="Moonshadow", name="Moonrock",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -868,6 +959,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Teslafunds", name="Teslafunds",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=1898,
slip44=60,
shortcut="BOY",
name="BON Network",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=1987, chain_id=1987,
slip44=1987, slip44=1987,
@ -903,6 +1001,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Ecoball", name="Ecoball",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=2213,
slip44=60,
shortcut="EVA",
name="Evanesco",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=2559, chain_id=2559,
slip44=60, slip44=60,
@ -910,6 +1015,20 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Kortho", name="Kortho",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=3400,
slip44=60,
shortcut="PRB",
name="Paribu Net",
rskip60=False,
)
yield NetworkInfo(
chain_id=3966,
slip44=60,
shortcut="DYNO",
name="DYNO",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=4689, chain_id=4689,
slip44=60, slip44=60,
@ -945,6 +1064,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Pixie Chain", name="Pixie Chain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=8000,
slip44=60,
shortcut="TELE",
name="Teleport",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=8217, chain_id=8217,
slip44=8217, slip44=8217,
@ -966,6 +1092,20 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="bloxberg", name="bloxberg",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=9001,
slip44=60,
shortcut="EVMOS",
name="Evmos",
rskip60=False,
)
yield NetworkInfo(
chain_id=9100,
slip44=60,
shortcut="GNC",
name="Genesis Coin",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=10101, chain_id=10101,
slip44=60, slip44=60,
@ -973,6 +1113,20 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Blockchain Genesis", name="Blockchain Genesis",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=10823,
slip44=60,
shortcut="CCP",
name="CryptoCoinPay",
rskip60=False,
)
yield NetworkInfo(
chain_id=11111,
slip44=60,
shortcut="WGM",
name="WAGMI",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=12052, chain_id=12052,
slip44=621, slip44=621,
@ -980,6 +1134,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Singularity ZERO", name="Singularity ZERO",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=13381,
slip44=60,
shortcut="PHX",
name="Phoenix",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=16000, chain_id=16000,
slip44=60, slip44=60,
@ -994,6 +1155,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="BTCIX Network", name="BTCIX Network",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=21816,
slip44=60,
shortcut="OML",
name="omChain",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=24484, chain_id=24484,
slip44=227, slip44=227,
@ -1059,9 +1227,9 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
) )
yield NetworkInfo( yield NetworkInfo(
chain_id=43114, chain_id=43114,
slip44=9000, slip44=9005,
shortcut="AVAX", shortcut="AVAX",
name="Avalanche", name="Avalanche C-Chain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -1071,6 +1239,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Celo Alfajores Testnet", name="Celo Alfajores Testnet",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=47805,
slip44=60,
shortcut="REI",
name="REI Network",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=49797, chain_id=49797,
slip44=1, slip44=1,
@ -1078,6 +1253,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Energi Testnet", name="Energi Testnet",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=53935,
slip44=60,
shortcut="JEWEL",
name="DFK Chain",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=62320, chain_id=62320,
slip44=1, slip44=1,
@ -1085,6 +1267,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Celo Baklava Testnet", name="Celo Baklava Testnet",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=63000,
slip44=60,
shortcut="ECS",
name="eCredits",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=73799, chain_id=73799,
slip44=1, slip44=1,
@ -1103,7 +1292,14 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
chain_id=80001, chain_id=80001,
slip44=1, slip44=1,
shortcut="tMATIC", shortcut="tMATIC",
name="Polygon Testnet Mumbai", name="Mumbai",
rskip60=False,
)
yield NetworkInfo(
chain_id=99999,
slip44=60,
shortcut="UBC",
name="UB Smart Chain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo( yield NetworkInfo(
@ -1169,6 +1365,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="QuarkChain", name="QuarkChain",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=108801,
slip44=60,
shortcut="BRO",
name="BROChain",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=200625, chain_id=200625,
slip44=200625, slip44=200625,
@ -1183,6 +1386,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Alaya", name="Alaya",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=210425,
slip44=60,
shortcut="lat",
name="PlatON",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=246529, chain_id=246529,
slip44=246529, slip44=246529,
@ -1211,6 +1421,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Polis", name="Polis",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=888888,
slip44=60,
shortcut="VS",
name="Vision -",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=955305, chain_id=955305,
slip44=1011, slip44=1011,
@ -1239,6 +1456,13 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="Musicoin", name="Musicoin",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=11155111,
slip44=1,
shortcut="tSEP",
name="Sepolia",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=13371337, chain_id=13371337,
slip44=60, slip44=60,
@ -1309,13 +1533,6 @@ def _networks_iterator() -> Iterator[NetworkInfo]:
name="IPOS Network", name="IPOS Network",
rskip60=False, rskip60=False,
) )
yield NetworkInfo(
chain_id=1313161554,
slip44=60,
shortcut="aETH",
name="Aurora",
rskip60=False,
)
yield NetworkInfo( yield NetworkInfo(
chain_id=1666600000, chain_id=1666600000,
slip44=60, slip44=60,

View File

@ -85,6 +85,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("AFA", 2) # eth / Africahead Ipparts return TokenInfo("AFA", 2) # eth / Africahead Ipparts
if address == b"\x8e\xb2\x43\x19\x39\x37\x16\x66\x8d\x76\x8d\xce\xc2\x93\x56\xae\x9c\xff\xe2\x85": if address == b"\x8e\xb2\x43\x19\x39\x37\x16\x66\x8d\x76\x8d\xce\xc2\x93\x56\xae\x9c\xff\xe2\x85":
return TokenInfo("AGI", 8) # eth / SingularityNET return TokenInfo("AGI", 8) # eth / SingularityNET
if address == b"\x73\x88\x65\x30\x1a\x9b\x7d\xd8\x0d\xc3\x66\x6d\xd4\x8c\xf0\x34\xec\x42\xbd\xda":
return TokenInfo("AGRS", 8) # eth / Agoras: Currency of Tau
if address == b"\x7d\xb5\x45\x4f\x35\x00\xf2\x81\x71\xd1\xf9\xc7\xa3\x85\x27\xc9\xcf\x94\xe6\xb2": if address == b"\x7d\xb5\x45\x4f\x35\x00\xf2\x81\x71\xd1\xf9\xc7\xa3\x85\x27\xc9\xcf\x94\xe6\xb2":
return TokenInfo("AGS", 4) # eth / Silver Standard return TokenInfo("AGS", 4) # eth / Silver Standard
if address == b"\x51\x21\xe3\x48\xe8\x97\xda\xef\x1e\xef\x23\x95\x9a\xb2\x90\xe5\x55\x7c\xf2\x74": if address == b"\x51\x21\xe3\x48\xe8\x97\xda\xef\x1e\xef\x23\x95\x9a\xb2\x90\xe5\x55\x7c\xf2\x74":
@ -113,6 +115,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("aLINK", 18) # eth / Aave Interest bearing LINK return TokenInfo("aLINK", 18) # eth / Aave Interest bearing LINK
if address == b"\xea\x61\x0b\x11\x53\x47\x77\x20\x74\x8d\xc1\x3e\xd3\x78\x00\x39\x41\xd8\x4f\xab": if address == b"\xea\x61\x0b\x11\x53\x47\x77\x20\x74\x8d\xc1\x3e\xd3\x78\x00\x39\x41\xd8\x4f\xab":
return TokenInfo("ALIS", 18) # eth / ALIS Token return TokenInfo("ALIS", 18) # eth / ALIS Token
if address == b"\xe0\xcc\xa8\x6b\x25\x40\x05\x88\x9a\xc3\xa8\x1e\x73\x7f\x56\xa1\x4f\x4a\x38\xf5":
return TokenInfo("ALTA", 18) # eth / Alta Finance
if address == b"\x63\x8a\xc1\x49\xea\x8e\xf9\xa1\x28\x6c\x41\xb9\x77\x01\x7a\xa7\x35\x9e\x6c\xfa": if address == b"\x63\x8a\xc1\x49\xea\x8e\xf9\xa1\x28\x6c\x41\xb9\x77\x01\x7a\xa7\x35\x9e\x6c\xfa":
return TokenInfo("ALTS", 18) # eth / ALTS Token return TokenInfo("ALTS", 18) # eth / ALTS Token
if address == b"\x49\xb1\x27\xbc\x33\xce\x7e\x15\x86\xec\x28\xce\xc6\xa6\x5b\x11\x25\x96\xc8\x22": if address == b"\x49\xb1\x27\xbc\x33\xce\x7e\x15\x86\xec\x28\xce\xc6\xa6\x5b\x11\x25\x96\xc8\x22":
@ -521,8 +525,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("CHSB", 8) # eth / SwissBorg return TokenInfo("CHSB", 8) # eth / SwissBorg
if address == b"\x14\x60\xa5\x80\x96\xd8\x0a\x50\xa2\xf1\xf9\x56\xdd\xa4\x97\x61\x1f\xa4\xf1\x65": if address == b"\x14\x60\xa5\x80\x96\xd8\x0a\x50\xa2\xf1\xf9\x56\xdd\xa4\x97\x61\x1f\xa4\xf1\x65":
return TokenInfo("CHX", 18) # eth / Own return TokenInfo("CHX", 18) # eth / Own
if address == b"\x00\x38\x01\x43\x12\x91\x67\x39\x5e\x8b\x4f\x0a\x35\xed\xc1\xbc\x60\xe7\xce\x65":
return TokenInfo("CIG", 8) # eth / Clorigin
if address == b"\x37\xfe\x0f\x06\x7f\xa8\x08\xff\xbd\xd1\x28\x91\xc0\x85\x85\x32\xcf\xe7\x36\x1d": if address == b"\x37\xfe\x0f\x06\x7f\xa8\x08\xff\xbd\xd1\x28\x91\xc0\x85\x85\x32\xcf\xe7\x36\x1d":
return TokenInfo("CIV", 18) # eth / Civilization return TokenInfo("CIV", 18) # eth / Civilization
if address == b"\xf7\x5f\xbf\xa2\xf6\x81\x86\x0b\x9a\x6d\x19\xfc\x3f\xf3\xd3\x4c\xb3\x22\xe2\xd6": if address == b"\xf7\x5f\xbf\xa2\xf6\x81\x86\x0b\x9a\x6d\x19\xfc\x3f\xf3\xd3\x4c\xb3\x22\xe2\xd6":
@ -555,6 +557,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("CNB", 8) # eth / Canabio return TokenInfo("CNB", 8) # eth / Canabio
if address == b"\xd4\xc4\x35\xf5\xb0\x9f\x85\x5c\x33\x17\xc8\x52\x4c\xb1\xf5\x86\xe4\x27\x95\xfa": if address == b"\xd4\xc4\x35\xf5\xb0\x9f\x85\x5c\x33\x17\xc8\x52\x4c\xb1\xf5\x86\xe4\x27\x95\xfa":
return TokenInfo("CND", 18) # eth / Cindicator return TokenInfo("CND", 18) # eth / Cindicator
if address == b"\xbc\x13\x8b\xd2\x0c\x98\x18\x6c\xc0\x34\x2c\x8e\x38\x09\x53\xaf\x0c\xb4\x8b\xa8":
return TokenInfo("CNDL", 18) # eth / Candle
if address == b"\x87\x13\xd2\x66\x37\xcf\x49\xe1\xb6\xb4\xa7\xce\x57\x10\x6a\xab\xc9\x32\x53\x43": if address == b"\x87\x13\xd2\x66\x37\xcf\x49\xe1\xb6\xb4\xa7\xce\x57\x10\x6a\xab\xc9\x32\x53\x43":
return TokenInfo("CNN", 18) # eth / Content Neutrality Network return TokenInfo("CNN", 18) # eth / Content Neutrality Network
if address == b"\xb4\xb1\xd2\xc2\x17\xec\x07\x76\x58\x4c\xe0\x8d\x3d\xd9\x8f\x90\xed\xed\xa4\x4b": if address == b"\xb4\xb1\xd2\xc2\x17\xec\x07\x76\x58\x4c\xe0\x8d\x3d\xd9\x8f\x90\xed\xed\xa4\x4b":
@ -677,8 +681,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("DACS", 18) # eth / DACSEE return TokenInfo("DACS", 18) # eth / DACSEE
if address == b"\xfb\x2f\x26\xf2\x66\xfb\x28\x05\xa3\x87\x23\x0f\x2a\xa0\xa3\x31\xb4\xd9\x6f\xba": if address == b"\xfb\x2f\x26\xf2\x66\xfb\x28\x05\xa3\x87\x23\x0f\x2a\xa0\xa3\x31\xb4\xd9\x6f\xba":
return TokenInfo("DADI", 18) # eth / DADI return TokenInfo("DADI", 18) # eth / DADI
if address == b"\x6b\x17\x54\x74\xe8\x90\x94\xc4\x4d\xa9\x8b\x95\x4e\xed\xea\xc4\x95\x27\x1d\x0f": if address == b"\x1d\x01\x98\x82\x9c\xba\x76\x8e\x4e\xf2\xf7\x62\xcd\x82\x84\x2b\xba\x3e\x34\x58":
return TokenInfo("DAI", 18) # eth / Dai Stablecoin v2.0 return TokenInfo("DAF", 6) # eth / Diamonds are Forever
if address == b"\x07\xd9\xe4\x9e\xa4\x02\x19\x4b\xf4\x8a\x82\x76\xda\xfb\x16\xe4\xed\x63\x33\x17": if address == b"\x07\xd9\xe4\x9e\xa4\x02\x19\x4b\xf4\x8a\x82\x76\xda\xfb\x16\xe4\xed\x63\x33\x17":
return TokenInfo("DALC", 8) # eth / DaleCoin return TokenInfo("DALC", 8) # eth / DaleCoin
if address == b"\x9b\x70\x74\x0e\x70\x8a\x08\x3c\x6f\xf3\x8d\xf5\x22\x97\x02\x0f\x5d\xfa\xa5\xee": if address == b"\x9b\x70\x74\x0e\x70\x8a\x08\x3c\x6f\xf3\x8d\xf5\x22\x97\x02\x0f\x5d\xfa\xa5\xee":
@ -731,8 +735,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("DEX", 18) # eth / DEX return TokenInfo("DEX", 18) # eth / DEX
if address == b"\x43\x1a\xd2\xff\x6a\x9c\x36\x58\x05\xeb\xad\x47\xee\x02\x11\x48\xd6\xf7\xdb\xe0": if address == b"\x43\x1a\xd2\xff\x6a\x9c\x36\x58\x05\xeb\xad\x47\xee\x02\x11\x48\xd6\xf7\xdb\xe0":
return TokenInfo("DF", 18) # eth / dForce Platform Token return TokenInfo("DF", 18) # eth / dForce Platform Token
if address == b"\xe0\xb7\x92\x7c\x4a\xf2\x37\x65\xcb\x51\x31\x4a\x0e\x05\x21\xa9\x64\x5f\x0e\x2a":
return TokenInfo("DGD", 9) # eth / Digix DAO
if address == b"\xf6\xcf\xe5\x3d\x6f\xeb\xae\xea\x05\x1f\x40\x0f\xf5\xfc\x14\xf0\xcb\xbd\xac\xa1": if address == b"\xf6\xcf\xe5\x3d\x6f\xeb\xae\xea\x05\x1f\x40\x0f\xf5\xfc\x14\xf0\xcb\xbd\xac\xa1":
return TokenInfo("DGPT", 18) # eth / DigiPulse return TokenInfo("DGPT", 18) # eth / DigiPulse
if address == b"\x6a\xed\xbf\x8d\xff\x31\x43\x72\x20\xdf\x35\x19\x50\xba\x2a\x33\x62\x16\x8d\x1b": if address == b"\x6a\xed\xbf\x8d\xff\x31\x43\x72\x20\xdf\x35\x19\x50\xba\x2a\x33\x62\x16\x8d\x1b":
@ -1213,8 +1215,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("GLA", 8) # eth / Gladius return TokenInfo("GLA", 8) # eth / Gladius
if address == b"\x68\x10\xe7\x76\x88\x0c\x02\x93\x3d\x47\xdb\x1b\x9f\xc0\x59\x08\xe5\x38\x6b\x96": if address == b"\x68\x10\xe7\x76\x88\x0c\x02\x93\x3d\x47\xdb\x1b\x9f\xc0\x59\x08\xe5\x38\x6b\x96":
return TokenInfo("GNO", 18) # eth / Gnosis return TokenInfo("GNO", 18) # eth / Gnosis
if address == b"\xa7\x44\x76\x44\x31\x19\xa9\x42\xde\x49\x85\x90\xfe\x1f\x24\x54\xd7\xd4\xac\x0d":
return TokenInfo("GNT", 18) # eth / Golem
if address == b"\x6e\xc8\xa2\x4c\xab\xdc\x33\x9a\x06\xa1\x72\xf8\x22\x3e\xa5\x57\x05\x5a\xda\xa5": if address == b"\x6e\xc8\xa2\x4c\xab\xdc\x33\x9a\x06\xa1\x72\xf8\x22\x3e\xa5\x57\x05\x5a\xda\xa5":
return TokenInfo("GNX", 9) # eth / Genaro Network return TokenInfo("GNX", 9) # eth / Genaro Network
if address == b"\x24\x75\x51\xf2\xeb\x33\x62\xe2\x22\xc7\x42\xe9\xc7\x88\xb8\x95\x7d\x9b\xc8\x7e": if address == b"\x24\x75\x51\xf2\xeb\x33\x62\xe2\x22\xc7\x42\xe9\xc7\x88\xb8\x95\x7d\x9b\xc8\x7e":
@ -1255,8 +1255,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("GUESS", 2) # eth / Peerguess return TokenInfo("GUESS", 2) # eth / Peerguess
if address == b"\x98\x47\x34\x5d\xe8\xb6\x14\xc9\x56\x14\x6b\xbe\xa5\x49\x33\x6d\x9c\x8d\x26\xb6": if address == b"\x98\x47\x34\x5d\xe8\xb6\x14\xc9\x56\x14\x6b\xbe\xa5\x49\x33\x6d\x9c\x8d\x26\xb6":
return TokenInfo("GULD", 8) # eth / GULD ERC20 return TokenInfo("GULD", 8) # eth / GULD ERC20
if address == b"\xf7\xb0\x98\x29\x8f\x7c\x69\xfc\x14\x61\x0b\xf7\x1d\x5e\x02\xc6\x07\x92\x89\x4c":
return TokenInfo("GUP", 3) # eth / Matchpool
if address == b"\x05\x6f\xd4\x09\xe1\xd7\xa1\x24\xbd\x70\x17\x45\x9d\xfe\xa2\xf3\x87\xb6\xd5\xcd": if address == b"\x05\x6f\xd4\x09\xe1\xd7\xa1\x24\xbd\x70\x17\x45\x9d\xfe\xa2\xf3\x87\xb6\xd5\xcd":
return TokenInfo("GUSD", 2) # eth / Gemini dollar return TokenInfo("GUSD", 2) # eth / Gemini dollar
if address == b"\x10\x3c\x3a\x20\x9d\xa5\x9d\x3e\x7c\x4a\x89\x30\x7e\x66\x52\x1e\x08\x1c\xfd\xf0": if address == b"\x10\x3c\x3a\x20\x9d\xa5\x9d\x3e\x7c\x4a\x89\x30\x7e\x66\x52\x1e\x08\x1c\xfd\xf0":
@ -1503,8 +1501,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("KAN", 18) # eth / BitKan return TokenInfo("KAN", 18) # eth / BitKan
if address == b"\xe1\x52\x54\xa1\x3d\x34\xf9\x70\x03\x20\x33\x0a\xbc\xb7\xc7\xf8\x57\xaf\x2f\xb7": if address == b"\xe1\x52\x54\xa1\x3d\x34\xf9\x70\x03\x20\x33\x0a\xbc\xb7\xc7\xf8\x57\xaf\x2f\xb7":
return TokenInfo("KAPA", 2) # eth / KAPA COIN return TokenInfo("KAPA", 2) # eth / KAPA COIN
if address == b"\x0d\x6d\xd9\xf6\x8d\x24\xec\x1d\x5f\xe2\x17\x4f\x3e\xc8\xda\xb5\x2b\x52\xba\xf5":
return TokenInfo("KC", 18) # eth / KMCC
if address == b"\x03\x9b\x56\x49\xa5\x99\x67\xe3\xe9\x36\xd7\x47\x1f\x9c\x37\x00\x10\x0e\xe1\xab": if address == b"\x03\x9b\x56\x49\xa5\x99\x67\xe3\xe9\x36\xd7\x47\x1f\x9c\x37\x00\x10\x0e\xe1\xab":
return TokenInfo("KCS", 6) # eth / KuCoin return TokenInfo("KCS", 6) # eth / KuCoin
if address == b"\x72\xd3\x2a\xc1\xc5\xe6\x6b\xfc\x5b\x08\x80\x62\x71\xf8\xee\xf9\x15\x54\x51\x64": if address == b"\x72\xd3\x2a\xc1\xc5\xe6\x6b\xfc\x5b\x08\x80\x62\x71\xf8\xee\xf9\x15\x54\x51\x64":
@ -1625,6 +1621,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("[deprecated] LRC (old)", 18) # eth / Loopring return TokenInfo("[deprecated] LRC (old)", 18) # eth / Loopring
if address == b"\x5d\xbe\x29\x6f\x97\xb2\x3c\x4a\x6a\xa6\x18\x3d\x73\xe5\x74\xd0\x2b\xa5\xc7\x19": if address == b"\x5d\xbe\x29\x6f\x97\xb2\x3c\x4a\x6a\xa6\x18\x3d\x73\xe5\x74\xd0\x2b\xa5\xc7\x19":
return TokenInfo("LUC", 18) # eth / LUCToken return TokenInfo("LUC", 18) # eth / LUCToken
if address == b"\xa5\xef\x74\x06\x8d\x04\xba\x08\x09\xb7\x37\x9d\xd7\x6a\xf5\xce\x34\xab\x7c\x57":
return TokenInfo("LUCHOW", 18) # eth / LunaChow
if address == b"\xfb\x12\xe3\xcc\xa9\x83\xb9\xf5\x9d\x90\x91\x2f\xd1\x7f\x8d\x74\x5a\x8b\x29\x53": if address == b"\xfb\x12\xe3\xcc\xa9\x83\xb9\xf5\x9d\x90\x91\x2f\xd1\x7f\x8d\x74\x5a\x8b\x29\x53":
return TokenInfo("LUCK", 0) # eth / LUCK return TokenInfo("LUCK", 0) # eth / LUCK
if address == b"\x01\xcd\x3d\x9d\xf5\x86\x9c\xa7\x95\x47\x45\x66\x3b\xd6\x20\x1c\x57\x1e\x05\xcf": if address == b"\x01\xcd\x3d\x9d\xf5\x86\x9c\xa7\x95\x47\x45\x66\x3b\xd6\x20\x1c\x57\x1e\x05\xcf":
@ -1667,6 +1665,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("MDT", 18) # eth / Measurable Data Token return TokenInfo("MDT", 18) # eth / Measurable Data Token
if address == b"\x94\x7a\xeb\x02\x30\x43\x91\xf8\xfb\xe5\xb2\x5d\x7d\x98\xd6\x49\xb5\x7b\x17\x88": if address == b"\x94\x7a\xeb\x02\x30\x43\x91\xf8\xfb\xe5\xb2\x5d\x7d\x98\xd6\x49\xb5\x7b\x17\x88":
return TokenInfo("MDX", 18) # eth / Mandala Exchange Token return TokenInfo("MDX", 18) # eth / Mandala Exchange Token
if address == b"\x66\x52\xfa\x20\x1b\x6b\xbb\xc0\xb5\xb0\xad\x3f\x57\x02\xb2\xb9\x84\x9c\xc8\x30":
return TokenInfo("MEDCASH", 4) # eth / MEDCASH
if address == b"\xfd\x1e\x80\x50\x8f\x24\x3e\x64\xce\x23\x4e\xa8\x8a\x5f\xd2\x82\x7c\x71\xd4\xb7": if address == b"\xfd\x1e\x80\x50\x8f\x24\x3e\x64\xce\x23\x4e\xa8\x8a\x5f\xd2\x82\x7c\x71\xd4\xb7":
return TokenInfo("MEDX", 8) # eth / MediBloc [ERC20] return TokenInfo("MEDX", 8) # eth / MediBloc [ERC20]
if address == b"\xd5\x52\x5d\x39\x78\x98\xe5\x50\x20\x75\xea\x5e\x83\x0d\x89\x14\xf6\xf0\xaf\xfe": if address == b"\xd5\x52\x5d\x39\x78\x98\xe5\x50\x20\x75\xea\x5e\x83\x0d\x89\x14\xf6\xf0\xaf\xfe":
@ -1701,14 +1701,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("MITH", 18) # eth / Mithril return TokenInfo("MITH", 18) # eth / Mithril
if address == b"\x4a\x52\x7d\x8f\xc1\x3c\x52\x03\xab\x24\xba\x09\x44\xf4\xcb\x14\x65\x8d\x1d\xb6": if address == b"\x4a\x52\x7d\x8f\xc1\x3c\x52\x03\xab\x24\xba\x09\x44\xf4\xcb\x14\x65\x8d\x1d\xb6":
return TokenInfo("MITX", 18) # eth / Morpheus Infrastructure Token return TokenInfo("MITX", 18) # eth / Morpheus Infrastructure Token
if address == b"\x9f\x8f\x72\xaa\x93\x04\xc8\xb5\x93\xd5\x55\xf1\x2e\xf6\x58\x9c\xc3\xa5\x79\xa2":
return TokenInfo("MKR", 18) # eth / MakerDAO
if address == b"\x79\x39\x88\x2b\x54\xfc\xf0\xbc\xae\x6b\x53\xde\xc3\x9a\xd6\xe8\x06\x17\x64\x42": if address == b"\x79\x39\x88\x2b\x54\xfc\xf0\xbc\xae\x6b\x53\xde\xc3\x9a\xd6\xe8\x06\x17\x64\x42":
return TokenInfo("MKT", 8) # eth / Mikado return TokenInfo("MKT", 8) # eth / Mikado
if address == b"\xec\x67\x00\x5c\x4e\x49\x8e\xc7\xf5\x5e\x09\x2b\xd1\xd3\x5c\xbc\x47\xc9\x18\x92":
return TokenInfo("MLN (new)", 18) # eth / Melonport
if address == b"\xbe\xb9\xef\x51\x4a\x37\x9b\x99\x7e\x07\x98\xfd\xcc\x90\x1e\xe4\x74\xb6\xd9\xa1":
return TokenInfo("[deprecated] MLN (old)", 18) # eth / Melonport
if address == b"\x6b\x4c\x7a\x5e\x3f\x0b\x99\xfc\xd8\x3e\x9c\x08\x9b\xdd\xd6\xc7\xfc\xe5\xc6\x11": if address == b"\x6b\x4c\x7a\x5e\x3f\x0b\x99\xfc\xd8\x3e\x9c\x08\x9b\xdd\xd6\xc7\xfc\xe5\xc6\x11":
return TokenInfo("MM", 18) # eth / Million return TokenInfo("MM", 18) # eth / Million
if address == b"\x1a\x95\xb2\x71\xb0\x53\x5d\x15\xfa\x49\x93\x2d\xab\xa3\x1b\xa6\x12\xb5\x29\x46": if address == b"\x1a\x95\xb2\x71\xb0\x53\x5d\x15\xfa\x49\x93\x2d\xab\xa3\x1b\xa6\x12\xb5\x29\x46":
@ -1825,8 +1819,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("NOAH", 18) # eth / Noah Coin return TokenInfo("NOAH", 18) # eth / Noah Coin
if address == b"\xf4\xfa\xea\x45\x55\x75\x35\x4d\x26\x99\xbc\x20\x9b\x0a\x65\xca\x99\xf6\x99\x82": if address == b"\xf4\xfa\xea\x45\x55\x75\x35\x4d\x26\x99\xbc\x20\x9b\x0a\x65\xca\x99\xf6\x99\x82":
return TokenInfo("NOBS", 18) # eth / No BS Crypto return TokenInfo("NOBS", 18) # eth / No BS Crypto
if address == b"\x64\x3b\x68\x70\xbe\xab\xee\x94\x1b\x92\x60\xa0\xa8\x78\xbc\xf4\xa6\x1f\xb0\xf1":
return TokenInfo("NONE", 0) # eth / None
if address == b"\x00\x27\x44\x9b\xf0\x88\x7c\xa3\xe4\x31\xd2\x63\xff\xde\xfb\x24\x4d\x95\xb5\x55": if address == b"\x00\x27\x44\x9b\xf0\x88\x7c\xa3\xe4\x31\xd2\x63\xff\xde\xfb\x24\x4d\x95\xb5\x55":
return TokenInfo("NOT", 18) # eth / Token Not return TokenInfo("NOT", 18) # eth / Token Not
if address == b"\xec\x46\xf8\x20\x7d\x76\x60\x12\x45\x4c\x40\x8d\xe2\x10\xbc\xbc\x22\x43\xe7\x1c": if address == b"\xec\x46\xf8\x20\x7d\x76\x60\x12\x45\x4c\x40\x8d\xe2\x10\xbc\xbc\x22\x43\xe7\x1c":
@ -2153,8 +2145,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("RBX", 18) # eth / RBX return TokenInfo("RBX", 18) # eth / RBX
if address == b"\xf9\x70\xb8\xe3\x6e\x23\xf7\xfc\x3f\xd7\x52\xee\xa8\x6f\x8b\xe8\xd8\x33\x75\xa6": if address == b"\xf9\x70\xb8\xe3\x6e\x23\xf7\xfc\x3f\xd7\x52\xee\xa8\x6f\x8b\xe8\xd8\x33\x75\xa6":
return TokenInfo("RCN", 18) # eth / Ripio Credit Network return TokenInfo("RCN", 18) # eth / Ripio Credit Network
if address == b"\x25\x5a\xa6\xdf\x07\x54\x0c\xb5\xd3\xd2\x97\xf0\xd0\xd4\xd8\x4c\xb5\x2b\xc8\xe6":
return TokenInfo("RDN", 18) # eth / Raiden Network
if address == b"\xd9\x67\xd9\xf9\x41\xcd\x31\x6a\xb2\x38\xd3\xee\x76\x1f\x80\xb7\xca\xec\x78\x19": if address == b"\xd9\x67\xd9\xf9\x41\xcd\x31\x6a\xb2\x38\xd3\xee\x76\x1f\x80\xb7\xca\xec\x78\x19":
return TokenInfo("RDV", 18) # eth / Rendezvous return TokenInfo("RDV", 18) # eth / Rendezvous
if address == b"\x76\x7b\xa2\x91\x5e\xc3\x44\x01\x5a\x79\x38\xe3\xee\xdf\xec\x27\x85\x19\x5d\x05": if address == b"\x76\x7b\xa2\x91\x5e\xc3\x44\x01\x5a\x79\x38\xe3\xee\xdf\xec\x27\x85\x19\x5d\x05":
@ -2177,10 +2167,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("REMI", 18) # eth / REMI return TokenInfo("REMI", 18) # eth / REMI
if address == b"\x40\x8e\x41\x87\x6c\xcc\xdc\x0f\x92\x21\x06\x00\xef\x50\x37\x26\x56\x05\x2a\x38": if address == b"\x40\x8e\x41\x87\x6c\xcc\xdc\x0f\x92\x21\x06\x00\xef\x50\x37\x26\x56\x05\x2a\x38":
return TokenInfo("REN", 18) # eth / Republic Token return TokenInfo("REN", 18) # eth / Republic Token
if address == b"\x19\x85\x36\x5e\x9f\x78\x35\x9a\x9b\x6a\xd7\x60\xe3\x24\x12\xf4\xa4\x45\xe8\x62":
return TokenInfo("REP", 18) # eth / Augur
if address == b"\xe9\x43\x27\xd0\x7f\xc1\x79\x07\xb4\xdb\x78\x8e\x5a\xdf\x2e\xd4\x24\xad\xdf\xf6":
return TokenInfo("[deprecated] REP", 18) # eth / Augur
if address == b"\x22\x16\x57\x77\x68\x46\x89\x09\x89\xa7\x59\xba\x29\x73\xe4\x27\xdf\xf5\xc9\xbb": if address == b"\x22\x16\x57\x77\x68\x46\x89\x09\x89\xa7\x59\xba\x29\x73\xe4\x27\xdf\xf5\xc9\xbb":
return TokenInfo("REPv2", 18) # eth / Reputation return TokenInfo("REPv2", 18) # eth / Reputation
if address == b"\x8f\x82\x21\xaf\xbb\x33\x99\x8d\x85\x84\xa2\xb0\x57\x49\xba\x73\xc3\x7a\x93\x8a": if address == b"\x8f\x82\x21\xaf\xbb\x33\x99\x8d\x85\x84\xa2\xb0\x57\x49\xba\x73\xc3\x7a\x93\x8a":
@ -2205,8 +2191,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("RIYA", 8) # eth / Etheriya return TokenInfo("RIYA", 8) # eth / Etheriya
if address == b"\x10\x6a\xa4\x92\x95\xb5\x25\xfc\xf9\x59\xaa\x75\xec\x3f\x7d\xcb\xf5\x35\x2f\x1c": if address == b"\x10\x6a\xa4\x92\x95\xb5\x25\xfc\xf9\x59\xaa\x75\xec\x3f\x7d\xcb\xf5\x35\x2f\x1c":
return TokenInfo("RKT", 18) # eth / Rock return TokenInfo("RKT", 18) # eth / Rock
if address == b"\x60\x7f\x4c\x5b\xb6\x72\x23\x0e\x86\x72\x08\x55\x32\xf7\xe9\x01\x54\x4a\x73\x75":
return TokenInfo("RLC", 9) # eth / IEx.ec
if address == b"\xcc\xed\x5b\x82\x88\x08\x6b\xe8\xc3\x8e\x23\x56\x7e\x68\x4c\x37\x40\xbe\x4d\x48": if address == b"\xcc\xed\x5b\x82\x88\x08\x6b\xe8\xc3\x8e\x23\x56\x7e\x68\x4c\x37\x40\xbe\x4d\x48":
return TokenInfo("RLT", 10) # eth / RouletteToken return TokenInfo("RLT", 10) # eth / RouletteToken
if address == b"\xbe\x99\xb0\x97\x09\xfc\x75\x3b\x09\xbc\xf5\x57\xa9\x92\xf6\x60\x5d\x59\x97\xb0": if address == b"\xbe\x99\xb0\x97\x09\xfc\x75\x3b\x09\xbc\xf5\x57\xa9\x92\xf6\x60\x5d\x59\x97\xb0":
@ -2241,6 +2225,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("RPE", 18) # eth / REPE return TokenInfo("RPE", 18) # eth / REPE
if address == b"\xb4\xef\xd8\x5c\x19\x99\x9d\x84\x25\x13\x04\xbd\xa9\x9e\x90\xb9\x23\x00\xbd\x93": if address == b"\xb4\xef\xd8\x5c\x19\x99\x9d\x84\x25\x13\x04\xbd\xa9\x9e\x90\xb9\x23\x00\xbd\x93":
return TokenInfo("RPL", 18) # eth / Rocket Pool return TokenInfo("RPL", 18) # eth / Rocket Pool
if address == b"\x32\x06\x23\xb8\xe4\xff\x03\x37\x39\x31\x76\x9a\x31\xfc\x52\xa4\xe7\x8b\x5d\x70":
return TokenInfo("RSR", 18) # eth / Reserve Rights
if address == b"\xec\x49\x1c\x10\x88\xea\xe9\x92\xb7\xa2\x14\xef\xb0\xa2\x66\xad\x09\x27\xa7\x2a": if address == b"\xec\x49\x1c\x10\x88\xea\xe9\x92\xb7\xa2\x14\xef\xb0\xa2\x66\xad\x09\x27\xa7\x2a":
return TokenInfo("RTB", 18) # eth / AB-Chain RTB return TokenInfo("RTB", 18) # eth / AB-Chain RTB
if address == b"\x7a\x55\x99\xb9\x7e\x8c\x4a\xbb\x5d\xd0\x6e\xba\x0e\x9d\x1f\x75\xaf\x81\x8d\xb9": if address == b"\x7a\x55\x99\xb9\x7e\x8c\x4a\xbb\x5d\xd0\x6e\xba\x0e\x9d\x1f\x75\xaf\x81\x8d\xb9":
@ -2307,6 +2293,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("SETS", 18) # eth / Sensitrust Token return TokenInfo("SETS", 18) # eth / Sensitrust Token
if address == b"\x68\x47\x3d\xc4\xb7\xa4\xb0\x86\x7f\xd7\xc5\xb9\xa9\x82\xfe\xa4\x07\xda\xd3\x20": if address == b"\x68\x47\x3d\xc4\xb7\xa4\xb0\x86\x7f\xd7\xc5\xb9\xa9\x82\xfe\xa4\x07\xda\xd3\x20":
return TokenInfo("sEUR", 18) # eth / Synth sEUR return TokenInfo("sEUR", 18) # eth / Synth sEUR
if address == b"\xaf\x50\xf8\xbe\xc1\xdb\xec\x01\x3b\x70\x25\xdb\x44\x4d\xa0\x19\xc2\xf5\xd4\x88":
return TokenInfo("SEV", 18) # eth / SeveraDAO
if address == b"\x98\xf5\xe9\xb7\xf0\xe3\x39\x56\xc0\x44\x3e\x81\xbf\x7d\xeb\x8b\x5b\x1e\xd5\x45": if address == b"\x98\xf5\xe9\xb7\xf0\xe3\x39\x56\xc0\x44\x3e\x81\xbf\x7d\xeb\x8b\x5b\x1e\xd5\x45":
return TokenInfo("SEXY", 18) # eth / Sexy Token return TokenInfo("SEXY", 18) # eth / Sexy Token
if address == b"\xed\x08\x49\xbf\x46\xcf\xb9\x84\x5a\x2d\x90\x0a\x0a\x4e\x59\x3f\x2d\xd3\x67\x3c": if address == b"\xed\x08\x49\xbf\x46\xcf\xb9\x84\x5a\x2d\x90\x0a\x0a\x4e\x59\x3f\x2d\xd3\x67\x3c":
@ -2525,6 +2513,8 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("SXUT", 18) # eth / Spectre.ai U-Token return TokenInfo("SXUT", 18) # eth / Spectre.ai U-Token
if address == b"\x10\xb1\x23\xfd\xdd\xe0\x03\x24\x31\x99\xaa\xd0\x35\x22\x06\x5d\xc0\x58\x27\xa0": if address == b"\x10\xb1\x23\xfd\xdd\xe0\x03\x24\x31\x99\xaa\xd0\x35\x22\x06\x5d\xc0\x58\x27\xa0":
return TokenInfo("SYN", 18) # eth / Synapse return TokenInfo("SYN", 18) # eth / Synapse
if address == b"\x46\xea\xf7\x5e\x6d\x39\x17\x08\xb7\xf1\xa0\xd5\x68\x75\xd9\x08\x44\x11\x95\x21":
return TokenInfo("SYS", 18) # eth / Syscoin
if address == b"\x3a\x0d\x74\x6b\x3e\xa1\xd8\xcc\xdf\x19\xad\x91\x59\x13\xbd\x68\x39\x11\x33\xca": if address == b"\x3a\x0d\x74\x6b\x3e\xa1\xd8\xcc\xdf\x19\xad\x91\x59\x13\xbd\x68\x39\x11\x33\xca":
return TokenInfo("SYSX", 8) # eth / SyscoinToken return TokenInfo("SYSX", 8) # eth / SyscoinToken
if address == b"\xe7\x77\x5a\x6e\x9b\xcf\x90\x4e\xb3\x9d\xa2\xb6\x8c\x5e\xfb\x4f\x93\x60\xe0\x8c": if address == b"\xe7\x77\x5a\x6e\x9b\xcf\x90\x4e\xb3\x9d\xa2\xb6\x8c\x5e\xfb\x4f\x93\x60\xe0\x8c":
@ -3395,6 +3385,10 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("WINGS", 18) # eth / WINGS return TokenInfo("WINGS", 18) # eth / WINGS
if address == b"\x1b\x22\xc3\x2c\xd9\x36\xcb\x97\xc2\x8c\x56\x90\xa0\x69\x5a\x82\xab\xf6\x88\xe6": if address == b"\x1b\x22\xc3\x2c\xd9\x36\xcb\x97\xc2\x8c\x56\x90\xa0\x69\x5a\x82\xab\xf6\x88\xe6":
return TokenInfo("WISH", 18) # eth / MyWish return TokenInfo("WISH", 18) # eth / MyWish
if address == b"\xd6\x4d\xee\xa5\xf2\x49\x34\xe3\xa1\xaa\x75\x29\x12\xae\xe8\xff\xd8\x30\x0c\x3f":
return TokenInfo("WLKR", 18) # eth / WLKR Innovation Index
if address == b"\xc9\x02\x06\xab\x21\xbd\xbf\x5e\x92\xaf\xf4\xe6\xb5\xf0\x97\xb6\x5b\x0e\xcc\x06":
return TokenInfo("WLKRR", 18) # eth / Walker
if address == b"\x68\x5e\xd3\x90\xb1\x6a\xc9\xdf\x9a\xb9\x70\x72\x94\xa4\x2a\x10\x7c\xfb\x62\xaf": if address == b"\x68\x5e\xd3\x90\xb1\x6a\xc9\xdf\x9a\xb9\x70\x72\x94\xa4\x2a\x10\x7c\xfb\x62\xaf":
return TokenInfo("WMA", 18) # eth / weeMarketplaceAccessToken return TokenInfo("WMA", 18) # eth / weeMarketplaceAccessToken
if address == b"\xbf\xbe\x53\x32\xf1\x72\xd7\x78\x11\xbc\x6c\x27\x28\x44\xf3\xe5\x4a\x7b\x23\xbb": if address == b"\xbf\xbe\x53\x32\xf1\x72\xd7\x78\x11\xbc\x6c\x27\x28\x44\xf3\xe5\x4a\x7b\x23\xbb":
@ -3561,8 +3555,6 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("ZMN", 18) # eth / ZMINE return TokenInfo("ZMN", 18) # eth / ZMINE
if address == b"\xb5\xb8\xf5\x61\x6f\xe4\x2d\x5c\xec\xa3\xe8\x7f\x3f\xdd\xbd\xd8\xf4\x96\xd7\x60": if address == b"\xb5\xb8\xf5\x61\x6f\xe4\x2d\x5c\xec\xa3\xe8\x7f\x3f\xdd\xbd\xd8\xf4\x96\xd7\x60":
return TokenInfo("ZPR", 18) # eth / ZPER return TokenInfo("ZPR", 18) # eth / ZPER
if address == b"\xe4\x1d\x24\x89\x57\x1d\x32\x21\x89\x24\x6d\xaf\xa5\xeb\xde\x1f\x46\x99\xf4\x98":
return TokenInfo("ZRX", 18) # eth / 0x Project
if address == b"\xe3\x86\xb1\x39\xed\x37\x15\xca\x4b\x18\xfd\x52\x67\x1b\xdc\xea\x1c\xdf\xe4\xb1": if address == b"\xe3\x86\xb1\x39\xed\x37\x15\xca\x4b\x18\xfd\x52\x67\x1b\xdc\xea\x1c\xdf\xe4\xb1":
return TokenInfo("ZST", 8) # eth / Zeus Exchange return TokenInfo("ZST", 8) # eth / Zeus Exchange
if address == b"\xe8\xf9\xfa\x97\x7e\xa5\x85\x59\x1d\x9f\x39\x46\x81\x31\x8c\x16\x55\x25\x77\xfb": if address == b"\xe8\xf9\xfa\x97\x7e\xa5\x85\x59\x1d\x9f\x39\x46\x81\x31\x8c\x16\x55\x25\x77\xfb":
@ -3578,27 +3570,17 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("ILSC", 2) # rop / IsraCoin return TokenInfo("ILSC", 2) # rop / IsraCoin
if address == b"\x4c\x57\x2f\xbc\x03\xd4\xa2\xb6\x83\xcf\x4f\x10\xff\xdc\xaf\xd0\x08\x85\xe1\x08": if address == b"\x4c\x57\x2f\xbc\x03\xd4\xa2\xb6\x83\xcf\x4f\x10\xff\xdc\xaf\xd0\x08\x85\xe1\x08":
return TokenInfo("MEWV5", 9) # rop / MEW V5 Test Token return TokenInfo("MEWV5", 9) # rop / MEW V5 Test Token
if address == b"\xfd\x5a\x69\xa1\x30\x95\x95\xff\x51\x21\x55\x3f\x52\xc8\xa5\xb2\xb1\xb3\x10\x31": if address == b"\xaa\x1d\x9d\x07\x88\xda\xca\x9a\x30\x11\x1d\x12\xaa\x0d\x98\x09\x0f\x02\xea\x30":
return TokenInfo("NONE", 0) # rop / None return TokenInfo("RCL", 18) # rop / RascalCoin
if address == b"\x73\x14\xdc\x4d\x77\x94\xb5\xe7\x89\x42\x12\xca\x15\x56\xae\x8e\x3d\xe5\x86\x21":
return TokenInfo("RLC", 9) # rop / iExec RLC
if chain_id == 4: if chain_id == 4:
if address == b"\xe2\x78\x26\xee\x77\x8b\x6f\x78\xa4\x9a\x68\x6d\xa7\xd6\x4f\x6e\x7b\x08\x4a\x4f": if address == b"\xe2\x78\x26\xee\x77\x8b\x6f\x78\xa4\x9a\x68\x6d\xa7\xd6\x4f\x6e\x7b\x08\x4a\x4f":
return TokenInfo("BHNT", 0) # rin / Berlin Hack&Tell winner token return TokenInfo("BHNT", 0) # rin / Berlin Hack&Tell winner token
if address == b"\x8b\x65\xd4\xb7\xee\x3f\xff\xa9\x86\xc5\x77\xf0\xf4\xb7\x0a\x21\xba\xe3\xdd\x54": if address == b"\x8b\x65\xd4\xb7\xee\x3f\xff\xa9\x86\xc5\x77\xf0\xf4\xb7\x0a\x21\xba\xe3\xdd\x54":
return TokenInfo("CTGA", 18) # rin / Convenient To Go return TokenInfo("CTGA", 18) # rin / Convenient To Go
if address == b"\x27\x5a\x5b\x34\x65\x99\xb5\x69\x17\xe7\xb1\xc9\xde\x01\x9d\xcf\x9e\xad\x86\x1a":
return TokenInfo("KC", 18) # rin / Karma Token
if address == b"\x64\x75\xa7\xfa\x6e\xd2\xd5\x18\x0f\x0e\x0a\x07\xc2\xd9\x51\xd1\x2c\x0e\xdb\x91":
return TokenInfo("NONE", 0) # rin / None
if address == b"\x12\xfe\x17\x4c\x09\x7f\x6b\x3e\x87\x6b\x3b\x06\x0c\x90\x61\xf4\xb9\xde\xbb\x80": if address == b"\x12\xfe\x17\x4c\x09\x7f\x6b\x3e\x87\x6b\x3b\x06\x0c\x90\x61\xf4\xb9\xde\xbb\x80":
return TokenInfo("PPD", 18) # rin / PP Donation return TokenInfo("PPD", 18) # rin / PP Donation
if address == b"\x2d\x42\x7d\x9e\x53\x5e\x43\x82\x60\x6b\x93\x29\x0d\xcc\x13\xa5\xe9\xa6\x94\xbe": if address == b"\x2d\x42\x7d\x9e\x53\x5e\x43\x82\x60\x6b\x93\x29\x0d\xcc\x13\xa5\xe9\xa6\x94\xbe":
return TokenInfo("qwe", 18) # rin / qweToken return TokenInfo("qwe", 18) # rin / qweToken
if address == b"\x36\x15\x75\x70\x11\x11\x25\x60\x52\x15\x36\x25\x8c\x1e\x73\x25\xae\x3b\x48\xae":
return TokenInfo("RDN", 18) # rin / Raiden
if address == b"\xf1\xe6\xad\x3a\x7e\xf0\xc8\x6c\x91\x5f\x0f\xed\xf8\x0e\xd8\x51\x80\x9b\xea\x90":
return TokenInfo("RLC", 9) # rin / iExec RLC
if address == b"\x0a\x05\x7a\x87\xce\x9c\x56\xd7\xe3\x36\xb4\x17\xc7\x9c\xf3\x0e\x8d\x27\x86\x0b": if address == b"\x0a\x05\x7a\x87\xce\x9c\x56\xd7\xe3\x36\xb4\x17\xc7\x9c\xf3\x0e\x8d\x27\x86\x0b":
return TokenInfo("WALL", 15) # rin / WALLETH Community-Token return TokenInfo("WALL", 15) # rin / WALLETH Community-Token
if chain_id == 8: if chain_id == 8:
@ -3608,10 +3590,26 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
return TokenInfo("CEFS", 8) # ubq / CEFS return TokenInfo("CEFS", 8) # ubq / CEFS
if address == b"\x94\xad\x7e\x41\xc1\xd4\x40\x22\xc4\xf4\x7c\xb1\xba\x01\x9f\xd1\xa0\x22\xc5\x36": if address == b"\x94\xad\x7e\x41\xc1\xd4\x40\x22\xc4\xf4\x7c\xb1\xba\x01\x9f\xd1\xa0\x22\xc5\x36":
return TokenInfo("DOT", 8) # ubq / DOT return TokenInfo("DOT", 8) # ubq / DOT
if address == b"\xcf\x32\x22\xb7\xfd\xa7\xa7\x56\x3b\x9e\x1e\x6c\x96\x6b\xea\xd0\x4a\xc2\x3c\x36":
return TokenInfo("ESCH", 18) # ubq / Escher
if address == b"\x50\x06\x84\xce\x0d\x4f\x04\xab\xed\xff\x3e\x54\xfc\xf8\xac\xc5\xe6\xcf\xc4\xbd":
return TokenInfo("GEO", 8) # ubq / GeoCoin
if address == b"\x08\x26\x18\x0a\x4c\x98\x1d\x50\x95\xcb\x5c\x48\xbb\x2a\x09\x8a\x44\xcf\x6f\x73":
return TokenInfo("GRANS", 18) # ubq / 10grans
if address == b"\x78\x45\xfc\xbe\x28\xac\x19\xab\x7e\xc1\xc1\xd9\x67\x4e\x34\xfd\xcb\x49\x17\xdb":
return TokenInfo("INK", 18) # ubq / INK
if address == b"\x4b\x48\x99\xa1\x0f\x3e\x50\x7d\xb2\x07\xb0\xee\x24\x26\x02\x9e\xfa\x16\x8a\x67": if address == b"\x4b\x48\x99\xa1\x0f\x3e\x50\x7d\xb2\x07\xb0\xee\x24\x26\x02\x9e\xfa\x16\x8a\x67":
return TokenInfo("QWARK", 8) # ubq / QWARK return TokenInfo("QWARK", 8) # ubq / QWARK
if address == b"\x5e\x17\x15\xbb\x79\x80\x5b\xd6\x72\x72\x97\x60\xb3\xf7\xf3\x4d\x6f\x48\x50\x98": if address == b"\x5e\x17\x15\xbb\x79\x80\x5b\xd6\x72\x72\x97\x60\xb3\xf7\xf3\x4d\x6f\x48\x50\x98":
return TokenInfo("RICKS", 8) # ubq / RICKS return TokenInfo("RICKS", 8) # ubq / RICKS
if address == b"\x49\x7e\x20\x58\x6f\x86\xc3\x55\x92\xff\x8f\x65\xcd\xe9\x4f\x29\x65\x14\xc3\x87":
return TokenInfo("SNARG", 0) # ubq / Snarg01
if address == b"\x20\xe3\xdd\x74\x6d\xdf\x51\x9b\x23\xff\xbb\xb6\xda\x7a\x5d\x33\xea\x63\x49\xd6":
return TokenInfo("SPHR", 8) # ubq / Sphere
if address == b"\x40\x2c\x2c\x3a\xce\xeb\x52\xb8\x65\x4a\x06\x31\x01\x2c\xed\x49\xcb\xc9\xbd\xc4":
return TokenInfo("SPHRC", 18) # ubq / Sphere Cubed
if address == b"\xcf\x82\x3e\xb6\xf6\x2a\x30\xed\xb0\x05\xb9\x13\x83\xe2\xfa\x36\x4d\xd7\x53\xdc":
return TokenInfo("TGE1", 18) # ubq / Ubiq TGE 1
if chain_id == 30: if chain_id == 30:
if address == b"\xd5\x2d\xa6\x36\x89\x54\x39\x24\xdc\xa6\x6b\xcb\xe2\xe2\xea\x59\x9c\x45\xd5\x75": if address == b"\xd5\x2d\xa6\x36\x89\x54\x39\x24\xdc\xa6\x6b\xcb\xe2\xe2\xea\x59\x9c\x45\xd5\x75":
return TokenInfo("ARSCB", 18) # rsk / Pesos Argentinos PagoLinea return TokenInfo("ARSCB", 18) # rsk / Pesos Argentinos PagoLinea
@ -3692,30 +3690,10 @@ def token_by_chain_address(chain_id: int, address: bytes) -> TokenInfo:
if chain_id == 42: if chain_id == 42:
if address == b"\x86\x67\x55\x92\x54\x24\x1d\xde\xd4\xd1\x13\x92\xf8\x68\xd7\x20\x92\x76\x53\x67": if address == b"\x86\x67\x55\x92\x54\x24\x1d\xde\xd4\xd1\x13\x92\xf8\x68\xd7\x20\x92\x76\x53\x67":
return TokenInfo("Aeternity", 18) # kov / Aeternity return TokenInfo("Aeternity", 18) # kov / Aeternity
if address == b"\xc4\x37\x5b\x7d\xe8\xaf\x5a\x38\xa9\x35\x48\xeb\x84\x53\xa4\x98\x22\x2c\x4f\xf2":
return TokenInfo("DAI", 18) # kov / RadarRelay test Dai Stablecoin v1.0
if address == b"\xee\xe3\x87\x06\x57\xe4\x71\x66\x70\xf1\x85\xdf\x08\x65\x2d\xd8\x48\xfe\x8f\x7e":
return TokenInfo("DGD", 18) # kov / RadarRelay test Digix DAO Token
if address == b"\x47\x33\x65\x9a\x5c\xb7\x89\x6a\x65\xc9\x18\xad\xd6\xf5\x9c\x51\x48\xfb\x5f\xfa": if address == b"\x47\x33\x65\x9a\x5c\xb7\x89\x6a\x65\xc9\x18\xad\xd6\xf5\x9c\x51\x48\xfb\x5f\xfa":
return TokenInfo("GAV", 6) # kov / GavCoin return TokenInfo("GAV", 6) # kov / GavCoin
if address == b"\xef\x7f\xff\x64\x38\x9b\x81\x4a\x94\x6f\x3e\x92\x10\x55\x13\x70\x5c\xa6\xb9\x90":
return TokenInfo("GNT", 18) # kov / RadarRelay test Golem Network Token
if address == b"\x3c\x67\xf7\xd4\xde\xcf\x77\x95\x22\x5f\x51\xb5\x41\x34\xf8\x11\x37\x38\x5f\x83":
return TokenInfo("GUP", 3) # kov / GUP
if address == b"\x1d\xad\x47\x83\xcf\x3f\xe3\x08\x5c\x14\x26\x15\x7a\xb1\x75\xa6\x11\x9a\x04\xba":
return TokenInfo("MKR", 18) # kov / RadarRelay test MakerDAO
if address == b"\xaa\xf6\x4b\xfc\xc3\x2d\x0f\x15\x87\x3a\x02\x16\x3e\x7e\x50\x06\x71\xa4\xff\xcd":
return TokenInfo("MKR", 18) # kov / MakerDAO
if address == b"\x32\x3b\x5d\x4c\x32\x34\x5c\xed\x77\x39\x3b\x35\x30\xb1\xee\xd0\xf3\x46\x42\x9d":
return TokenInfo("MLN", 18) # kov / RadarRelay test Melon Tokens
if address == b"\xb1\x88\x45\xc2\x60\xf6\x80\xd5\xb9\xd8\x46\x49\x63\x88\x13\xe3\x42\xe4\xf8\xc9":
return TokenInfo("REP", 18) # kov / RadarRelay test Augur Reputation Token
if address == b"\xc5\x75\x38\x84\x6e\xc4\x05\xea\x25\xde\xb0\x0e\x0f\x9b\x29\xa4\x32\xd5\x35\x07":
return TokenInfo("RLC", 9) # kov / iExec RLC
if address == b"\x4a\x6e\x6c\x38\x68\xa2\x79\xe1\xd9\x04\x7b\x42\xc3\xfb\x35\x6f\xf4\x68\x00\x03": if address == b"\x4a\x6e\x6c\x38\x68\xa2\x79\xe1\xd9\x04\x7b\x42\xc3\xfb\x35\x6f\xf4\x68\x00\x03":
return TokenInfo("TIB", 18) # kov / ThibCoin return TokenInfo("TIB", 18) # kov / ThibCoin
if address == b"\x6f\xf6\xc0\xff\x1d\x68\xb9\x64\x90\x1f\x98\x6d\x4c\x9f\xa3\xac\x68\x34\x65\x70":
return TokenInfo("ZRX", 18) # kov / RadarRelay test 0x Protocol Token
if chain_id == 61: if chain_id == 61:
if address == b"\x08\x5f\xb4\xf2\x40\x31\xea\xed\xbc\x2b\x61\x1a\xa5\x28\xf2\x23\x43\xeb\x52\xdb": if address == b"\x08\x5f\xb4\xf2\x40\x31\xea\xed\xbc\x2b\x61\x1a\xa5\x28\xf2\x23\x43\xeb\x52\xdb":
return TokenInfo("BEC", 8) # etc / BEC return TokenInfo("BEC", 8) # etc / BEC

View File

@ -43,7 +43,7 @@ class TestWriters(unittest.TestCase):
b = bytearray() b = bytearray()
writers.write_tx_input_check(b, inp) writers.write_tx_input_check(b, inp)
self.assertEqual(len(b), 32 + 4 + 4 + 1 + 4 + 4 + 4 + 8 + 26) self.assertEqual(len(b), 4 + 4 + 32 + 4 + 11 + 4 + 4 + 1 + 8 + 1 + 1 + 1 + 4 + 26)
for bad_prevhash in (b"", b"x", b"hello", b"x" * 33): for bad_prevhash in (b"", b"x", b"hello", b"x" * 33):
inp.prev_hash = bad_prevhash inp.prev_hash = bad_prevhash

View File

@ -286,10 +286,10 @@ set to `tx.extra_data_chunk`.
Trezor sets `request_type` to `TXORIGINPUT`. `request_details.tx_hash` is the Trezor sets `request_type` to `TXORIGINPUT`. `request_details.tx_hash` is the
transaction hash of the original transaction. transaction hash of the original transaction.
Host must respond with a `TxAckInput` message. All relevant data must be set in The host must respond with a `TxAckInput` message. All relevant data must be set in
`tx.input`. The derivation path and `script_type` are mandatory for all original `tx.input`. The derivation path and `script_type` are mandatory for all original
internal inputs. For each original transaction, one of its original internal inputs must internal inputs. All original internal inputs must also be accompanied with full
be accompanied with a valid signature in the `script_sig` and/or `witness` fields. transaction signature data in the `script_sig` and/or `witness` fields.
### Original transaction output ### Original transaction output

View File

@ -1 +0,0 @@
Avoid accidental build with broken stack protector

View File

@ -1 +0,0 @@
Compress firmware verification coordinates to be able link bootloader into preallocated space.

View File

@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 1.11.0 [May 2022]
### Added
- Bootloader will report version of installed firmware. [#2231]
### Fixed
- Compress firmware verification coordinates to be able link bootloader into preallocated space. [#1884]
### Security
- Erase storage when downgrading below fix_version.
- Avoid accidental build with broken stack protector [#1642]
## 1.10.0 [May 2021] ## 1.10.0 [May 2021]
### Added ### Added
@ -112,3 +125,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Initial import of code. - Initial import of code.
[#1461]: https://github.com/trezor/trezor-firmware/pull/1461 [#1461]: https://github.com/trezor/trezor-firmware/pull/1461
[#1642]: https://github.com/trezor/trezor-firmware/pull/1642
[#1884]: https://github.com/trezor/trezor-firmware/pull/1884
[#2231]: https://github.com/trezor/trezor-firmware/pull/2231

View File

@ -156,6 +156,51 @@ static secbool readprotobufint(const uint8_t **ptr, uint32_t *result) {
return sectrue; return sectrue;
} }
/** Reverse-endian version comparison
*
* Versions are loaded from the header via a packed struct image_header. A
* version is represented as a single uint32_t. Arm is natively little-endian,
* but the version is actually stored as four bytes in major-minor-patch-build
* order. This function implements `cmp` with "lowest" byte first.
*/
static int version_compare(const uint32_t vera, const uint32_t verb) {
int a, b; // signed temp values so that we can safely return a signed result
a = vera & 0xFF;
b = verb & 0xFF;
if (a != b) return a - b;
a = (vera >> 8) & 0xFF;
b = (verb >> 8) & 0xFF;
if (a != b) return a - b;
a = (vera >> 16) & 0xFF;
b = (verb >> 16) & 0xFF;
if (a != b) return a - b;
a = (vera >> 24) & 0xFF;
b = (verb >> 24) & 0xFF;
return a - b;
}
static int should_keep_storage(int old_was_signed,
uint32_t fix_version_current) {
// if the current firmware is unsigned, always erase storage
if (SIG_OK != old_was_signed) return SIG_FAIL;
const image_header *new_hdr = (const image_header *)FW_HEADER;
// if the new header is unsigned, erase storage
if (SIG_OK != signatures_new_ok(new_hdr, NULL)) return SIG_FAIL;
// if the new header hashes don't match flash contents, erase storage
if (SIG_OK != check_firmware_hashes(new_hdr)) return SIG_FAIL;
// going from old-style header to new-style is always an upgrade, keep storage
if (firmware_present_old()) return SIG_OK;
// if the current fix_version is higher than the new one, erase storage
if (version_compare(new_hdr->version, fix_version_current) < 0) {
return SIG_FAIL;
}
return SIG_OK;
}
static void rx_callback(usbd_device *dev, uint8_t ep) { static void rx_callback(usbd_device *dev, uint8_t ep) {
(void)ep; (void)ep;
static uint16_t msg_id = 0xFFFF; static uint16_t msg_id = 0xFFFF;
@ -163,6 +208,7 @@ static void rx_callback(usbd_device *dev, uint8_t ep) {
static uint32_t w; static uint32_t w;
static int wi; static int wi;
static int old_was_signed; static int old_was_signed;
static uint32_t fix_version_current = 0xffffffff;
if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return; if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return;
@ -235,10 +281,13 @@ static void rx_callback(usbd_device *dev, uint8_t ep) {
(const image_header *)FLASH_PTR(FLASH_FWHEADER_START); (const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
old_was_signed = old_was_signed =
signatures_new_ok(hdr, NULL) & check_firmware_hashes(hdr); signatures_new_ok(hdr, NULL) & check_firmware_hashes(hdr);
fix_version_current = hdr->fix_version;
} else if (firmware_present_old()) { } else if (firmware_present_old()) {
old_was_signed = signatures_old_ok(); old_was_signed = signatures_old_ok();
fix_version_current = 0;
} else { } else {
old_was_signed = SIG_FAIL; old_was_signed = SIG_FAIL;
fix_version_current = 0xffffffff;
} }
erase_code_progress(); erase_code_progress();
send_msg_success(dev); send_msg_success(dev);
@ -388,8 +437,7 @@ static void rx_callback(usbd_device *dev, uint8_t ep) {
// 1) old firmware was unsigned or not present // 1) old firmware was unsigned or not present
// 2) signatures are not OK // 2) signatures are not OK
// 3) hashes are not OK // 3) hashes are not OK
if (SIG_OK != old_was_signed || SIG_OK != signatures_new_ok(hdr, NULL) || if (SIG_OK != should_keep_storage(old_was_signed, fix_version_current)) {
SIG_OK != check_firmware_hashes(hdr)) {
// erase storage // erase storage
erase_storage(); erase_storage();
// check erasure // check erasure

View File

@ -37,7 +37,7 @@ static void send_msg_failure(usbd_device *dev, uint8_t code) {
static void send_msg_features(usbd_device *dev) { static void send_msg_features(usbd_device *dev) {
uint8_t response[64]; uint8_t response[64];
memzero(response, sizeof(response)); memzero(response, sizeof(response));
// response: Features message (id 17), payload len 26 // response: Features message (id 17), payload len 26 / 41
// - vendor = "trezor.io" // - vendor = "trezor.io"
// - major_version = VERSION_MAJOR // - major_version = VERSION_MAJOR
// - minor_version = VERSION_MINOR // - minor_version = VERSION_MINOR
@ -45,28 +45,62 @@ static void send_msg_features(usbd_device *dev) {
// - bootloader_mode = True // - bootloader_mode = True
// - firmware_present = True/False // - firmware_present = True/False
// - model = "1" // - model = "1"
memcpy(response, // ? fw_version_major = version_major
// ? fw_version_minor = version_minor
// ? fw_version_patch = version_patch
const bool firmware_present = firmware_present_new();
const image_header *current_hdr = (const image_header *)FLASH_FWHEADER_START;
uint32_t version = firmware_present ? current_hdr->version : 0;
// clang-format off
const uint8_t feature_bytes[] = {
0x0a, // vendor field
0x09, // vendor length
't', 'r', 'e', 'z', 'o', 'r', '.', 'i', 'o',
0x10, VERSION_MAJOR,
0x18, VERSION_MINOR,
0x20, VERSION_PATCH,
0x28, 0x01, // bootloader_mode
0x90, 0x01, // firmware_present field
firmware_present ? 0x01 : 0x00,
0xaa, 0x01, // model field
0x01, // model length
'1',
};
const uint8_t version_bytes[] = {
// fw_version_major
0xb0, 0x01, version & 0xff,
// fw_version_minor
0xb8, 0x01, (version >> 8) & 0xff,
// fw_version_patch
0xc0, 0x01, (version >> 16) & 0xff,
};
uint8_t header_bytes[] = {
// header // header
"?##" '?', '#', '#',
// msg_id // msg_id
"\x00\x11" 0x00, 0x11,
// msg_size // msg_size
"\x00\x00\x00\x1a" 0x00, 0x00, 0x00, sizeof(feature_bytes) + (firmware_present ? sizeof(version_bytes) : 0),
// data };
"\x0a" // clang-format on
"\x09"
"trezor.io" // Check that the response will fit into an USB packet, and also that the
"\x10" VERSION_MAJOR_CHAR "\x18" VERSION_MINOR_CHAR // sizeof expression above fits into a single byte
"\x20" VERSION_PATCH_CHAR _Static_assert(
"\x28" sizeof(feature_bytes) + sizeof(version_bytes) + sizeof(header_bytes) <=
"\x01" 64,
"\x90\x01" "Features response too long");
"\x00"
"\xaa" memcpy(response, header_bytes, sizeof(header_bytes));
"\x01\x01" memcpy(response + sizeof(header_bytes), feature_bytes, sizeof(feature_bytes));
"1", if (firmware_present) {
35); memcpy(response + sizeof(header_bytes) + sizeof(feature_bytes),
response[30] = firmware_present_new() ? 0x01 : 0x00; version_bytes, sizeof(version_bytes));
}
while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) { while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {
} }
} }

View File

@ -1,7 +1,7 @@
#define VERSION_MAJOR 1 #define VERSION_MAJOR 1
#define VERSION_MINOR 10 #define VERSION_MINOR 11
#define VERSION_PATCH 1 #define VERSION_PATCH 0
#define VERSION_MAJOR_CHAR "\x01" #define VERSION_MAJOR_CHAR "\x01"
#define VERSION_MINOR_CHAR "\x0A" #define VERSION_MINOR_CHAR "\x0B"
#define VERSION_PATCH_CHAR "\x01" #define VERSION_PATCH_CHAR "\x00"

View File

@ -1 +0,0 @@
Ensure input's script type and path match the scriptPubKey.

View File

@ -1 +0,0 @@
Trezor will refuse to sign UTXOs that do not match the provided derivation path (e.g., transactions belonging to a different wallet, or synthetic transaction inputs).

View File

@ -1 +0,0 @@
Show "signature is valid" dialog when VerifyMessage succeeds.

View File

@ -1 +0,0 @@
Support Zcash version 5 transaction format.

View File

@ -1 +0,0 @@
Fix domain-only EIP-712 hashes (i.e. when `primaryType`=`EIP712Domain`)

View File

@ -1 +0,0 @@
Add extra check for Taproot scripts validity

View File

@ -1 +0,0 @@
Support Electrum signatures in VerifyMessage.

View File

@ -1 +0,0 @@
Fix legacy technical debt in USB handling (readability and FSM unwanted states).

View File

@ -1 +0,0 @@
\[emulator] Added support for `DebugLinkReseedRandom`.

View File

@ -1 +0,0 @@
\[emulator] Removed support for /dev/urandom or custom entropy source.

View File

@ -1 +0,0 @@
Support unverified external inputs.

View File

@ -1 +0,0 @@
GAME, NIX and POLIS support

View File

@ -1 +0,0 @@
Add firmware hashing functionality.

View File

@ -4,6 +4,39 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 1.11.1 [18th May 2022]
### Added
- Show "signature is valid" dialog when VerifyMessage succeeds. [#1880]
- Add extra check for Taproot scripts validity. [#2077]
- Support Electrum signatures in VerifyMessage. [#2100]
- \[emulator] Added support for `DebugLinkReseedRandom`. [#2115]
- Support unverified external inputs. [#2144]
- Support Zcash version 5 transaction format. [#2031]
- Add firmware hashing functionality. [#2239]
### Changed
- Ensure input's script type and path match the scriptPubKey. [#1018]
- Included bootloader 1.11.0.
### Removed
- \[emulator] Removed support for /dev/urandom or custom entropy source. [#2115]
- GAME, NIX and POLIS support. [#2181]
### Fixed
- Fix domain-only EIP-712 hashes (i.e. when `primaryType`=`EIP712Domain`). [#2036]
- Fix legacy technical debt in USB handling (readability and FSM unwanted states). [#2107]
### Security
- Strict path validations for altcoins.
- Fix soft-lock bypass vulnerability.
- Make Bitcoin path checks as strict as in Trezor T.
### Incompatible changes
- Trezor will refuse to sign UTXOs that do not match the provided derivation path (e.g., transactions belonging to a different wallet, or synthetic transaction inputs). [#1018]
## 1.10.5 [19th January 2022] ## 1.10.5 [19th January 2022]
### Added ### Added
@ -459,6 +492,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#131]: https://github.com/trezor/trezor-firmware/pull/131 [#131]: https://github.com/trezor/trezor-firmware/pull/131
[#965]: https://github.com/trezor/trezor-firmware/pull/965 [#965]: https://github.com/trezor/trezor-firmware/pull/965
[#1018]: https://github.com/trezor/trezor-firmware/pull/1018
[#1030]: https://github.com/trezor/trezor-firmware/pull/1030 [#1030]: https://github.com/trezor/trezor-firmware/pull/1030
[#1098]: https://github.com/trezor/trezor-firmware/pull/1098 [#1098]: https://github.com/trezor/trezor-firmware/pull/1098
[#1105]: https://github.com/trezor/trezor-firmware/pull/1105 [#1105]: https://github.com/trezor/trezor-firmware/pull/1105
@ -497,5 +531,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#1854]: https://github.com/trezor/trezor-firmware/pull/1854 [#1854]: https://github.com/trezor/trezor-firmware/pull/1854
[#1857]: https://github.com/trezor/trezor-firmware/pull/1857 [#1857]: https://github.com/trezor/trezor-firmware/pull/1857
[#1872]: https://github.com/trezor/trezor-firmware/pull/1872 [#1872]: https://github.com/trezor/trezor-firmware/pull/1872
[#1880]: https://github.com/trezor/trezor-firmware/pull/1880
[#1897]: https://github.com/trezor/trezor-firmware/pull/1897 [#1897]: https://github.com/trezor/trezor-firmware/pull/1897
[#1985]: https://github.com/trezor/trezor-firmware/pull/1985 [#1985]: https://github.com/trezor/trezor-firmware/pull/1985
[#2031]: https://github.com/trezor/trezor-firmware/pull/2031
[#2036]: https://github.com/trezor/trezor-firmware/pull/2036
[#2077]: https://github.com/trezor/trezor-firmware/pull/2077
[#2100]: https://github.com/trezor/trezor-firmware/pull/2100
[#2107]: https://github.com/trezor/trezor-firmware/pull/2107
[#2115]: https://github.com/trezor/trezor-firmware/pull/2115
[#2144]: https://github.com/trezor/trezor-firmware/pull/2144
[#2181]: https://github.com/trezor/trezor-firmware/pull/2181
[#2239]: https://github.com/trezor/trezor-firmware/pull/2239

View File

@ -179,6 +179,8 @@ bl_data.h: bl_data.py bootloader.dat
@printf " PYTHON bl_data.py\n" @printf " PYTHON bl_data.py\n"
$(Q)$(PYTHON) bl_data.py $(Q)$(PYTHON) bl_data.py
header.o: version.h
clean:: clean::
rm -f bl_data.h rm -f bl_data.h
find -maxdepth 1 -name "*.mako" | sed 's/.mako$$//' | xargs rm -f find -maxdepth 1 -name "*.mako" | sed 's/.mako$$//' | xargs rm -f

View File

@ -132,6 +132,12 @@ static int known_bootloader(int r, const uint8_t *hash) {
"\xb9\xc7\xf6\x03\xcd\xc7\x30\xe7\x30\x78\x50\xa3\xf4\xd6\x2a\x5c", "\xb9\xc7\xf6\x03\xcd\xc7\x30\xe7\x30\x78\x50\xa3\xf4\xd6\x2a\x5c",
32)) 32))
return 1; // 1.10.0 shipped with fw 1.10.0 return 1; // 1.10.0 shipped with fw 1.10.0
if (0 ==
memcmp(hash,
"\xfa\x12\xa4\x4f\xa0\x5f\xd1\xd2\x05\x39\x35\x8b\x54\xf3\x01\xce"
"\xe4\xc3\x21\x9c\x9f\x1b\xb3\xa5\x77\x2f\xfd\x60\x9a\xf9\xe8\xe2",
32))
return 1; // 1.11.0 shipped with fw 1.11.1
return 0; return 0;
} }
#endif #endif

Binary file not shown.

View File

@ -431,7 +431,10 @@ void session_clearCache(Session *session) {
session->seedCached = false; session->seedCached = false;
} }
void config_lockDevice(void) { storage_lock(); } void config_lockDevice(void) {
fsm_abortWorkflows();
storage_lock();
}
static void get_u2froot_callback(uint32_t iter, uint32_t total) { static void get_u2froot_callback(uint32_t iter, uint32_t total) {
layoutProgress(_("Updating"), 1000 * iter / total); layoutProgress(_("Updating"), 1000 * iter / total);

View File

@ -36,10 +36,6 @@
#include "cash_addr.h" #include "cash_addr.h"
#endif #endif
#define PATH_MAX_ACCOUNT 100
#define PATH_MAX_CHANGE 1
#define PATH_MAX_ADDRESS_INDEX 1000000
uint32_t ser_length(uint32_t len, uint8_t *out) { uint32_t ser_length(uint32_t len, uint8_t *out) {
if (len < 253) { if (len < 253) {
out[0] = len & 0xFF; out[0] = len & 0xFF;
@ -491,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.
const bool check_known = (level >= CoinPathCheckLevel_KNOWN); if (address_n_count == 0) {
const bool check_script_type = (level >= CoinPathCheckLevel_SCRIPT_TYPE); return false;
}
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] == (0x80000000 + 44)) { if (address_n[0] == PATH_HARDENED + 44) {
if (check_known) {
valid = valid && (address_n_count == 5); valid = valid && (address_n_count == 5);
} else { valid = valid && check_cointype(coin, address_n[1], full_check);
valid = valid && (address_n_count >= 2); valid = valid && (address_n[2] & PATH_HARDENED);
} valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
valid = valid && check_cointype(coin, address_n[1], check_known);
if (check_script_type) {
valid = valid && (script_type == InputScriptType_SPENDADDRESS);
valid = valid && (!has_multisig);
}
if (check_known) {
valid = valid && ((address_n[2] & 0x80000000) == 0x80000000);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE); valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDADDRESS);
valid = valid && (!has_multisig);
} }
return valid; return valid;
} }
if (address_n_count > 0 && address_n[0] == (0x80000000 + 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 && (script_type == InputScriptType_SPENDMULTISIG);
}
if (check_known) {
valid = valid && (address_n[1] <= 100); valid = valid && (address_n[1] <= 100);
valid = valid && (address_n[2] <= PATH_MAX_CHANGE); valid = valid && (address_n[2] <= PATH_MAX_CHANGE);
valid = valid && (address_n[3] <= PATH_MAX_ADDRESS_INDEX); 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);
}
if (check_known) {
valid = valid && ((address_n[2] & 0x80000000) == 0x80000000);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= 1000000); valid = valid && (address_n[3] <= 1000000);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); 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 && check_cointype(coin, 0x80000000 | address_n[1], check_known);
if (check_script_type) {
valid = valid && (script_type == InputScriptType_SPENDADDRESS ||
script_type == InputScriptType_SPENDMULTISIG);
}
if (check_known) {
valid = valid && valid = valid &&
((address_n[1] & 0x80000000) == (address_n[2] & 0x80000000)); check_cointype(coin, PATH_HARDENED | address_n[1], full_check);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT); valid = valid && ((address_n[1] & PATH_HARDENED) ==
(address_n[2] & PATH_HARDENED));
valid =
valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= 1000000); valid = valid && (address_n[3] <= 1000000);
valid = valid && (address_n[4] <= PATH_MAX_CHANGE); valid = valid && (address_n[4] <= PATH_MAX_CHANGE);
valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX); 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
// Electrum:
// m / purpose' / coin_type' / account' / type' / change / address_index
if (address_n_count > 0 && address_n[0] == (0x80000000 + 48)) {
if (check_known) {
valid = valid && (address_n_count == 5 || address_n_count == 6); valid = valid && (address_n_count == 5 || address_n_count == 6);
} else { valid = valid && check_cointype(coin, address_n[1], full_check);
valid = valid && (address_n_count >= 2); valid = valid && (address_n[2] & PATH_HARDENED);
} valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
valid = valid && check_cointype(coin, address_n[1], check_known);
if (check_script_type) {
valid = valid && has_multisig;
// we do not support Multisig with Taproot yet
valid = valid && (script_type == InputScriptType_SPENDMULTISIG ||
script_type == InputScriptType_SPENDP2SHWITNESS ||
script_type == InputScriptType_SPENDWITNESS);
}
if (check_known) {
valid = valid && ((address_n[2] & 0x80000000) == 0x80000000);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
if (address_n_count == 5) { if (address_n_count == 5) {
// [OBSOLETE] m/48' Copay Multisig P2SH
// m / purpose' / coin_type' / account' / change / address_index
// NOTE: this pattern is not recognized by trezor-core
valid = valid && (address_n[3] <= PATH_MAX_CHANGE); valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (full_check) {
valid = valid && has_multisig;
valid = valid && (script_type == InputScriptType_SPENDMULTISIG);
}
} else if (address_n_count == 6) { } else if (address_n_count == 6) {
valid = valid && ((address_n[3] & 0x80000000) == 0x80000000); // BIP-48:
valid = valid && ((address_n[3] & 0x7fffffff) <= 3); // m / purpose' / coin_type' / account' / type' / change / address_index
valid = valid && (address_n[3] & PATH_HARDENED);
uint32_t type = address_n[3] & PATH_UNHARDEN_MASK;
valid = valid && (type <= 2);
valid = valid && (type == 0 || coin->has_segwit);
valid = valid && (address_n[4] <= PATH_MAX_CHANGE); valid = valid && (address_n[4] <= PATH_MAX_CHANGE);
valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (address_n[5] <= PATH_MAX_ADDRESS_INDEX);
} else { if (full_check) {
valid = valid && has_multisig;
switch (type) {
case 0:
valid = valid && (script_type == InputScriptType_SPENDMULTISIG ||
script_type == InputScriptType_SPENDADDRESS);
break;
case 1:
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
break;
case 2:
valid = valid && (script_type == InputScriptType_SPENDWITNESS);
break;
default:
return false; 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] == (0x80000000 + 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);
} else { valid = valid && check_cointype(coin, address_n[1], full_check);
valid = valid && (address_n_count >= 2); valid = valid && (address_n[2] & PATH_HARDENED);
} valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
valid = valid && check_cointype(coin, address_n[1], check_known);
if (check_script_type) {
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
}
if (check_known) {
valid = valid && ((address_n[2] & 0x80000000) == 0x80000000);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE); valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
} }
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] == (0x80000000 + 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);
} else { valid = valid && check_cointype(coin, address_n[1], full_check);
valid = valid && (address_n_count >= 2); valid = valid && (address_n[2] & PATH_HARDENED);
} valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
valid = valid && check_cointype(coin, address_n[1], check_known);
if (check_script_type) {
valid = valid && (script_type == InputScriptType_SPENDWITNESS);
}
if (check_known) {
valid = valid && ((address_n[2] & 0x80000000) == 0x80000000);
valid = valid && ((address_n[2] & 0x7fffffff) <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE); valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDWITNESS);
} }
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] == (0x80000000 + 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);
} else { valid = valid && check_cointype(coin, address_n[1], full_check);
valid = valid && (address_n_count >= 2); valid = valid && (address_n[2] & PATH_HARDENED);
} valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
valid = valid && check_cointype(coin, address_n[1], check_known); valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
if (check_script_type) { valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
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] & 0x80000000) == 0x80000000);
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] == (0x80000000 + 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] & 0x80000000) == 0x80000000); valid = valid && (address_n[1] & PATH_HARDENED);
valid = valid && ((address_n[1] & 0x7fffffff) <= 100); valid = valid && ((address_n[1] & PATH_UNHARDEN_MASK) <= 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;
} }
@ -723,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);
} else {
valid = valid && (address_n_count >= 2);
}
valid = valid =
valid && check_cointype(coin, 0x80000000 | address_n[1], check_known); valid && check_cointype(coin, PATH_HARDENED | address_n[1], full_check);
if (check_script_type) { valid = valid && ((address_n[1] & PATH_HARDENED) == 0);
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
}
if (check_known) {
valid = valid && ((address_n[1] & 0x80000000) == 0);
valid = valid && (address_n[2] <= PATH_MAX_ACCOUNT); valid = valid && (address_n[2] <= PATH_MAX_ACCOUNT);
valid = valid && (address_n[3] <= PATH_MAX_CHANGE); valid = valid && (address_n[3] <= PATH_MAX_CHANGE);
valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX); valid = valid && (address_n[4] <= PATH_MAX_ADDRESS_INDEX);
if (full_check) {
valid = valid && (script_type == InputScriptType_SPENDP2SHWITNESS);
} }
return valid; return valid;
} }
// we allow unknown paths only when a full check is not required // unknown path
return level == CoinPathCheckLevel_BASIC; return false;
} }

View File

@ -32,11 +32,11 @@
#include "messages-bitcoin.pb.h" #include "messages-bitcoin.pb.h"
#include "messages-crypto.pb.h" #include "messages-crypto.pb.h"
typedef enum _CoinPathCheckLevel { #define PATH_HARDENED 0x80000000
CoinPathCheckLevel_BASIC = 0, #define PATH_UNHARDEN_MASK 0x7fffffff
CoinPathCheckLevel_KNOWN = 1, #define PATH_MAX_ACCOUNT 100
CoinPathCheckLevel_SCRIPT_TYPE = 2, #define PATH_MAX_CHANGE 1
} CoinPathCheckLevel; #define PATH_MAX_ADDRESS_INDEX 1000000
#define ser_length_size(len) ((len) < 253 ? 1 : (len) < 0x10000 ? 3 : 5) #define ser_length_size(len) ((len) < 253 ? 1 : (len) < 0x10000 ? 3 : 5)
@ -79,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

View File

@ -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;
}

View File

@ -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

View File

@ -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,6 +28,8 @@ 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:
@ -31,4 +38,24 @@ def align_suffix(n):
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

View File

@ -100,6 +100,12 @@ static uint8_t msg_resp[MSG_OUT_DECODED_SIZE] __attribute__((aligned));
return; \ return; \
} }
#define CHECK_UNLOCKED \
if (!session_isUnlocked()) { \
layoutHome(); \
return; \
}
#define CHECK_PARAM(cond, errormsg) \ #define CHECK_PARAM(cond, errormsg) \
if (!(cond)) { \ if (!(cond)) { \
fsm_sendFailure(FailureType_Failure_DataError, (errormsg)); \ fsm_sendFailure(FailureType_Failure_DataError, (errormsg)); \
@ -370,6 +376,27 @@ void fsm_msgRebootToBootloader(void) {
#endif #endif
} }
void fsm_abortWorkflows(void) {
recovery_abort();
signing_abort();
#if !BITCOIN_ONLY
ethereum_signing_abort();
stellar_signingAbort();
#endif
}
bool fsm_layoutPathWarning(void) {
layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL,
_("Wrong address path"), _("for selected coin."), NULL,
_("Continue at your"), _("own risk!"), NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_UnknownDerivationPath,
false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
return false;
}
return true;
}
#include "fsm_msg_coin.h" #include "fsm_msg_coin.h"
#include "fsm_msg_common.h" #include "fsm_msg_common.h"
#include "fsm_msg_crypto.h" #include "fsm_msg_crypto.h"

View File

@ -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"
@ -143,4 +144,11 @@ void fsm_msgRebootToBootloader(void);
bool fsm_layoutSignMessage(const uint8_t *msg, uint32_t len); 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_checkCoinPath(const CoinInfo *coin, InputScriptType script_type,
uint32_t address_n_count, const uint32_t *address_n,
bool has_multisig, bool show_warning);
void fsm_abortWorkflows(void);
#endif #endif

View File

@ -37,7 +37,7 @@ void fsm_msgGetPublicKey(const GetPublicKey *msg) {
// derive m/0' to obtain root_fingerprint // derive m/0' to obtain root_fingerprint
uint32_t root_fingerprint; uint32_t root_fingerprint;
uint32_t path[1] = {0x80000000 | 0}; uint32_t path[1] = {PATH_HARDENED | 0};
HDNode *node = fsm_getDerivedNode(curve, path, 1, &root_fingerprint); HDNode *node = fsm_getDerivedNode(curve, path, 1, &root_fingerprint);
if (!node) return; if (!node) return;
@ -146,34 +146,32 @@ void fsm_msgSignTx(const SignTx *msg) {
} }
void fsm_msgTxAck(TxAck *msg) { void fsm_msgTxAck(TxAck *msg) {
CHECK_UNLOCKED
CHECK_PARAM(msg->has_tx, _("No transaction provided")); CHECK_PARAM(msg->has_tx, _("No transaction provided"));
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)) {
return true;
}
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict && if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict &&
!coin_path_check(coin, script_type, address_n_count, address_n, !coin_path_check(coin, script_type, address_n_count, address_n,
has_multisig, CoinPathCheckLevel_KNOWN)) { has_multisig, false)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path")); fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
layoutHome();
return false; return false;
} }
layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL, if (show_warning) {
_("Wrong address path"), _("for selected coin."), NULL, return fsm_layoutPathWarning();
_("Continue at your"), _("own risk!"), NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_UnknownDerivationPath,
false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return false;
}
} }
return true; return true;
} }
@ -186,6 +184,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;
@ -225,11 +231,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) {
@ -274,7 +275,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;
} }

View File

@ -95,8 +95,7 @@ bool get_features(Features *resp) {
} }
void fsm_msgInitialize(const Initialize *msg) { void fsm_msgInitialize(const Initialize *msg) {
recovery_abort(); fsm_abortWorkflows();
signing_abort();
uint8_t *session_id; uint8_t *session_id;
if (msg && msg->has_session_id) { if (msg && msg->has_session_id) {
@ -251,6 +250,8 @@ void fsm_msgWipeDevice(const WipeDevice *msg) {
} }
void fsm_msgGetEntropy(const GetEntropy *msg) { void fsm_msgGetEntropy(const GetEntropy *msg) {
CHECK_PIN
#if !DEBUG_RNG #if !DEBUG_RNG
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("send entropy?"), NULL, NULL, _("Do you really want to"), _("send entropy?"), NULL, NULL,
@ -345,11 +346,7 @@ void fsm_msgBackupDevice(const BackupDevice *msg) {
void fsm_msgCancel(const Cancel *msg) { void fsm_msgCancel(const Cancel *msg) {
(void)msg; (void)msg;
recovery_abort(); fsm_abortWorkflows();
signing_abort();
#if !BITCOIN_ONLY
ethereum_signing_abort();
#endif
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
} }
@ -516,9 +513,15 @@ void fsm_msgRecoveryDevice(const RecoveryDevice *msg) {
msg->has_u2f_counter ? msg->u2f_counter : 0, dry_run); msg->has_u2f_counter ? msg->u2f_counter : 0, dry_run);
} }
void fsm_msgWordAck(const WordAck *msg) { recovery_word(msg->word); } void fsm_msgWordAck(const WordAck *msg) {
CHECK_UNLOCKED
recovery_word(msg->word);
}
void fsm_msgSetU2FCounter(const SetU2FCounter *msg) { void fsm_msgSetU2FCounter(const SetU2FCounter *msg) {
CHECK_PIN
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you want to set"), _("the U2F counter?"), NULL, NULL, _("Do you want to set"), _("the U2F counter?"), NULL, NULL,
NULL, NULL); NULL, NULL);
@ -533,6 +536,8 @@ void fsm_msgSetU2FCounter(const SetU2FCounter *msg) {
} }
void fsm_msgGetNextU2FCounter() { void fsm_msgGetNextU2FCounter() {
CHECK_PIN
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you want to"), _("increase and retrieve"), _("Do you want to"), _("increase and retrieve"),
_("the U2F counter?"), NULL, NULL, NULL); _("the U2F counter?"), NULL, NULL, NULL);

View File

@ -75,6 +75,8 @@ void fsm_msgSignIdentity(const SignIdentity *msg) {
CHECK_INITIALIZED CHECK_INITIALIZED
CHECK_PIN
layoutSignIdentity(&(msg->identity), layoutSignIdentity(&(msg->identity),
msg->has_challenge_visual ? msg->challenge_visual : 0); msg->has_challenge_visual ? msg->challenge_visual : 0);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
@ -83,8 +85,6 @@ void fsm_msgSignIdentity(const SignIdentity *msg) {
return; return;
} }
CHECK_PIN
uint8_t hash[32]; uint8_t hash[32];
if (cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { if (cryptoIdentityFingerprint(&(msg->identity), hash) == 0) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity"));
@ -93,14 +93,14 @@ void fsm_msgSignIdentity(const SignIdentity *msg) {
} }
uint32_t address_n[5]; uint32_t address_n[5];
address_n[0] = 0x80000000 | 13; address_n[0] = PATH_HARDENED | 13;
address_n[1] = 0x80000000 | hash[0] | (hash[1] << 8) | (hash[2] << 16) | address_n[1] = PATH_HARDENED | hash[0] | (hash[1] << 8) | (hash[2] << 16) |
((uint32_t)hash[3] << 24); ((uint32_t)hash[3] << 24);
address_n[2] = 0x80000000 | hash[4] | (hash[5] << 8) | (hash[6] << 16) | address_n[2] = PATH_HARDENED | hash[4] | (hash[5] << 8) | (hash[6] << 16) |
((uint32_t)hash[7] << 24); ((uint32_t)hash[7] << 24);
address_n[3] = 0x80000000 | hash[8] | (hash[9] << 8) | (hash[10] << 16) | address_n[3] = PATH_HARDENED | hash[8] | (hash[9] << 8) | (hash[10] << 16) |
((uint32_t)hash[11] << 24); ((uint32_t)hash[11] << 24);
address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | address_n[4] = PATH_HARDENED | hash[12] | (hash[13] << 8) | (hash[14] << 16) |
((uint32_t)hash[15] << 24); ((uint32_t)hash[15] << 24);
const char *curve = SECP256K1_NAME; const char *curve = SECP256K1_NAME;
@ -179,6 +179,8 @@ void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg) {
CHECK_INITIALIZED CHECK_INITIALIZED
CHECK_PIN
layoutDecryptIdentity(&msg->identity); layoutDecryptIdentity(&msg->identity);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
@ -186,8 +188,6 @@ void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg) {
return; return;
} }
CHECK_PIN
uint8_t hash[32]; uint8_t hash[32];
if (cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { if (cryptoIdentityFingerprint(&(msg->identity), hash) == 0) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity"));
@ -196,14 +196,14 @@ void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg) {
} }
uint32_t address_n[5]; uint32_t address_n[5];
address_n[0] = 0x80000000 | 17; address_n[0] = PATH_HARDENED | 17;
address_n[1] = 0x80000000 | hash[0] | (hash[1] << 8) | (hash[2] << 16) | address_n[1] = PATH_HARDENED | hash[0] | (hash[1] << 8) | (hash[2] << 16) |
((uint32_t)hash[3] << 24); ((uint32_t)hash[3] << 24);
address_n[2] = 0x80000000 | hash[4] | (hash[5] << 8) | (hash[6] << 16) | address_n[2] = PATH_HARDENED | hash[4] | (hash[5] << 8) | (hash[6] << 16) |
((uint32_t)hash[7] << 24); ((uint32_t)hash[7] << 24);
address_n[3] = 0x80000000 | hash[8] | (hash[9] << 8) | (hash[10] << 16) | address_n[3] = PATH_HARDENED | hash[8] | (hash[9] << 8) | (hash[10] << 16) |
((uint32_t)hash[11] << 24); ((uint32_t)hash[11] << 24);
address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | address_n[4] = PATH_HARDENED | hash[12] | (hash[13] << 8) | (hash[14] << 16) |
((uint32_t)hash[15] << 24); ((uint32_t)hash[15] << 24);
const char *curve = SECP256K1_NAME; const char *curve = SECP256K1_NAME;
@ -235,6 +235,22 @@ void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg) {
layoutHome(); layoutHome();
} }
static bool fsm_checkCosiPath(uint32_t address_n_count,
const uint32_t *address_n) {
// The path should typically match "m / 10018' / [0-9]'", but we allow
// any path from the SLIP-18 domain "m / 10018' / *".
if (address_n_count >= 1 && address_n[0] == PATH_HARDENED + 10018) {
return true;
}
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
return false;
}
return fsm_layoutPathWarning();
}
void fsm_msgCosiCommit(const CosiCommit *msg) { void fsm_msgCosiCommit(const CosiCommit *msg) {
RESP_INIT(CosiCommitment); RESP_INIT(CosiCommitment);
@ -242,6 +258,13 @@ void fsm_msgCosiCommit(const CosiCommit *msg) {
CHECK_PARAM(msg->has_data, _("No data provided")); CHECK_PARAM(msg->has_data, _("No data provided"));
CHECK_PIN
if (!fsm_checkCosiPath(msg->address_n_count, msg->address_n)) {
layoutHome();
return;
}
layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes,
msg->data.size, false); msg->data.size, false);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
@ -250,8 +273,6 @@ void fsm_msgCosiCommit(const CosiCommit *msg) {
return; return;
} }
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL); msg->address_n_count, NULL);
if (!node) return; if (!node) return;
@ -285,6 +306,13 @@ void fsm_msgCosiSign(const CosiSign *msg) {
CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32, CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32,
_("Invalid global pubkey")); _("Invalid global pubkey"));
if (!fsm_checkCosiPath(msg->address_n_count, msg->address_n)) {
layoutHome();
return;
}
CHECK_PIN
layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes,
msg->data.size, true); msg->data.size, true);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
@ -293,8 +321,6 @@ void fsm_msgCosiSign(const CosiSign *msg) {
return; return;
} }
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL); msg->address_n_count, NULL);
if (!node) return; if (!node) return;

View File

@ -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;
@ -91,6 +125,8 @@ void fsm_msgEthereumSignTxEIP1559(const EthereumSignTxEIP1559 *msg) {
} }
void fsm_msgEthereumTxAck(const EthereumTxAck *msg) { void fsm_msgEthereumTxAck(const EthereumTxAck *msg) {
CHECK_UNLOCKED
ethereum_signing_txack(msg); ethereum_signing_txack(msg);
} }
@ -101,16 +137,25 @@ 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] & 0x7fffffff) : 0; (msg->address_n_count > 1) ? (msg->address_n[1] & PATH_UNHARDEN_MASK) : 0;
bool rskip60 = false; bool rskip60 = false;
uint64_t chain_id = 0; uint64_t chain_id = 0;
// constants from trezor-common/defs/ethereum/networks.json // constants from trezor-common/defs/ethereum/networks.json
@ -150,12 +195,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 +282,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 +303,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;
} }

View File

@ -17,6 +17,21 @@
* 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_nemCheckPath(uint32_t address_n_count,
const uint32_t *address_n, uint8_t network) {
if (nem_path_check(address_n_count, address_n, network, true)) {
return true;
}
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict &&
!nem_path_check(address_n_count, address_n, network, false)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
return false;
}
return fsm_layoutPathWarning();
}
void fsm_msgNEMGetAddress(NEMGetAddress *msg) { void fsm_msgNEMGetAddress(NEMGetAddress *msg) {
if (!msg->has_network) { if (!msg->has_network) {
msg->network = NEM_NETWORK_MAINNET; msg->network = NEM_NETWORK_MAINNET;
@ -31,11 +46,19 @@ void fsm_msgNEMGetAddress(NEMGetAddress *msg) {
RESP_INIT(NEMAddress); RESP_INIT(NEMAddress);
if (!fsm_nemCheckPath(msg->address_n_count, msg->address_n, msg->network)) {
layoutHome();
return;
}
HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n, HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n,
msg->address_n_count, NULL); msg->address_n_count, NULL);
if (!node) return; if (!node) return;
if (!hdnode_get_nem_address(node, msg->network, resp->address)) return; if (!hdnode_get_nem_address(node, msg->network, resp->address)) {
layoutHome();
return;
}
if (msg->has_show_display && msg->show_display) { if (msg->has_show_display && msg->show_display) {
char desc[16]; char desc[16];
@ -116,6 +139,12 @@ void fsm_msgNEMSignTx(NEMSignTx *msg) {
RESP_INIT(NEMSignedTx); RESP_INIT(NEMSignedTx);
if (!fsm_nemCheckPath(msg->transaction.address_n_count,
msg->transaction.address_n, msg->transaction.network)) {
layoutHome();
return;
}
HDNode *node = HDNode *node =
fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n, fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n,
msg->transaction.address_n_count, NULL); msg->transaction.address_n_count, NULL);
@ -304,6 +333,8 @@ void fsm_msgNEMDecryptMessage(NEMDecryptMessage *msg) {
CHECK_PARAM(msg->has_public_key, _("No public key provided")); CHECK_PARAM(msg->has_public_key, _("No public key provided"));
CHECK_PARAM(msg->public_key.size == 32, _("Invalid public key")); CHECK_PARAM(msg->public_key.size == 32, _("Invalid public key"));
CHECK_PIN
char address[NEM_ADDRESS_SIZE + 1]; char address[NEM_ADDRESS_SIZE + 1];
nem_get_address(msg->public_key.bytes, msg->network, address); nem_get_address(msg->public_key.bytes, msg->network, address);
@ -315,8 +346,10 @@ void fsm_msgNEMDecryptMessage(NEMDecryptMessage *msg) {
return; return;
} }
CHECK_PIN if (!fsm_nemCheckPath(msg->address_n_count, msg->address_n, msg->network)) {
layoutHome();
return;
}
const HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n, const HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n,
msg->address_n_count, NULL); msg->address_n_count, NULL);
if (!node) return; if (!node) return;

View File

@ -17,6 +17,20 @@
* 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_stellarCheckPath(uint32_t address_n_count,
const uint32_t *address_n) {
if (stellar_path_check(address_n_count, address_n)) {
return true;
}
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
return false;
}
return fsm_layoutPathWarning();
}
void fsm_msgStellarGetAddress(const StellarGetAddress *msg) { void fsm_msgStellarGetAddress(const StellarGetAddress *msg) {
RESP_INIT(StellarAddress); RESP_INIT(StellarAddress);
@ -24,10 +38,16 @@ void fsm_msgStellarGetAddress(const StellarGetAddress *msg) {
CHECK_PIN CHECK_PIN
if (!fsm_stellarCheckPath(msg->address_n_count, msg->address_n)) {
layoutHome();
return;
}
const HDNode *node = stellar_deriveNode(msg->address_n, msg->address_n_count); const HDNode *node = stellar_deriveNode(msg->address_n, msg->address_n_count);
if (!node) { if (!node) {
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive private key")); _("Failed to derive private key"));
layoutHome();
return; return;
} }
@ -55,6 +75,11 @@ void fsm_msgStellarSignTx(const StellarSignTx *msg) {
CHECK_INITIALIZED CHECK_INITIALIZED
CHECK_PIN CHECK_PIN
if (!fsm_stellarCheckPath(msg->address_n_count, msg->address_n)) {
layoutHome();
return;
}
if (!stellar_signingInit(msg)) { if (!stellar_signingInit(msg)) {
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive private key")); _("Failed to derive private key"));
@ -72,6 +97,8 @@ void fsm_msgStellarSignTx(const StellarSignTx *msg) {
} }
void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg) { void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmCreateAccountOp(msg)) return; if (!stellar_confirmCreateAccountOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -90,6 +117,8 @@ void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg) {
} }
void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg) { void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg) {
CHECK_UNLOCKED
// This will display additional dialogs to the user // This will display additional dialogs to the user
if (!stellar_confirmPaymentOp(msg)) return; if (!stellar_confirmPaymentOp(msg)) return;
@ -111,6 +140,8 @@ void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg) {
void fsm_msgStellarPathPaymentStrictReceiveOp( void fsm_msgStellarPathPaymentStrictReceiveOp(
const StellarPathPaymentStrictReceiveOp *msg) { const StellarPathPaymentStrictReceiveOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmPathPaymentStrictReceiveOp(msg)) return; if (!stellar_confirmPathPaymentStrictReceiveOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -130,6 +161,8 @@ void fsm_msgStellarPathPaymentStrictReceiveOp(
void fsm_msgStellarPathPaymentStrictSendOp( void fsm_msgStellarPathPaymentStrictSendOp(
const StellarPathPaymentStrictSendOp *msg) { const StellarPathPaymentStrictSendOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmPathPaymentStrictSendOp(msg)) return; if (!stellar_confirmPathPaymentStrictSendOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -148,6 +181,8 @@ void fsm_msgStellarPathPaymentStrictSendOp(
} }
void fsm_msgStellarManageBuyOfferOp(const StellarManageBuyOfferOp *msg) { void fsm_msgStellarManageBuyOfferOp(const StellarManageBuyOfferOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmManageBuyOfferOp(msg)) return; if (!stellar_confirmManageBuyOfferOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -166,6 +201,8 @@ void fsm_msgStellarManageBuyOfferOp(const StellarManageBuyOfferOp *msg) {
} }
void fsm_msgStellarManageSellOfferOp(const StellarManageSellOfferOp *msg) { void fsm_msgStellarManageSellOfferOp(const StellarManageSellOfferOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmManageSellOfferOp(msg)) return; if (!stellar_confirmManageSellOfferOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -185,6 +222,8 @@ void fsm_msgStellarManageSellOfferOp(const StellarManageSellOfferOp *msg) {
void fsm_msgStellarCreatePassiveSellOfferOp( void fsm_msgStellarCreatePassiveSellOfferOp(
const StellarCreatePassiveSellOfferOp *msg) { const StellarCreatePassiveSellOfferOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmCreatePassiveSellOfferOp(msg)) return; if (!stellar_confirmCreatePassiveSellOfferOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -203,6 +242,8 @@ void fsm_msgStellarCreatePassiveSellOfferOp(
} }
void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg) { void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmSetOptionsOp(msg)) return; if (!stellar_confirmSetOptionsOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -221,6 +262,8 @@ void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg) {
} }
void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg) { void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmChangeTrustOp(msg)) return; if (!stellar_confirmChangeTrustOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -239,6 +282,8 @@ void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg) {
} }
void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg) { void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmAllowTrustOp(msg)) return; if (!stellar_confirmAllowTrustOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -257,6 +302,8 @@ void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg) {
} }
void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg) { void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmAccountMergeOp(msg)) return; if (!stellar_confirmAccountMergeOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -275,6 +322,8 @@ void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg) {
} }
void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg) { void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmManageDataOp(msg)) return; if (!stellar_confirmManageDataOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {
@ -293,6 +342,8 @@ void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg) {
} }
void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg) { void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg) {
CHECK_UNLOCKED
if (!stellar_confirmBumpSequenceOp(msg)) return; if (!stellar_confirmBumpSequenceOp(msg)) return;
if (stellar_allOperationsConfirmed()) { if (stellar_allOperationsConfirmed()) {

View File

@ -27,6 +27,7 @@
#include "bignum.h" #include "bignum.h"
#include "bitmaps.h" #include "bitmaps.h"
#include "config.h" #include "config.h"
#include "crypto.h"
#include "gettext.h" #include "gettext.h"
#include "layout2.h" #include "layout2.h"
#include "memzero.h" #include "memzero.h"
@ -43,10 +44,10 @@
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
static const char *slip44_extras(uint32_t coin_type) { static const char *slip44_extras(uint32_t coin_type) {
if ((coin_type & 0x80000000) == 0) { if ((coin_type & PATH_HARDENED) == 0) {
return 0; return 0;
} }
switch (coin_type & 0x7fffffff) { switch (coin_type & PATH_UNHARDEN_MASK) {
case 40: case 40:
return "EXP"; // Expanse return "EXP"; // Expanse
case 43: case 43:
@ -82,14 +83,15 @@ static const char *address_n_str(const uint32_t *address_n,
// known BIP44/49/84/86 path // known BIP44/49/84/86 path
static char path[100]; static char path[100];
if (address_n_count == 5 && if (address_n_count == 5 &&
(address_n[0] == (0x80000000 + 44) || address_n[0] == (0x80000000 + 49) || (address_n[0] == (PATH_HARDENED + 44) ||
address_n[0] == (0x80000000 + 84) || address_n[0] == (PATH_HARDENED + 49) ||
address_n[0] == (0x80000000 + 86)) && address_n[0] == (PATH_HARDENED + 84) ||
(address_n[1] & 0x80000000) && (address_n[2] & 0x80000000) && address_n[0] == (PATH_HARDENED + 86)) &&
(address_n[1] & PATH_HARDENED) && (address_n[2] & PATH_HARDENED) &&
(address_n[3] <= 1) && (address_n[4] <= BIP32_MAX_LAST_ELEMENT)) { (address_n[3] <= 1) && (address_n[4] <= BIP32_MAX_LAST_ELEMENT)) {
bool taproot = (address_n[0] == (0x80000000 + 86)); bool taproot = (address_n[0] == (PATH_HARDENED + 86));
bool native_segwit = (address_n[0] == (0x80000000 + 84)); bool native_segwit = (address_n[0] == (PATH_HARDENED + 84));
bool p2sh_segwit = (address_n[0] == (0x80000000 + 49)); bool p2sh_segwit = (address_n[0] == (PATH_HARDENED + 49));
bool legacy = false; bool legacy = false;
const CoinInfo *coin = coinBySlip44(address_n[1]); const CoinInfo *coin = coinBySlip44(address_n[1]);
const char *abbr = 0; const char *abbr = 0;
@ -118,8 +120,8 @@ static const char *address_n_str(const uint32_t *address_n,
} }
} }
const uint32_t accnum = address_is_account const uint32_t accnum = address_is_account
? ((address_n[4] & 0x7fffffff) + 1) ? ((address_n[4] & PATH_UNHARDEN_MASK) + 1)
: (address_n[2] & 0x7fffffff) + 1; : (address_n[2] & PATH_UNHARDEN_MASK) + 1;
if (abbr && accnum < 100) { if (abbr && accnum < 100) {
memzero(path, sizeof(path)); memzero(path, sizeof(path));
strlcpy(path, abbr, sizeof(path)); strlcpy(path, abbr, sizeof(path));
@ -164,11 +166,11 @@ static const char *address_n_str(const uint32_t *address_n,
for (int n = (int)address_n_count - 1; n >= 0; n--) { for (int n = (int)address_n_count - 1; n >= 0; n--) {
uint32_t i = address_n[n]; uint32_t i = address_n[n];
if (i & 0x80000000) { if (i & PATH_HARDENED) {
*c = '\''; *c = '\'';
c--; c--;
} }
i = i & 0x7fffffff; i = i & PATH_UNHARDEN_MASK;
do { do {
*c = '0' + (i % 10); *c = '0' + (i % 10);
c--; c--;
@ -596,6 +598,13 @@ void layoutChangeCountOverThreshold(uint32_t change_count) {
_("Continue?"), NULL); _("Continue?"), NULL);
} }
void layoutConfirmUnverifiedExternalInputs(void) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Warning!"), _("The transaction"),
_("contains unverified"), _("external inputs."),
_("Continue?"), NULL);
}
void layoutConfirmNondefaultLockTime(uint32_t lock_time, void layoutConfirmNondefaultLockTime(uint32_t lock_time,
bool lock_time_disabled) { bool lock_time_disabled) {
if (lock_time_disabled) { if (lock_time_disabled) {
@ -1176,8 +1185,10 @@ void layoutNEMLevy(const NEMMosaicDefinition *definition, uint8_t network) {
static inline bool is_slip18(const uint32_t *address_n, static inline bool is_slip18(const uint32_t *address_n,
size_t address_n_count) { size_t address_n_count) {
return address_n_count == 2 && address_n[0] == (0x80000000 + 10018) && // m / 10018' / [0-9]'
(address_n[1] & 0x80000000) && (address_n[1] & 0x7FFFFFFF) <= 9; return address_n_count == 2 && address_n[0] == (PATH_HARDENED + 10018) &&
(address_n[1] & PATH_HARDENED) &&
(address_n[1] & PATH_UNHARDEN_MASK) <= 9;
} }
void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count, void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count,
@ -1187,10 +1198,10 @@ void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count,
if (is_slip18(address_n, address_n_count)) { if (is_slip18(address_n, address_n_count)) {
if (final_sign) { if (final_sign) {
strlcpy(desc_buf, _("CoSi sign index #?"), sizeof(desc_buf)); strlcpy(desc_buf, _("CoSi sign index #?"), sizeof(desc_buf));
desc_buf[16] = '0' + (address_n[1] & 0x7FFFFFFF); desc_buf[16] = '0' + (address_n[1] & PATH_UNHARDEN_MASK);
} else { } else {
strlcpy(desc_buf, _("CoSi commit index #?"), sizeof(desc_buf)); strlcpy(desc_buf, _("CoSi commit index #?"), sizeof(desc_buf));
desc_buf[18] = '0' + (address_n[1] & 0x7FFFFFFF); desc_buf[18] = '0' + (address_n[1] & PATH_UNHARDEN_MASK);
} }
desc = desc_buf; desc = desc_buf;
} }

View File

@ -68,6 +68,7 @@ void layoutConfirmModifyFee(const CoinInfo *coin, AmountUnit amount_unit,
void layoutFeeOverThreshold(const CoinInfo *coin, AmountUnit amount_unit, void layoutFeeOverThreshold(const CoinInfo *coin, AmountUnit amount_unit,
uint64_t fee); uint64_t fee);
void layoutChangeCountOverThreshold(uint32_t change_count); void layoutChangeCountOverThreshold(uint32_t change_count);
void layoutConfirmUnverifiedExternalInputs(void);
void layoutConfirmNondefaultLockTime(uint32_t lock_time, void layoutConfirmNondefaultLockTime(uint32_t lock_time,
bool lock_time_disabled); bool lock_time_disabled);
void layoutVerifyAddress(const CoinInfo *coin, const char *address); void layoutVerifyAddress(const CoinInfo *coin, const char *address);

View File

@ -20,6 +20,7 @@
#include "nem2.h" #include "nem2.h"
#include "aes/aes.h" #include "aes/aes.h"
#include "crypto.h"
#include "fsm.h" #include "fsm.h"
#include "gettext.h" #include "gettext.h"
#include "layout2.h" #include "layout2.h"
@ -741,3 +742,44 @@ bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition,
return false; return false;
} }
} }
bool nem_path_check(uint32_t address_n_count, const uint32_t *address_n,
uint8_t network, bool check_coin_type) {
bool valid = (address_n_count >= 3);
valid = valid && (address_n[0] == (PATH_HARDENED | 44));
valid = valid && (address_n[1] == (PATH_HARDENED | 43) ||
address_n[1] == (PATH_HARDENED | 1));
valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
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'
// No further checks required.
} else if (address_n_count == 5) {
// NanoWallet compatibility path
// "m/44'/coin_type'/account'/0'/0'"
valid = valid && (address_n[3] == (PATH_HARDENED | 0));
valid = valid && (address_n[4] == (PATH_HARDENED | 0));
} else {
return false;
}
if (check_coin_type) {
// Check that the appropriate coin_type is set for the given network.
switch (network) {
case NEM_NETWORK_MAINNET:
case NEM_NETWORK_MIJIN:
valid = valid && (address_n[1] == (PATH_HARDENED | 43));
break;
case NEM_NETWORK_TESTNET:
valid = valid && (address_n[1] == (PATH_HARDENED | 1));
break;
default:
return false;
}
}
return valid;
}

View File

@ -100,6 +100,9 @@ bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition,
uint64_t quantity, const bignum256 *multiplier, uint64_t quantity, const bignum256 *multiplier,
uint8_t network, char *str_out, size_t size); uint8_t network, char *str_out, size_t size);
bool nem_path_check(uint32_t address_n_count, const uint32_t *address_n,
uint8_t network, bool check_coin_type);
static inline void nem_mosaicFormatName(const char *namespace, static inline void nem_mosaicFormatName(const char *namespace,
const char *mosaic, char *str_out, const char *mosaic, char *str_out,
size_t size) { size_t size) {

View File

@ -57,6 +57,9 @@ enum {
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
STAGE_REQUEST_3_PREV_EXTRADATA, STAGE_REQUEST_3_PREV_EXTRADATA,
#endif #endif
STAGE_REQUEST_3_ORIG_INPUT,
STAGE_REQUEST_3_ORIG_OUTPUT,
STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT,
STAGE_REQUEST_4_INPUT, STAGE_REQUEST_4_INPUT,
STAGE_REQUEST_4_OUTPUT, STAGE_REQUEST_4_OUTPUT,
STAGE_REQUEST_NONLEGACY_INPUT, STAGE_REQUEST_NONLEGACY_INPUT,
@ -66,6 +69,7 @@ enum {
STAGE_REQUEST_DECRED_WITNESS, STAGE_REQUEST_DECRED_WITNESS,
#endif #endif
} signing_stage; } signing_stage;
static bool foreign_address_confirmed; // indicates that user approved warning
static bool taproot_only; // indicates whether all internal inputs are Taproot static bool taproot_only; // indicates whether all internal inputs are Taproot
static uint32_t idx1; // The index of the input or output in the current tx static uint32_t idx1; // The index of the input or output in the current tx
// which is being processed, signed or serialized. // which is being processed, signed or serialized.
@ -93,16 +97,15 @@ static uint8_t sig[64]; // Used in Phase 1 to store signature of original tx
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
static uint8_t decred_hash_prefix[32]; static uint8_t decred_hash_prefix[32];
#endif #endif
static uint8_t hash_inputs_check[32];
static uint64_t total_in, total_out, change_out; static uint64_t total_in, total_out, change_out;
static uint64_t orig_total_in, orig_total_out, orig_change_out; static uint64_t orig_total_in, orig_total_out, orig_change_out;
static uint32_t next_legacy_input;
static uint32_t progress, progress_step, progress_meta_step; static uint32_t progress, progress_step, progress_meta_step;
static uint32_t tx_weight; static uint32_t tx_weight;
typedef struct { typedef struct {
uint32_t inputs_count; uint32_t inputs_count;
uint32_t outputs_count; uint32_t outputs_count;
uint32_t next_legacy_input;
uint32_t min_sequence; uint32_t min_sequence;
bool multisig_fp_set; bool multisig_fp_set;
bool multisig_fp_mismatch; bool multisig_fp_mismatch;
@ -118,11 +121,13 @@ typedef struct {
uint32_t branch_id; uint32_t branch_id;
uint8_t hash_header[32]; uint8_t hash_header[32];
#endif #endif
Hasher hasher_check;
Hasher hasher_prevouts; Hasher hasher_prevouts;
Hasher hasher_amounts; Hasher hasher_amounts;
Hasher hasher_scriptpubkeys; Hasher hasher_scriptpubkeys;
Hasher hasher_sequences; Hasher hasher_sequences;
Hasher hasher_outputs; Hasher hasher_outputs;
uint8_t hash_inputs_check[32];
uint8_t hash_prevouts[32]; uint8_t hash_prevouts[32];
uint8_t hash_amounts[32]; uint8_t hash_amounts[32];
uint8_t hash_scriptpubkeys[32]; uint8_t hash_scriptpubkeys[32];
@ -137,9 +142,6 @@ static TxInfo info;
/* Variables specific to replacement transactions. */ /* Variables specific to replacement transactions. */
static bool is_replacement; // Is this a replacement transaction? static bool is_replacement; // Is this a replacement transaction?
static bool have_orig_verif_input; // Is orig_verif_input, sig and node set?
static uint32_t orig_verif_input_idx; // Index of orig_verif_input in orig tx.
static TxInputType orig_verif_input; // The input for signature verification.
static TxInfo orig_info; static TxInfo orig_info;
static uint8_t orig_hash[32]; // TXID of the original transaction. static uint8_t orig_hash[32]; // TXID of the original transaction.
@ -216,6 +218,7 @@ Phase1 - process inputs
- check previous transactions - check previous transactions
========================================================= =========================================================
Stage 1: Get inputs and optionally get original inputs.
foreach I (idx1): foreach I (idx1):
Request I STAGE_REQUEST_1_INPUT Request I STAGE_REQUEST_1_INPUT
Add I to segwit sub-hashes Add I to segwit sub-hashes
@ -225,9 +228,11 @@ foreach I (idx1):
Request input I2 orig_hash, orig_index STAGE_REQUEST_1_ORIG_INPUT Request input I2 orig_hash, orig_index STAGE_REQUEST_1_ORIG_INPUT
Check I matches I2 Check I matches I2
Add I2 to original segwit sub-hashes Add I2 to original segwit sub-hashes
Add I2 to orig_info.hash_inputs_check
if (Decred) if (Decred)
Return I Return I
Stage 2: Get outputs and optionally get original outputs.
foreach O (idx1): foreach O (idx1):
Request O STAGE_REQUEST_2_OUTPUT Request O STAGE_REQUEST_2_OUTPUT
Add O to Decred decred_hash_prefix Add O to Decred decred_hash_prefix
@ -245,8 +250,10 @@ foreach O (idx1):
Check tx fee Check tx fee
Ask for confirmation Ask for confirmation
Stage 3: Check transaction.
if (taproot_only) if (taproot_only)
Skip to Phase 2. Skip checking of previous transactions.
foreach I (idx1): foreach I (idx1):
Request I STAGE_REQUEST_3_INPUT Request I STAGE_REQUEST_3_INPUT
@ -259,6 +266,34 @@ foreach I (idx1):
Request prevhash extra data (if applicable) STAGE_REQUEST_3_PREV_EXTRADATA Request prevhash extra data (if applicable) STAGE_REQUEST_3_PREV_EXTRADATA
Calculate hash of streamed tx, compare to prevhash I Calculate hash of streamed tx, compare to prevhash I
if (is_replacement)
foreach orig I (idx1):
if (orig idx1 is not legacy)
Request input I, orig_hash, idx1 STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT
Add I to OuterTransactionChecksum
Verify signature of I if I is internal
else
foreach orig I (idx2):
Request input I, orig_hash, idx2 STAGE_REQUEST_3_ORIG_INPUT
Add I to InnerTransactionChecksum
Add I to LegacyTransactionDigest
if idx1 == idx2
Add I to OuterTransactionChecksum
Save signature for verification
Ensure InnerTransactionChecksum matches orig_info.hash_inputs_check
foreach orig O (idx2):
Request output O, orig_hash, idx2 STAGE_REQUEST_3_ORIG_OUTPUT
Add O to InnerTransactionChecksum
Add O to LegacyTransactionDigest
Ensure InnerTransactionChecksum matches orig_hash_outputs
Verify signature of LegacyTransactionDigest
Ensure OuterTransactionChecksum matches orig_info.hash_inputs_check
Phase2: sign inputs, check that nothing changed Phase2: sign inputs, check that nothing changed
=============================================== ===============================================
@ -340,6 +375,14 @@ static bool is_external_input(uint32_t i) {
return external_inputs[i / 32] & (1 << (i % 32)); return external_inputs[i / 32] & (1 << (i % 32));
} }
static bool has_external_input(void) {
uint32_t sum = 0;
for (size_t i = 0; i < sizeof(external_inputs) / sizeof(uint32_t); ++i) {
sum |= external_inputs[i];
}
return sum != 0;
}
void send_req_1_input(void) { void send_req_1_input(void) {
signing_stage = STAGE_REQUEST_1_INPUT; signing_stage = STAGE_REQUEST_1_INPUT;
resp.has_request_type = true; resp.has_request_type = true;
@ -356,9 +399,8 @@ void send_req_1_orig_meta(void) {
resp.request_type = RequestType_TXMETA; resp.request_type = RequestType_TXMETA;
resp.has_details = true; resp.has_details = true;
resp.details.has_tx_hash = true; resp.details.has_tx_hash = true;
resp.details.tx_hash.size = input.orig_hash.size; resp.details.tx_hash.size = sizeof(orig_hash);
memcpy(resp.details.tx_hash.bytes, input.orig_hash.bytes, memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size);
resp.details.tx_hash.size);
msg_write(MessageType_MessageType_TxRequest, &resp); msg_write(MessageType_MessageType_TxRequest, &resp);
} }
@ -370,9 +412,8 @@ void send_req_1_orig_input(void) {
resp.details.has_request_index = true; resp.details.has_request_index = true;
resp.details.request_index = idx2; resp.details.request_index = idx2;
resp.details.has_tx_hash = true; resp.details.has_tx_hash = true;
resp.details.tx_hash.size = input.orig_hash.size; resp.details.tx_hash.size = sizeof(orig_hash);
memcpy(resp.details.tx_hash.bytes, input.orig_hash.bytes, memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size);
resp.details.tx_hash.size);
msg_write(MessageType_MessageType_TxRequest, &resp); msg_write(MessageType_MessageType_TxRequest, &resp);
} }
@ -394,9 +435,8 @@ void send_req_2_orig_output(void) {
resp.details.has_request_index = true; resp.details.has_request_index = true;
resp.details.request_index = idx2; resp.details.request_index = idx2;
resp.details.has_tx_hash = true; resp.details.has_tx_hash = true;
resp.details.tx_hash.size = output.orig_hash.size; resp.details.tx_hash.size = sizeof(orig_hash);
memcpy(resp.details.tx_hash.bytes, output.orig_hash.bytes, memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size);
resp.details.tx_hash.size);
msg_write(MessageType_MessageType_TxRequest, &resp); msg_write(MessageType_MessageType_TxRequest, &resp);
} }
@ -485,6 +525,45 @@ void send_req_3_prev_extradata(uint32_t chunk_offset, uint32_t chunk_len) {
} }
#endif #endif
void send_req_3_orig_nonlegacy_input(void) {
signing_stage = STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT;
resp.has_request_type = true;
resp.request_type = RequestType_TXORIGINPUT;
resp.has_details = true;
resp.details.has_request_index = true;
resp.details.request_index = idx1;
resp.details.has_tx_hash = true;
resp.details.tx_hash.size = sizeof(orig_hash);
memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size);
msg_write(MessageType_MessageType_TxRequest, &resp);
}
void send_req_3_orig_input(void) {
signing_stage = STAGE_REQUEST_3_ORIG_INPUT;
resp.has_request_type = true;
resp.request_type = RequestType_TXORIGINPUT;
resp.has_details = true;
resp.details.has_request_index = true;
resp.details.request_index = idx2;
resp.details.has_tx_hash = true;
resp.details.tx_hash.size = sizeof(orig_hash);
memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size);
msg_write(MessageType_MessageType_TxRequest, &resp);
}
void send_req_3_orig_output(void) {
signing_stage = STAGE_REQUEST_3_ORIG_OUTPUT;
resp.has_request_type = true;
resp.request_type = RequestType_TXORIGOUTPUT;
resp.has_details = true;
resp.details.has_request_index = true;
resp.details.request_index = idx2;
resp.details.has_tx_hash = true;
resp.details.tx_hash.size = sizeof(orig_hash);
memcpy(resp.details.tx_hash.bytes, orig_hash, resp.details.tx_hash.size);
msg_write(MessageType_MessageType_TxRequest, &resp);
}
void send_req_4_input(void) { void send_req_4_input(void) {
signing_stage = STAGE_REQUEST_4_INPUT; signing_stage = STAGE_REQUEST_4_INPUT;
resp.has_request_type = true; resp.has_request_type = true;
@ -505,7 +584,7 @@ void send_req_4_output(void) {
msg_write(MessageType_MessageType_TxRequest, &resp); msg_write(MessageType_MessageType_TxRequest, &resp);
} }
void send_req_segwit_input(void) { void send_req_nonlegacy_input(void) {
signing_stage = STAGE_REQUEST_NONLEGACY_INPUT; signing_stage = STAGE_REQUEST_NONLEGACY_INPUT;
resp.has_request_type = true; resp.has_request_type = true;
resp.request_type = RequestType_TXINPUT; resp.request_type = RequestType_TXINPUT;
@ -560,7 +639,6 @@ void phase1_request_next_input(void) {
idx1++; idx1++;
send_req_1_input(); send_req_1_input();
} else { } else {
hasher_Final(&hasher_check, hash_inputs_check);
idx1 = 0; idx1 = 0;
if (is_replacement) { if (is_replacement) {
@ -628,11 +706,41 @@ void phase1_request_orig_input(void) {
} }
void phase2_request_next_input(void) { void phase2_request_next_input(void) {
if (idx1 == next_legacy_input) { if (idx1 == info.next_legacy_input) {
idx2 = 0; idx2 = 0;
send_req_4_input(); send_req_4_input();
} else { } else {
send_req_segwit_input(); send_req_nonlegacy_input();
}
}
void phase2_request_orig_input(void) {
if (idx1 < orig_info.inputs_count) {
if (idx1 == 0) {
// Reset outer transaction check.
hasher_Reset(&hasher_check);
}
if (idx1 == orig_info.next_legacy_input) {
idx2 = 0;
send_req_3_orig_input();
} else {
send_req_3_orig_nonlegacy_input();
}
} else {
// Ensure that the original transaction inputs haven't changed for the outer
// transaction check.
uint8_t hash[32];
hasher_Final(&hasher_check, hash);
if (memcmp(hash, orig_info.hash_inputs_check, 32) != 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Transaction has changed during signing"));
signing_abort();
return;
}
idx1 = 0;
phase2_request_next_input();
} }
} }
@ -747,32 +855,25 @@ 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)) { config_getSafetyCheckLevel() == SafetyCheckLevel_Strict) {
if (is_replacement) {
fsm_sendFailure(
FailureType_Failure_ProcessError,
_("Non-standard paths not allowed in replacement transactions."));
layoutHome();
return false;
}
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path")); fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
signing_abort(); signing_abort();
return false; return false;
} }
layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL, // Sanity check not critical for security. The main reason for this is that we
_("Wrong address path"), _("for selected coin."), NULL, // are not comfortable with using the same private key in multiple signature
_("Continue at your"), _("own risk!"), NULL); // schemes (ECDSA and Schnorr) and we want to be sure that the user went
if (!protectButton(ButtonRequestType_ButtonRequest_UnknownDerivationPath, // through a warning screen before we sign the input.
false)) { if (!foreign_address_confirmed &&
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); !coin_path_check(coin, tinput->script_type, tinput->address_n_count,
tinput->address_n, tinput->has_multisig, true)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Transaction has changed during signing"));
signing_abort(); signing_abort();
return false; return false;
} }
}
memcpy(&node, &root, sizeof(HDNode)); memcpy(&node, &root, sizeof(HDNode));
if (hdnode_private_ckd_cached(&node, tinput->address_n, if (hdnode_private_ckd_cached(&node, tinput->address_n,
@ -830,6 +931,7 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count,
tx_info->inputs_count = inputs_count; tx_info->inputs_count = inputs_count;
tx_info->outputs_count = outputs_count; tx_info->outputs_count = outputs_count;
tx_info->next_legacy_input = 0xffffffff;
tx_info->min_sequence = SEQUENCE_FINAL; tx_info->min_sequence = SEQUENCE_FINAL;
tx_info->multisig_fp_set = false; tx_info->multisig_fp_set = false;
tx_info->multisig_fp_mismatch = false; tx_info->multisig_fp_mismatch = false;
@ -890,6 +992,8 @@ static bool tx_info_init(TxInfo *tx_info, uint32_t inputs_count,
} }
#endif #endif
hasher_Init(&tx_info->hasher_check, HASHER_SHA2);
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
if (coin->overwintered) { if (coin->overwintered) {
if (tx_info->version == 5) { if (tx_info->version == 5) {
@ -959,6 +1063,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin,
tx_weight = 4 * size; tx_weight = 4 * size;
foreign_address_confirmed = false;
taproot_only = true; taproot_only = true;
signatures = 0; signatures = 0;
idx1 = 0; idx1 = 0;
@ -974,16 +1079,12 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin,
memzero(&output, sizeof(TxOutputType)); memzero(&output, sizeof(TxOutputType));
memzero(&resp, sizeof(TxRequest)); memzero(&resp, sizeof(TxRequest));
is_replacement = false; is_replacement = false;
have_orig_verif_input = false;
orig_verif_input_idx = 0xffffffff;
signing = true; signing = true;
progress = 0; progress = 0;
// we step by 500/inputs_count per input in phase1 and phase2 // we step by 500/inputs_count per input in phase1 and phase2
// this means 50 % per phase. // this means 50 % per phase.
progress_step = (500 << PROGRESS_PRECISION) / info.inputs_count; progress_step = (500 << PROGRESS_PRECISION) / info.inputs_count;
next_legacy_input = 0xffffffff;
uint32_t branch_id = 0; uint32_t branch_id = 0;
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
branch_id = info.branch_id; branch_id = info.branch_id;
@ -1345,6 +1446,7 @@ static void txinfo_fill_zip244_header_hash(TxInfo *tx_info) {
#endif #endif
static void tx_info_finish(TxInfo *tx_info) { static void tx_info_finish(TxInfo *tx_info) {
hasher_Final(&tx_info->hasher_check, tx_info->hash_inputs_check);
hasher_Final(&tx_info->hasher_prevouts, tx_info->hash_prevouts); hasher_Final(&tx_info->hasher_prevouts, tx_info->hash_prevouts);
hasher_Final(&tx_info->hasher_amounts, tx_info->hash_amounts); hasher_Final(&tx_info->hasher_amounts, tx_info->hash_amounts);
hasher_Final(&tx_info->hasher_scriptpubkeys, tx_info->hash_scriptpubkeys); hasher_Final(&tx_info->hasher_scriptpubkeys, tx_info->hash_scriptpubkeys);
@ -1374,9 +1476,58 @@ static void tx_info_finish(TxInfo *tx_info) {
#endif #endif
} }
static bool tx_info_check_inputs_hash(TxInfo *tx_info) {
uint8_t hash[32];
hasher_Final(&tx_info->hasher_check, hash);
if (memcmp(hash, tx_info->hash_inputs_check, 32) != 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Transaction has changed during signing"));
signing_abort();
return false;
}
return true;
}
static bool tx_info_check_outputs_hash(TxInfo *tx_info) {
uint8_t hash[32] = {0};
hasher_Final(&tx_info->hasher_check, hash);
if (memcmp(hash, tx_info->hash_outputs, 32) != 0) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Transaction has changed during signing"));
signing_abort();
return false;
}
return true;
}
static bool signing_add_input(TxInputType *txinput) { 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); if (!tx_input_check_hash(&info.hasher_check, txinput)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to hash input"));
signing_abort();
return false;
}
if (txinput->script_type != InputScriptType_EXTERNAL &&
!coin_path_check(coin, txinput->script_type, txinput->address_n_count,
txinput->address_n, txinput->has_multisig, true)) {
if (config_getSafetyCheckLevel() == SafetyCheckLevel_Strict &&
!coin_path_check(coin, txinput->script_type, txinput->address_n_count,
txinput->address_n, txinput->has_multisig, false)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Forbidden key path"));
signing_abort();
return false;
}
if (!foreign_address_confirmed) {
if (!fsm_layoutPathWarning()) {
signing_abort();
return false;
}
foreign_address_confirmed = true;
}
}
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,
@ -1420,11 +1571,7 @@ static bool signing_check_prevtx_hash(void) {
idx1++; idx1++;
send_req_3_input(); send_req_3_input();
} else { } else {
hasher_Final(&hasher_check, hash); if (!tx_info_check_inputs_hash(&info)) {
if (memcmp(hash, hash_inputs_check, 32) != 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Transaction has changed during signing"));
signing_abort();
return false; return false;
} }
@ -1440,9 +1587,15 @@ static bool signing_check_prevtx_hash(void) {
} else } else
#endif #endif
{ {
if (is_replacement) {
// Verify original transaction.
phase2_request_orig_input();
} else {
// Proceed to transaction signing.
phase2_request_next_input(); phase2_request_next_input();
} }
} }
}
return true; return true;
} }
@ -1588,6 +1741,14 @@ static bool save_signature(TxInputType *txinput) {
} }
static bool signing_add_orig_input(TxInputType *orig_input) { static bool signing_add_orig_input(TxInputType *orig_input) {
// hash all input data to check it later
if (!tx_input_check_hash(&orig_info.hasher_check, orig_input)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to hash input"));
signing_abort();
return false;
}
if (!fill_input_script_pubkey(coin, &root, orig_input)) { if (!fill_input_script_pubkey(coin, &root, orig_input)) {
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive scriptPubKey")); _("Failed to derive scriptPubKey"));
@ -1626,7 +1787,7 @@ static bool signing_add_orig_input(TxInputType *orig_input) {
return false; return false;
} }
// Add input to original TXID computation before script_sig is overwritten. // Add input to original TXID computation.
if (!tx_serialize_input_hash(&tp, orig_input)) { if (!tx_serialize_input_hash(&tp, orig_input)) {
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize input")); _("Failed to serialize input"));
@ -1634,39 +1795,13 @@ static bool signing_add_orig_input(TxInputType *orig_input) {
return false; return false;
} }
// The first original input that has address_n set and a signature gets chosen // Remember the first original internal legacy input.
// as the verification input. Set script_sig for legacy digest computation. if ((orig_input->script_type == InputScriptType_SPENDMULTISIG ||
if (!have_orig_verif_input && orig_input->address_n_count != 0 && orig_input->script_type == InputScriptType_SPENDADDRESS) &&
!orig_input->has_multisig && !coin->force_bip143 && !coin->overwintered) {
((orig_input->has_script_sig && orig_input->script_sig.size != 0) || if (orig_info.next_legacy_input == 0xffffffff) {
(orig_input->has_witness && orig_input->witness.size > 1))) { orig_info.next_legacy_input = idx2;
// Save the signature before script_sig is overwritten.
if (!save_signature(orig_input)) {
return false;
} }
// Derive node.public_key and fill script_sig with the legacy scriptPubKey
// (aka BIP-143 script code), which is what our code expects here in order
// to properly compute the legacy transaction digest or BIP-143 transaction
// digest.
if (!derive_node(orig_input) || !fill_input_script_sig(orig_input)) {
return false;
}
// Save the verification input with script_sig set to scriptPubKey.
memcpy(&orig_verif_input, orig_input, sizeof(TxInputType));
orig_verif_input_idx = idx2;
have_orig_verif_input = true;
} else {
orig_input->script_sig.size = 0;
}
// Add input to original legacy digest computation now that script_sig is set.
if (!tx_serialize_input_hash(&ti, orig_input)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize input"));
signing_abort();
return false;
} }
return true; return true;
@ -1688,10 +1823,8 @@ static bool signing_add_orig_output(TxOutputType *orig_output) {
return false; return false;
} }
// Add output to the original legacy digest computation (ti) and to the // Add output to original TXID computation.
// original TXID computation (tp). if (!tx_serialize_output_hash(&tp, &orig_bin_output)) {
if (!tx_serialize_output_hash(&ti, &orig_bin_output) ||
!tx_serialize_output_hash(&tp, &orig_bin_output)) {
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize output")); _("Failed to serialize output"));
signing_abort(); signing_abort();
@ -1778,6 +1911,15 @@ static bool signing_add_orig_output(TxOutputType *orig_output) {
} }
static bool signing_confirm_tx(void) { static bool signing_confirm_tx(void) {
if (has_external_input()) {
layoutConfirmUnverifiedExternalInputs();
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
signing_abort();
return false;
}
}
if (coin->negative_fee) { if (coin->negative_fee) {
// bypass check for negative fee coins, required for reward TX // bypass check for negative fee coins, required for reward TX
} else { } else {
@ -2089,39 +2231,30 @@ static void signing_hash_zip244(const TxInfo *tx_info,
} }
#endif #endif
static bool signing_check_orig_tx(void) { static bool signing_verify_orig_nonlegacy_input(TxInputType *orig_input) {
uint8_t hash[32] = {0}; // Nothing to verify for external inputs.
if (orig_input->script_type == InputScriptType_EXTERNAL) {
return true;
}
// Finalize original TXID computation and ensure it matches orig_hash. // Save the signature before script_sig is overwritten.
tx_hash_final(&tp, hash, true); if (!save_signature(orig_input)) {
if (memcmp(hash, orig_hash, sizeof(orig_hash)) != 0) {
// This may happen if incorrect information is supplied in the TXORIGINPUT
// or TXORIGOUTPUT responses or if the device is loaded with the wrong seed,
// because we derive the scriptPubKeys of change-outputs from the seed using
// the provided path.
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Invalid original TXID."));
signing_abort();
return false; return false;
} }
if (!have_orig_verif_input) { // Derive node.public_key and fill script_sig with the legacy scriptPubKey
fsm_sendFailure(FailureType_Failure_ProcessError, // (aka BIP-143 script code), which is what our code expects here in order
_("The original transaction must specify address_n for at " // to properly compute the BIP-143 transaction digest.
"least one input.")); if (!derive_node(orig_input) || !fill_input_script_sig(orig_input)) {
signing_abort();
return false; return false;
} }
// Finish computation of BIP-143/BIP-341/ZIP-243 sub-hashes.
tx_info_finish(&orig_info);
// Compute the signed digest and verify signature. // Compute the signed digest and verify signature.
uint32_t hash_type = signing_hash_type(&orig_verif_input); uint8_t hash[32] = {0};
uint32_t hash_type = signing_hash_type(orig_input);
bool valid = false; bool valid = false;
if (orig_verif_input.script_type == InputScriptType_SPENDTAPROOT) { if (orig_input->script_type == InputScriptType_SPENDTAPROOT) {
signing_hash_bip341(&orig_info, orig_verif_input_idx, hash_type & 0xff, signing_hash_bip341(&orig_info, idx1, hash_type & 0xff, hash);
hash);
uint8_t output_public_key[32] = {0}; uint8_t output_public_key[32] = {0};
valid = (zkp_bip340_tweak_public_key(node.public_key + 1, NULL, valid = (zkp_bip340_tweak_public_key(node.public_key + 1, NULL,
output_public_key) == 0) && output_public_key) == 0) &&
@ -2129,18 +2262,11 @@ static bool signing_check_orig_tx(void) {
} else { } else {
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
if (coin->overwintered) { if (coin->overwintered) {
signing_hash_zip243(&orig_info, &orig_verif_input, hash); signing_hash_zip243(&orig_info, orig_input, hash);
} else } else
#endif #endif
{ {
if (is_segwit_input_script_type(&orig_verif_input) || signing_hash_bip143(&orig_info, orig_input, hash);
coin->force_bip143) {
signing_hash_bip143(&orig_info, &orig_verif_input, hash);
} else {
// Finalize legacy digest computation.
hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4);
tx_hash_final(&ti, hash, false);
}
} }
#ifdef USE_SECP256K1_ZKP_ECDSA #ifdef USE_SECP256K1_ZKP_ECDSA
@ -2163,6 +2289,142 @@ static bool signing_check_orig_tx(void) {
return valid; return valid;
} }
static bool signing_verify_orig_legacy_input(void) {
// Finalize legacy digest computation.
uint32_t hash_type = signing_hash_type(&input);
hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4);
// Compute the signed digest and verify signature.
uint8_t hash[32] = {0};
tx_hash_final(&ti, hash, false);
bool valid = false;
#ifdef USE_SECP256K1_ZKP_ECDSA
if (coin->curve->params == &secp256k1) {
valid = zkp_ecdsa_verify_digest(coin->curve->params, node.public_key, sig,
hash) == 0;
} else
#endif
{
valid = ecdsa_verify_digest(coin->curve->params, node.public_key, sig,
hash) == 0;
}
if (!valid) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature."));
signing_abort();
}
return valid;
}
static bool signing_hash_orig_input(TxInputType *orig_input) {
if (idx2 == 0) {
uint32_t branch_id = 0;
#if !BITCOIN_ONLY
branch_id = orig_info.branch_id;
#endif
tx_init(&ti, orig_info.inputs_count, orig_info.outputs_count,
orig_info.version, orig_info.lock_time, orig_info.expiry, branch_id,
0, coin->curve->hasher_sign, coin->overwintered,
orig_info.version_group_id, orig_info.timestamp);
// Reset the inner transaction check.
hasher_Reset(&orig_info.hasher_check);
}
// Add input to the inner transaction check.
if (!tx_input_check_hash(&orig_info.hasher_check, orig_input)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to hash input"));
signing_abort();
return false;
}
if (idx2 == idx1) {
// Add input to the outer transaction check.
if (!tx_input_check_hash(&hasher_check, orig_input)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to hash input"));
signing_abort();
return false;
}
// Save the signature before script_sig is overwritten.
if (!save_signature(orig_input)) {
return false;
}
// Derive node.public_key and fill script_sig with the legacy
// scriptPubKey which is what our code expects here in order to properly
// compute the transaction digest.
if (!derive_node(orig_input) || !fill_input_script_sig(orig_input)) {
return false;
}
memcpy(&input, orig_input, sizeof(input));
} else {
if (orig_info.next_legacy_input == idx1 && idx2 > idx1 &&
(orig_input->script_type == InputScriptType_SPENDADDRESS ||
orig_input->script_type == InputScriptType_SPENDMULTISIG)) {
orig_info.next_legacy_input = idx2;
}
orig_input->script_sig.size = 0;
}
// Add input to original legacy digest computation now that script_sig is
// set.
if (!tx_serialize_input_hash(&ti, orig_input)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize input"));
signing_abort();
return false;
}
return true;
}
static bool signing_hash_orig_output(TxOutputType *orig_output) {
if (compile_output(coin, amount_unit, &root, orig_output, &bin_output,
false) <= 0) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to compile output"));
signing_abort();
return false;
}
// Add the output to the inner transaction check.
tx_output_hash(&orig_info.hasher_check, &bin_output, coin->decred);
// Add the output to original legacy digest computation
if (!tx_serialize_output_hash(&ti, &bin_output)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize output"));
signing_abort();
return false;
}
return true;
}
static bool signing_check_orig_tx(void) {
uint8_t hash[32] = {0};
// Finalize original TXID computation and ensure it matches orig_hash.
tx_hash_final(&tp, hash, true);
if (memcmp(hash, orig_hash, sizeof(orig_hash)) != 0) {
// This may happen if incorrect information is supplied in the TXORIGINPUT
// or TXORIGOUTPUT responses or if the device is loaded with the wrong seed,
// because we derive the scriptPubKeys of change-outputs from the seed using
// the provided path.
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Invalid original TXID."));
signing_abort();
return false;
}
return true;
}
static void phase1_finish(void) { static void phase1_finish(void) {
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
if (coin->decred) { if (coin->decred) {
@ -2171,8 +2433,9 @@ static void phase1_finish(void) {
} }
#endif #endif
// Compute BIP143 hashPrevouts, hashSequence and hashOutputs. // Finish computation of BIP-143/BIP-341/ZIP-243 sub-hashes.
tx_info_finish(&info); tx_info_finish(&info);
tx_info_finish(&orig_info);
if (is_replacement) { if (is_replacement) {
if (!signing_check_orig_tx()) { if (!signing_check_orig_tx()) {
@ -2185,11 +2448,17 @@ static void phase1_finish(void) {
} }
if (taproot_only) { if (taproot_only) {
// All internal inputs are Taproot. We do not need to verify them so we // All internal inputs are Taproot. We do not need to verify that their
// proceed directly to Phase 2, where the transaction will be signed. We can // parameters match previous transactions. We can trust the amounts and
// trust the amounts and scriptPubKeys, because if an invalid value is // scriptPubKeys, because if an invalid value is provided then all issued
// provided then all issued signatures will be invalid. // signatures will be invalid.
if (is_replacement) {
// Verify original transaction.
phase2_request_orig_input();
} else {
// Proceed directly to transaction signing.
phase2_request_next_input(); phase2_request_next_input();
}
#if !BITCOIN_ONLY #if !BITCOIN_ONLY
} else if (coin->overwintered && info.version == 5) { } else if (coin->overwintered && info.version == 5) {
// ZIP-244 transactions are treated same as Taproot. // ZIP-244 transactions are treated same as Taproot.
@ -2354,17 +2623,12 @@ static bool signing_sign_bip340(const uint8_t *private_key,
} }
static bool signing_sign_legacy_input(void) { static bool signing_sign_legacy_input(void) {
uint8_t hash[32] = {0}; // Finalize legacy digest computation.
hasher_Final(&hasher_check, hash);
if (memcmp(hash, info.hash_outputs, 32) != 0 || taproot_only) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Transaction has changed during signing"));
signing_abort();
return false;
}
uint32_t hash_type = signing_hash_type(&input); uint32_t hash_type = signing_hash_type(&input);
hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4); hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4);
// Compute the digest and generate signature.
uint8_t hash[32] = {0};
tx_hash_final(&ti, hash, false); tx_hash_final(&ti, hash, false);
resp.has_serialized = true; resp.has_serialized = true;
if (!signing_sign_ecdsa(&input, privkey, pubkey, hash)) return false; if (!signing_sign_ecdsa(&input, privkey, pubkey, hash)) return false;
@ -2570,7 +2834,9 @@ void signing_txack(TransactionType *tx) {
if (!coin->force_bip143 && !coin->overwintered) { if (!coin->force_bip143 && !coin->overwintered) {
// remember the first non-segwit input -- this is the first input // remember the first non-segwit input -- this is the first input
// we need to sign during phase2 // we need to sign during phase2
if (next_legacy_input == 0xffffffff) next_legacy_input = idx1; if (info.next_legacy_input == 0xffffffff) {
info.next_legacy_input = idx1;
}
} }
} else if (is_segwit_input_script_type(&tx->inputs[0])) { } else if (is_segwit_input_script_type(&tx->inputs[0])) {
if (!to.is_segwit) { if (!to.is_segwit) {
@ -2645,11 +2911,6 @@ void signing_txack(TransactionType *tx) {
return; return;
} }
// Initialize computation of original legacy digest.
tx_init(&ti, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time,
tx->expiry, tx->branch_id, 0, coin->curve->hasher_sign,
coin->overwintered, tx->version_group_id, tx->timestamp);
// Initialize computation of original TXID. // Initialize computation of original TXID.
tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time,
tx->expiry, tx->branch_id, tx->extra_data_len, tx->expiry, tx->branch_id, tx->extra_data_len,
@ -2722,7 +2983,18 @@ void signing_txack(TransactionType *tx) {
return; return;
#endif #endif
case STAGE_REQUEST_3_INPUT: case STAGE_REQUEST_3_INPUT:
if (!signing_validate_input(&tx->inputs[0])) { if (idx1 == 0) {
hasher_Reset(&info.hasher_check);
}
if (!signing_validate_input(tx->inputs)) {
return;
}
if (!tx_input_check_hash(&info.hasher_check, tx->inputs)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to hash input"));
signing_abort();
return; return;
} }
@ -2733,11 +3005,6 @@ void signing_txack(TransactionType *tx) {
return; return;
} }
if (idx1 == 0) {
hasher_Reset(&hasher_check);
}
tx_input_check_hash(&hasher_check, tx->inputs);
memcpy(&input, tx->inputs, sizeof(TxInputType)); memcpy(&input, tx->inputs, sizeof(TxInputType));
if (!fill_input_script_pubkey(coin, &root, &input)) { if (!fill_input_script_pubkey(coin, &root, &input)) {
@ -2928,6 +3195,79 @@ void signing_txack(TransactionType *tx) {
} }
return; return;
#endif #endif
case STAGE_REQUEST_3_ORIG_NONLEGACY_INPUT:
if (!signing_validate_input(tx->inputs)) {
return;
}
// Add input to the outer transaction check.
if (!tx_input_check_hash(&hasher_check, tx->inputs)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to hash input"));
signing_abort();
return;
}
if (!signing_verify_orig_nonlegacy_input(tx->inputs)) {
return;
}
idx1++;
phase2_request_orig_input();
return;
case STAGE_REQUEST_3_ORIG_INPUT:
if (!signing_validate_input(tx->inputs) ||
!signing_hash_orig_input(tx->inputs)) {
return;
}
idx2++;
if (idx2 < orig_info.inputs_count) {
send_req_3_orig_input();
} else {
// Ensure that the original transaction inputs haven't changed for the
// inner transaction check.
if (!tx_info_check_inputs_hash(&orig_info)) {
return;
}
// Reset the inner transaction check.
hasher_Reset(&orig_info.hasher_check);
idx2 = 0;
send_req_3_orig_output();
}
return;
case STAGE_REQUEST_3_ORIG_OUTPUT:
if (!signing_validate_output(tx->outputs) ||
!signing_hash_orig_output(tx->outputs)) {
return;
}
idx2++;
if (idx2 < orig_info.outputs_count) {
send_req_3_orig_output();
} else {
// Ensure that the original transaction outputs haven't changed for the
// inner transaction check.
if (!tx_info_check_outputs_hash(&orig_info)) {
return;
}
// Verify original signature.
if (!signing_verify_orig_legacy_input()) {
return;
}
idx1++;
phase2_request_orig_input();
}
return;
case STAGE_REQUEST_4_INPUT: case STAGE_REQUEST_4_INPUT:
if (!signing_validate_input(&tx->inputs[0])) { if (!signing_validate_input(&tx->inputs[0])) {
return; return;
@ -2940,10 +3280,15 @@ void signing_txack(TransactionType *tx) {
info.lock_time, info.expiry, tx->branch_id, 0, info.lock_time, info.expiry, tx->branch_id, 0,
coin->curve->hasher_sign, coin->overwintered, coin->curve->hasher_sign, coin->overwintered,
info.version_group_id, info.timestamp); info.version_group_id, info.timestamp);
hasher_Reset(&hasher_check); hasher_Reset(&info.hasher_check);
} }
// check inputs are the same as those in phase 1 // check inputs are the same as those in phase 1
tx_input_check_hash(&hasher_check, tx->inputs); if (!tx_input_check_hash(&info.hasher_check, tx->inputs)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to hash input"));
signing_abort();
return;
}
if (idx2 == idx1) { if (idx2 == idx1) {
if (!tx_info_check_input(&info, &tx->inputs[0]) || if (!tx_info_check_input(&info, &tx->inputs[0]) ||
!derive_node(&tx->inputs[0]) || !derive_node(&tx->inputs[0]) ||
@ -2954,10 +3299,10 @@ void signing_txack(TransactionType *tx) {
memcpy(privkey, node.private_key, 32); memcpy(privkey, node.private_key, 32);
memcpy(pubkey, node.public_key, 33); memcpy(pubkey, node.public_key, 33);
} else { } else {
if (next_legacy_input == idx1 && idx2 > idx1 && if (info.next_legacy_input == idx1 && idx2 > idx1 &&
(tx->inputs[0].script_type == InputScriptType_SPENDADDRESS || (tx->inputs[0].script_type == InputScriptType_SPENDADDRESS ||
tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG)) { tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG)) {
next_legacy_input = idx2; info.next_legacy_input = idx2;
} }
tx->inputs[0].script_sig.size = 0; tx->inputs[0].script_sig.size = 0;
} }
@ -2971,15 +3316,11 @@ void signing_txack(TransactionType *tx) {
idx2++; idx2++;
send_req_4_input(); send_req_4_input();
} else { } else {
uint8_t hash[32] = {0}; if (!tx_info_check_inputs_hash(&info)) {
hasher_Final(&hasher_check, hash);
if (memcmp(hash, hash_inputs_check, 32) != 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Transaction has changed during signing"));
signing_abort();
return; return;
} }
hasher_Reset(&hasher_check);
hasher_Reset(&info.hasher_check);
idx2 = 0; idx2 = 0;
send_req_4_output(); send_req_4_output();
} }
@ -2999,7 +3340,7 @@ void signing_txack(TransactionType *tx) {
return; return;
} }
// check hashOutputs // check hashOutputs
tx_output_hash(&hasher_check, &bin_output, coin->decred); tx_output_hash(&info.hasher_check, &bin_output, coin->decred);
if (!tx_serialize_output_hash(&ti, &bin_output)) { if (!tx_serialize_output_hash(&ti, &bin_output)) {
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to serialize output")); _("Failed to serialize output"));
@ -3010,7 +3351,8 @@ void signing_txack(TransactionType *tx) {
idx2++; idx2++;
send_req_4_output(); send_req_4_output();
} else { } else {
if (!signing_sign_legacy_input()) { if (!tx_info_check_outputs_hash(&info) ||
!signing_sign_legacy_input()) {
return; return;
} }
// since this took a longer time, update progress // since this took a longer time, update progress

View File

@ -155,6 +155,22 @@ bool stellar_signingInit(const StellarSignTx *msg) {
return true; return true;
} }
void stellar_signingAbort(void) {
if (stellar_signing) {
stellar_signing = false;
layoutHome();
}
}
static void stellar_signingFail(const char *reason) {
if (!reason) {
reason = _("Unknown error");
}
fsm_sendFailure(FailureType_Failure_ProcessError, reason);
stellar_signingAbort();
}
bool stellar_confirmSourceAccount(bool has_source_account, bool stellar_confirmSourceAccount(bool has_source_account,
const char *str_account) { const char *str_account) {
stellar_hashupdate_bool(has_source_account); stellar_hashupdate_bool(has_source_account);
@ -174,7 +190,7 @@ bool stellar_confirmSourceAccount(bool has_source_account,
str_addr_rows[0], str_addr_rows[1], str_addr_rows[0], str_addr_rows[1],
str_addr_rows[2]); str_addr_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -189,7 +205,7 @@ bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -199,7 +215,7 @@ bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg) {
// Validate new account and convert to bytes // Validate new account and convert to bytes
uint8_t new_account_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t new_account_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->new_account, new_account_bytes)) { if (!stellar_getAddressBytes(msg->new_account, new_account_bytes)) {
stellar_signingAbort(_("Invalid new account address")); stellar_signingFail(_("Invalid new account address"));
return false; return false;
} }
@ -218,7 +234,7 @@ bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg) {
str_addr_rows[1], str_addr_rows[2], str_addr_rows[1], str_addr_rows[2],
str_amount_line); str_amount_line);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -236,7 +252,7 @@ bool stellar_confirmPaymentOp(const StellarPaymentOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -247,7 +263,7 @@ bool stellar_confirmPaymentOp(const StellarPaymentOp *msg) {
uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->destination_account, if (!stellar_getAddressBytes(msg->destination_account,
destination_account_bytes)) { destination_account_bytes)) {
stellar_signingAbort(_("Invalid destination account")); stellar_signingFail(_("Invalid destination account"));
return false; return false;
} }
@ -273,7 +289,7 @@ bool stellar_confirmPaymentOp(const StellarPaymentOp *msg) {
stellar_layoutTransactionDialog(str_pay_amount, str_asset_row, str_to, stellar_layoutTransactionDialog(str_pay_amount, str_asset_row, str_to,
str_addr_rows[1], str_addr_rows[2]); str_addr_rows[1], str_addr_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -295,7 +311,7 @@ bool stellar_confirmPathPaymentStrictReceiveOp(
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -306,7 +322,7 @@ bool stellar_confirmPathPaymentStrictReceiveOp(
uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->destination_account, if (!stellar_getAddressBytes(msg->destination_account,
destination_account_bytes)) { destination_account_bytes)) {
stellar_signingAbort(_("Invalid destination account")); stellar_signingFail(_("Invalid destination account"));
return false; return false;
} }
const char **str_dest_rows = const char **str_dest_rows =
@ -343,7 +359,7 @@ bool stellar_confirmPathPaymentStrictReceiveOp(
stellar_layoutTransactionDialog(str_pay_amount, str_dest_asset, str_to, stellar_layoutTransactionDialog(str_pay_amount, str_dest_asset, str_to,
str_dest_rows[1], str_dest_rows[2]); str_dest_rows[1], str_dest_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -360,7 +376,7 @@ bool stellar_confirmPathPaymentStrictReceiveOp(
_("This is the max"), _("This is the max"),
_("amount debited from your"), _("account.")); _("amount debited from your"), _("account."));
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
// Note: no confirmation for intermediate steps since they don't impact the // Note: no confirmation for intermediate steps since they don't impact the
@ -394,7 +410,7 @@ bool stellar_confirmPathPaymentStrictSendOp(
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -405,7 +421,7 @@ bool stellar_confirmPathPaymentStrictSendOp(
uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->destination_account, if (!stellar_getAddressBytes(msg->destination_account,
destination_account_bytes)) { destination_account_bytes)) {
stellar_signingAbort(_("Invalid destination account")); stellar_signingFail(_("Invalid destination account"));
return false; return false;
} }
const char **str_dest_rows = const char **str_dest_rows =
@ -441,7 +457,7 @@ bool stellar_confirmPathPaymentStrictSendOp(
stellar_layoutTransactionDialog(_("Path Pay at least"), str_pay_amount, stellar_layoutTransactionDialog(_("Path Pay at least"), str_pay_amount,
str_dest_asset, str_to, str_dest_rows[1]); str_dest_asset, str_to, str_dest_rows[1]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -458,7 +474,7 @@ bool stellar_confirmPathPaymentStrictSendOp(
str_dest_rows[2], str_source_amount, str_send_asset, str_dest_rows[2], str_source_amount, str_send_asset,
_("This is the amount debited"), _("from your account.")); _("This is the amount debited"), _("from your account."));
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
// Note: no confirmation for intermediate steps since they don't impact the // Note: no confirmation for intermediate steps since they don't impact the
@ -491,7 +507,7 @@ bool stellar_confirmManageBuyOfferOp(const StellarManageBuyOfferOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -551,7 +567,7 @@ bool stellar_confirmManageBuyOfferOp(const StellarManageBuyOfferOp *msg) {
stellar_layoutTransactionDialog(str_offer, str_buying, str_buying_asset, stellar_layoutTransactionDialog(str_offer, str_buying, str_buying_asset,
str_selling, str_selling_asset); str_selling, str_selling_asset);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -578,7 +594,7 @@ bool stellar_confirmManageSellOfferOp(const StellarManageSellOfferOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -637,7 +653,7 @@ bool stellar_confirmManageSellOfferOp(const StellarManageSellOfferOp *msg) {
stellar_layoutTransactionDialog(str_offer, str_selling, str_selling_asset, stellar_layoutTransactionDialog(str_offer, str_selling, str_selling_asset,
str_buying, str_buying_asset); str_buying, str_buying_asset);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -665,7 +681,7 @@ bool stellar_confirmCreatePassiveSellOfferOp(
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -715,7 +731,7 @@ bool stellar_confirmCreatePassiveSellOfferOp(
stellar_layoutTransactionDialog(str_offer, str_selling, str_selling_asset, stellar_layoutTransactionDialog(str_offer, str_selling, str_selling_asset,
str_buying, str_buying_asset); str_buying, str_buying_asset);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -740,7 +756,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -762,7 +778,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
uint8_t inflation_destination_account_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t inflation_destination_account_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->inflation_destination_account, if (!stellar_getAddressBytes(msg->inflation_destination_account,
inflation_destination_account_bytes)) { inflation_destination_account_bytes)) {
stellar_signingAbort(_("Invalid inflation destination account")); stellar_signingFail(_("Invalid inflation destination account"));
return false; return false;
} }
const char **str_addr_rows = const char **str_addr_rows =
@ -771,7 +787,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
stellar_layoutTransactionDialog(str_title, NULL, str_addr_rows[0], stellar_layoutTransactionDialog(str_title, NULL, str_addr_rows[0],
str_addr_rows[1], str_addr_rows[2]); str_addr_rows[1], str_addr_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -786,7 +802,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
// Auth required // Auth required
if (msg->clear_flags > 7) { if (msg->clear_flags > 7) {
stellar_signingAbort(_("Invalid flags")); stellar_signingFail(_("Invalid flags"));
return false; return false;
} }
if (msg->clear_flags & 0x01) { if (msg->clear_flags & 0x01) {
@ -807,7 +823,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2], stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2],
rows[3]); rows[3]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
memzero(rows, sizeof(rows)); memzero(rows, sizeof(rows));
@ -824,7 +840,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
// Auth required // Auth required
if (msg->set_flags > 7) { if (msg->set_flags > 7) {
stellar_signingAbort(_("Invalid flags")); stellar_signingFail(_("Invalid flags"));
return false; return false;
} }
if (msg->set_flags & 0x01) { if (msg->set_flags & 0x01) {
@ -845,7 +861,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2], stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2],
rows[3]); rows[3]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
memzero(rows, sizeof(rows)); memzero(rows, sizeof(rows));
@ -919,7 +935,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2], stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2],
rows[3]); rows[3]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
memzero(rows, sizeof(rows)); memzero(rows, sizeof(rows));
@ -942,7 +958,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
stellar_layoutTransactionDialog(str_title, rows[0], rows[1], NULL, NULL); stellar_layoutTransactionDialog(str_title, rows[0], rows[1], NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
memzero(rows, sizeof(rows)); memzero(rows, sizeof(rows));
@ -982,7 +998,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
str_addr_rows[2]); str_addr_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall,
false)) { false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
break; break;
@ -998,12 +1014,12 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
_("screen)")); _("screen)"));
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall,
false)) { false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
break; break;
default: default:
stellar_signingAbort(_("Stellar: invalid signer type")); stellar_signingFail(_("Stellar: invalid signer type"));
return false; return false;
} }
@ -1017,7 +1033,7 @@ bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) {
stellar_layoutTransactionDialog(_("Confirm Hash"), rows[0], rows[1], stellar_layoutTransactionDialog(_("Confirm Hash"), rows[0], rows[1],
rows[2], rows[3]); rows[2], rows[3]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
memzero(rows, sizeof(rows)); memzero(rows, sizeof(rows));
@ -1042,7 +1058,7 @@ bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -1073,7 +1089,7 @@ bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg) {
// Validate destination account and convert to bytes // Validate destination account and convert to bytes
uint8_t asset_issuer_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t asset_issuer_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->asset.issuer, asset_issuer_bytes)) { if (!stellar_getAddressBytes(msg->asset.issuer, asset_issuer_bytes)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_ProcessError,
_("Invalid asset issuer")); _("Invalid asset issuer"));
return false; return false;
@ -1085,7 +1101,7 @@ bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg) {
stellar_layoutTransactionDialog(str_title, str_amount_row, str_addr_rows[0], stellar_layoutTransactionDialog(str_title, str_amount_row, str_addr_rows[0],
str_addr_rows[1], str_addr_rows[2]); str_addr_rows[1], str_addr_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -1104,7 +1120,7 @@ bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -1126,7 +1142,7 @@ bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg) {
// Validate account and convert to bytes // Validate account and convert to bytes
uint8_t trusted_account_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t trusted_account_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->trusted_account, trusted_account_bytes)) { if (!stellar_getAddressBytes(msg->trusted_account, trusted_account_bytes)) {
stellar_signingAbort(_("Invalid trusted account")); stellar_signingFail(_("Invalid trusted account"));
return false; return false;
} }
@ -1141,7 +1157,7 @@ bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg) {
stellar_layoutTransactionDialog(str_title, str_asset_row, str_by, stellar_layoutTransactionDialog(str_title, str_asset_row, str_by,
str_trustor_rows[1], str_trustor_rows[2]); str_trustor_rows[1], str_trustor_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -1163,7 +1179,7 @@ bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg) {
stellar_hashupdate_bytes((uint8_t *)padded_code, 12); stellar_hashupdate_bytes((uint8_t *)padded_code, 12);
break; break;
default: default:
stellar_signingAbort(_("Stellar: invalid asset type")); stellar_signingFail(_("Stellar: invalid asset type"));
return false; return false;
} }
// is authorized // is authorized
@ -1179,7 +1195,7 @@ bool stellar_confirmAccountMergeOp(const StellarAccountMergeOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -1190,7 +1206,7 @@ bool stellar_confirmAccountMergeOp(const StellarAccountMergeOp *msg) {
uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t destination_account_bytes[STELLAR_KEY_SIZE] = {0};
if (!stellar_getAddressBytes(msg->destination_account, if (!stellar_getAddressBytes(msg->destination_account,
destination_account_bytes)) { destination_account_bytes)) {
stellar_signingAbort(_("Invalid destination account")); stellar_signingFail(_("Invalid destination account"));
return false; return false;
} }
@ -1202,7 +1218,7 @@ bool stellar_confirmAccountMergeOp(const StellarAccountMergeOp *msg) {
str_destination_rows[0], str_destination_rows[1], str_destination_rows[0], str_destination_rows[1],
str_destination_rows[2]); str_destination_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -1219,7 +1235,7 @@ bool stellar_confirmManageDataOp(const StellarManageDataOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -1240,7 +1256,7 @@ bool stellar_confirmManageDataOp(const StellarManageDataOp *msg) {
stellar_layoutTransactionDialog(str_title, str_key_lines[0], str_key_lines[1], stellar_layoutTransactionDialog(str_title, str_key_lines[0], str_key_lines[1],
str_key_lines[2], str_key_lines[3]); str_key_lines[2], str_key_lines[3]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -1258,7 +1274,7 @@ bool stellar_confirmManageDataOp(const StellarManageDataOp *msg) {
str_hash_lines[1], str_hash_lines[2], str_hash_lines[1], str_hash_lines[2],
str_hash_lines[3]); str_hash_lines[3]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
} }
@ -1282,7 +1298,7 @@ bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg) {
if (!stellar_confirmSourceAccount(msg->has_source_account, if (!stellar_confirmSourceAccount(msg->has_source_account,
msg->source_account)) { msg->source_account)) {
stellar_signingAbort(_("Source account error")); stellar_signingFail(_("Source account error"));
return false; return false;
} }
@ -1295,7 +1311,7 @@ bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg) {
stellar_layoutTransactionDialog(_("Bump Sequence"), _("Set sequence to:"), stellar_layoutTransactionDialog(_("Bump Sequence"), _("Set sequence to:"),
str_bump_to, NULL, NULL); str_bump_to, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return false; return false;
} }
@ -1307,16 +1323,6 @@ bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg) {
return true; return true;
} }
void stellar_signingAbort(const char *reason) {
if (!reason) {
reason = _("Unknown error");
}
stellar_signing = false;
fsm_sendFailure(FailureType_Failure_ProcessError, reason);
layoutHome();
}
/** /**
* Populates the fields of resp with the signature of the active transaction * Populates the fields of resp with the signature of the active transaction
*/ */
@ -1480,7 +1486,7 @@ void stellar_format_asset(const StellarAsset *asset, char *str_formatted,
// Validate issuer account for non-native assets // Validate issuer account for non-native assets
if (asset->type != StellarAssetType_NATIVE && if (asset->type != StellarAssetType_NATIVE &&
!stellar_validateAddress(asset->issuer)) { !stellar_validateAddress(asset->issuer)) {
stellar_signingAbort(_("Invalid asset issuer")); stellar_signingFail(_("Invalid asset issuer"));
return; return;
} }
@ -1736,7 +1742,7 @@ void stellar_hashupdate_asset(const StellarAsset *asset) {
uint8_t issuer_bytes[STELLAR_KEY_SIZE] = {0}; uint8_t issuer_bytes[STELLAR_KEY_SIZE] = {0};
if (asset->type != StellarAssetType_NATIVE && if (asset->type != StellarAssetType_NATIVE &&
!stellar_getAddressBytes(asset->issuer, issuer_bytes)) { !stellar_getAddressBytes(asset->issuer, issuer_bytes)) {
stellar_signingAbort(_("Invalid asset issuer")); stellar_signingFail(_("Invalid asset issuer"));
return; return;
} }
@ -1804,7 +1810,7 @@ void stellar_layoutTransactionSummary(const StellarSignTx *msg) {
str_addr_rows[0], str_addr_rows[1], str_addr_rows[0], str_addr_rows[1],
str_addr_rows[2]); str_addr_rows[2]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return; return;
} }
@ -1839,7 +1845,7 @@ void stellar_layoutTransactionSummary(const StellarSignTx *msg) {
strlcpy(str_lines[0], _("Memo (RETURN)"), sizeof(str_lines[0])); strlcpy(str_lines[0], _("Memo (RETURN)"), sizeof(str_lines[0]));
break; break;
default: default:
stellar_signingAbort(_("Stellar invalid memo type")); stellar_signingFail(_("Stellar invalid memo type"));
return; return;
} }
@ -1853,7 +1859,7 @@ void stellar_layoutTransactionSummary(const StellarSignTx *msg) {
stellar_layoutTransactionDialog(str_lines[0], str_lines[1], str_lines[2], stellar_layoutTransactionDialog(str_lines[0], str_lines[1], str_lines[2],
str_lines[3], str_lines[4]); str_lines[3], str_lines[4]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return; return;
} }
@ -1891,7 +1897,7 @@ void stellar_layoutTransactionSummary(const StellarSignTx *msg) {
stellar_layoutTransactionDialog(_("Confirm Time Bounds"), str_lines[0], stellar_layoutTransactionDialog(_("Confirm Time Bounds"), str_lines[0],
str_lines[1], str_lines[2], str_lines[3]); str_lines[1], str_lines[2], str_lines[3]);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
stellar_signingAbort(_("User canceled")); stellar_signingFail(_("User canceled"));
return; return;
} }
} }
@ -2013,3 +2019,16 @@ void stellar_layoutTransactionDialog(const char *line1, const char *line2,
line1, line2, line3, line4, line5, stellar_activeTx.address_n, line1, line2, line3, line4, line5, stellar_activeTx.address_n,
stellar_activeTx.address_n_count, str_warning, false); stellar_activeTx.address_n_count, str_warning, false);
} }
bool stellar_path_check(uint32_t address_n_count, const uint32_t *address_n) {
// 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'
bool valid = (address_n_count == 3);
valid = valid && (address_n[0] == (PATH_HARDENED | 44));
valid = valid && (address_n[1] == (PATH_HARDENED | 148) ||
address_n[1] == (PATH_HARDENED | 1));
valid = valid && (address_n[2] & PATH_HARDENED);
valid = valid && ((address_n[2] & PATH_UNHARDEN_MASK) <= PATH_MAX_ACCOUNT);
return valid;
}

View File

@ -54,7 +54,7 @@ typedef struct {
// Signing process // Signing process
bool stellar_signingInit(const StellarSignTx *tx); bool stellar_signingInit(const StellarSignTx *tx);
void stellar_signingAbort(const char *reason); void stellar_signingAbort(void);
bool stellar_confirmSourceAccount(bool has_source_account, bool stellar_confirmSourceAccount(bool has_source_account,
const char *str_account); const char *str_account);
bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg); bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg);
@ -117,4 +117,6 @@ bool stellar_validateAddress(const char *str_address);
bool stellar_getAddressBytes(const char *str_address, uint8_t *out_bytes); bool stellar_getAddressBytes(const char *str_address, uint8_t *out_bytes);
uint16_t stellar_crc16(uint8_t *bytes, uint32_t length); uint16_t stellar_crc16(uint8_t *bytes, uint32_t length);
bool stellar_path_check(uint32_t address_n_count, const uint32_t *address_n);
#endif #endif

View File

@ -556,22 +556,37 @@ uint32_t serialize_script_multisig(const CoinInfo *coin,
} }
// tx methods // tx methods
void tx_input_check_hash(Hasher *hasher, const TxInputType *input) { bool tx_input_check_hash(Hasher *hasher, const TxInputType *input) {
hasher_Update(hasher, input->prev_hash.bytes, sizeof(input->prev_hash.bytes));
hasher_Update(hasher, (const uint8_t *)&input->prev_index,
sizeof(input->prev_index));
hasher_Update(hasher, (const uint8_t *)&input->script_type,
sizeof(input->script_type));
hasher_Update(hasher, (const uint8_t *)&input->address_n_count, hasher_Update(hasher, (const uint8_t *)&input->address_n_count,
sizeof(input->address_n_count)); sizeof(input->address_n_count));
for (int i = 0; i < input->address_n_count; ++i) for (int i = 0; i < input->address_n_count; ++i)
hasher_Update(hasher, (const uint8_t *)&input->address_n[i], hasher_Update(hasher, (const uint8_t *)&input->address_n[i],
sizeof(input->address_n[0])); sizeof(input->address_n[0]));
hasher_Update(hasher, input->prev_hash.bytes, sizeof(input->prev_hash.bytes));
hasher_Update(hasher, (const uint8_t *)&input->prev_index,
sizeof(input->prev_index));
tx_script_hash(hasher, input->script_sig.size, input->script_sig.bytes);
hasher_Update(hasher, (const uint8_t *)&input->sequence, hasher_Update(hasher, (const uint8_t *)&input->sequence,
sizeof(input->sequence)); sizeof(input->sequence));
hasher_Update(hasher, (const uint8_t *)&input->script_type,
sizeof(input->script_type));
uint8_t multisig_fp[32] = {0};
if (input->has_multisig) {
if (cryptoMultisigFingerprint(&input->multisig, multisig_fp) == 0) {
// Invalid multisig parameters.
return false;
}
}
hasher_Update(hasher, multisig_fp, sizeof(multisig_fp));
hasher_Update(hasher, (const uint8_t *)&input->amount, sizeof(input->amount)); hasher_Update(hasher, (const uint8_t *)&input->amount, sizeof(input->amount));
tx_script_hash(hasher, input->witness.size, input->witness.bytes);
hasher_Update(hasher, (const uint8_t *)&input->has_orig_hash,
sizeof(input->has_orig_hash));
hasher_Update(hasher, input->orig_hash.bytes, sizeof(input->orig_hash.bytes));
hasher_Update(hasher, (const uint8_t *)&input->orig_index,
sizeof(input->orig_index));
tx_script_hash(hasher, input->script_pubkey.size, input->script_pubkey.bytes); tx_script_hash(hasher, input->script_pubkey.size, input->script_pubkey.bytes);
return; return true;
} }
uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input) { uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input) {

View File

@ -79,7 +79,7 @@ int compile_output(const CoinInfo *coin, AmountUnit amount_unit,
int fill_input_script_pubkey(const CoinInfo *coin, const HDNode *root, int fill_input_script_pubkey(const CoinInfo *coin, const HDNode *root,
TxInputType *in); TxInputType *in);
void tx_input_check_hash(Hasher *hasher, const TxInputType *input); bool tx_input_check_hash(Hasher *hasher, const TxInputType *input);
uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input); uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input);
uint32_t tx_amount_hash(Hasher *hasher, const TxInputType *input); uint32_t tx_amount_hash(Hasher *hasher, const TxInputType *input);
uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data); uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data);

View File

@ -23,6 +23,7 @@
#include "bip32.h" #include "bip32.h"
#include "buttons.h" #include "buttons.h"
#include "config.h" #include "config.h"
#include "crypto.h"
#include "curves.h" #include "curves.h"
#include "debug.h" #include "debug.h"
#include "gettext.h" #include "gettext.h"
@ -481,7 +482,7 @@ static const HDNode *generateKeyHandle(const uint8_t app_id[],
uint32_t key_path[KEY_PATH_ENTRIES] = {0}; uint32_t key_path[KEY_PATH_ENTRIES] = {0};
for (uint32_t i = 0; i < KEY_PATH_ENTRIES; i++) { for (uint32_t i = 0; i < KEY_PATH_ENTRIES; i++) {
// high bit for hardened keys // high bit for hardened keys
key_path[i] = 0x80000000 | random32(); key_path[i] = PATH_HARDENED | random32();
} }
// First half of keyhandle is key_path // First half of keyhandle is key_path
@ -508,7 +509,7 @@ static const HDNode *validateKeyHandle(const uint8_t app_id[],
memcpy(key_path, key_handle, KEY_PATH_LEN); memcpy(key_path, key_handle, KEY_PATH_LEN);
for (unsigned int i = 0; i < KEY_PATH_ENTRIES; i++) { for (unsigned int i = 0; i < KEY_PATH_ENTRIES; i++) {
// check high bit for hardened keys // check high bit for hardened keys
if (!(key_path[i] & 0x80000000)) { if (!(key_path[i] & PATH_HARDENED)) {
return NULL; return NULL;
} }
} }

View File

@ -3,5 +3,5 @@
#define VERSION_PATCH 2 #define VERSION_PATCH 2
#define FIX_VERSION_MAJOR 1 #define FIX_VERSION_MAJOR 1
#define FIX_VERSION_MINOR 10 #define FIX_VERSION_MINOR 11
#define FIX_VERSION_PATCH 0 #define FIX_VERSION_PATCH 0

View File

@ -25,7 +25,7 @@ if TYPE_CHECKING:
from ..client import TrezorClient from ..client import TrezorClient
from .. import messages from .. import messages
PATH_HELP = "BIP-32 path, e.g. m/44'/0'/0'/0/0" PATH_HELP = "BIP-32 path, e.g. m/10018'/0'"
@click.group(name="cosi") @click.group(name="cosi")

View File

@ -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"),

View File

@ -134,8 +134,9 @@ def test_p2pkh_fee_bump(client: Client):
request_input(0, TXHASH_beafc7), request_input(0, TXHASH_beafc7),
request_output(0, TXHASH_beafc7), request_output(0, TXHASH_beafc7),
(tt, request_orig_input(0, TXHASH_50f6f1)), (tt, request_orig_input(0, TXHASH_50f6f1)),
(tt, request_orig_output(0, TXHASH_50f6f1)), request_orig_input(0, TXHASH_50f6f1),
(tt, request_orig_output(1, TXHASH_50f6f1)), request_orig_output(0, TXHASH_50f6f1),
request_orig_output(1, TXHASH_50f6f1),
request_input(0), request_input(0),
request_output(0), request_output(0),
request_output(1), request_output(1),
@ -257,6 +258,8 @@ def test_p2tr_fee_bump(client: Client):
request_output(1), request_output(1),
request_orig_output(1, TXHASH_8e4af7), request_orig_output(1, TXHASH_8e4af7),
messages.ButtonRequest(code=B.SignTx), messages.ButtonRequest(code=B.SignTx),
request_orig_input(0, TXHASH_8e4af7),
request_orig_input(1, TXHASH_8e4af7),
request_input(0), request_input(0),
request_input(1), request_input(1),
request_output(0), request_output(0),
@ -328,6 +331,7 @@ def test_p2wpkh_finalize(client: Client):
request_output(0, TXHASH_43d273), request_output(0, TXHASH_43d273),
request_output(1, TXHASH_43d273), request_output(1, TXHASH_43d273),
request_output(2, TXHASH_43d273), request_output(2, TXHASH_43d273),
request_orig_input(0, TXHASH_70f987),
request_input(0), request_input(0),
request_output(0), request_output(0),
request_output(1), request_output(1),
@ -464,6 +468,7 @@ def test_p2wpkh_payjoin(
request_input(0, TXHASH_70f987), request_input(0, TXHASH_70f987),
request_output(0, TXHASH_70f987), request_output(0, TXHASH_70f987),
request_output(1, TXHASH_70f987), request_output(1, TXHASH_70f987),
request_orig_input(0, TXHASH_65b768),
request_input(0), request_input(0),
request_input(1), request_input(1),
request_output(0), request_output(0),
@ -538,6 +543,8 @@ def test_p2wpkh_in_p2sh_remove_change(client: Client):
request_meta(TXHASH_efaa41), request_meta(TXHASH_efaa41),
request_input(0, TXHASH_efaa41), request_input(0, TXHASH_efaa41),
request_output(0, TXHASH_efaa41), request_output(0, TXHASH_efaa41),
request_orig_input(0, TXHASH_334cd7),
request_orig_input(1, TXHASH_334cd7),
request_input(0), request_input(0),
request_input(1), request_input(1),
request_output(0), request_output(0),
@ -618,6 +625,8 @@ def test_p2wpkh_in_p2sh_fee_bump_from_external(client: Client):
request_meta(TXHASH_efaa41), request_meta(TXHASH_efaa41),
request_input(0, TXHASH_efaa41), request_input(0, TXHASH_efaa41),
request_output(0, TXHASH_efaa41), request_output(0, TXHASH_efaa41),
request_orig_input(0, TXHASH_334cd7),
request_orig_input(1, TXHASH_334cd7),
request_input(0), request_input(0),
request_input(1), request_input(1),
request_output(0), request_output(0),
@ -759,6 +768,10 @@ def test_tx_meld(client: Client):
request_input(1, TXHASH_927784), request_input(1, TXHASH_927784),
request_input(2, TXHASH_927784), request_input(2, TXHASH_927784),
request_output(0, TXHASH_927784), request_output(0, TXHASH_927784),
request_orig_input(0, TXHASH_334cd7),
request_orig_input(1, TXHASH_334cd7),
request_orig_input(0, TXHASH_ed89ac),
request_orig_input(1, TXHASH_ed89ac),
request_input(0), request_input(0),
request_input(1), request_input(1),
request_input(2), request_input(2),

View File

@ -18,6 +18,7 @@ import pytest
from trezorlib import device, messages from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
from ...common import MNEMONIC12 from ...common import MNEMONIC12
@ -84,7 +85,7 @@ def test_pin_passphrase(client: Client):
assert client.features.passphrase_protection is True assert client.features.passphrase_protection is True
# Do passphrase-protected action, PassphraseRequest should be raised # Do passphrase-protected action, PassphraseRequest should be raised
resp = client.call_raw(messages.GetAddress()) resp = client.call_raw(messages.GetAddress(address_n=parse_path("m/44'/0'/0'/0/0")))
assert isinstance(resp, messages.PassphraseRequest) assert isinstance(resp, messages.PassphraseRequest)
client.call_raw(messages.Cancel()) client.call_raw(messages.Cancel())
@ -135,7 +136,7 @@ def test_nopin_nopassphrase(client: Client):
assert client.features.passphrase_protection is False assert client.features.passphrase_protection is False
# Do pin & passphrase-protected action, PassphraseRequest should NOT be raised # Do pin & passphrase-protected action, PassphraseRequest should NOT be raised
resp = client.call_raw(messages.GetAddress()) resp = client.call_raw(messages.GetAddress(address_n=parse_path("m/44'/0'/0'/0/0")))
assert isinstance(resp, messages.Address) assert isinstance(resp, messages.Address)

View File

@ -19,6 +19,7 @@ from mnemonic import Mnemonic
from trezorlib import device, messages from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
from ...common import generate_entropy from ...common import generate_entropy
@ -87,7 +88,7 @@ def reset_device(client: Client, strength):
assert resp.passphrase_protection is False assert resp.passphrase_protection is False
# Do pin & passphrase-protected action, PassphraseRequest should NOT be raised # Do pin & passphrase-protected action, PassphraseRequest should NOT be raised
resp = client.call_raw(messages.GetAddress()) resp = client.call_raw(messages.GetAddress(address_n=parse_path("m/44'/0'/0'/0/0")))
assert isinstance(resp, messages.Address) assert isinstance(resp, messages.Address)
@ -186,7 +187,7 @@ def test_reset_device_256_pin(client: Client):
assert resp.passphrase_protection is True assert resp.passphrase_protection is True
# Do passphrase-protected action, PassphraseRequest should be raised # Do passphrase-protected action, PassphraseRequest should be raised
resp = client.call_raw(messages.GetAddress()) resp = client.call_raw(messages.GetAddress(address_n=parse_path("m/44'/0'/0'/0/0")))
assert isinstance(resp, messages.PassphraseRequest) assert isinstance(resp, messages.PassphraseRequest)
client.call_raw(messages.Cancel()) client.call_raw(messages.Cancel())

View File

@ -18,8 +18,9 @@ import time
import pytest import pytest
from trezorlib import btc from trezorlib import btc, device
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import SafetyCheckLevel
from trezorlib.tools import H_ from trezorlib.tools import H_
pytestmark = [ pytestmark = [
@ -29,6 +30,8 @@ pytestmark = [
def test_public_ckd(client: Client): def test_public_ckd(client: Client):
# disable safety checks to access non-standard paths
device.apply_settings(client, safety_checks=SafetyCheckLevel.PromptTemporarily)
btc.get_address(client, "Bitcoin", []) # to compute root node via BIP39 btc.get_address(client, "Bitcoin", []) # to compute root node via BIP39
for depth in range(8): for depth in range(8):
@ -41,6 +44,8 @@ def test_public_ckd(client: Client):
def test_private_ckd(client: Client): def test_private_ckd(client: Client):
# disable safety checks to access non-standard paths
device.apply_settings(client, safety_checks=SafetyCheckLevel.PromptTemporarily)
btc.get_address(client, "Bitcoin", []) # to compute root node via BIP39 btc.get_address(client, "Bitcoin", []) # to compute root node via BIP39
for depth in range(8): for depth in range(8):
@ -54,6 +59,9 @@ def test_private_ckd(client: Client):
def test_cache(client: Client): def test_cache(client: Client):
# disable safety checks to access non-standard paths
device.apply_settings(client, safety_checks=SafetyCheckLevel.PromptTemporarily)
start = time.time() start = time.time()
for x in range(10): for x in range(10):
btc.get_address(client, "Bitcoin", [x, 2, 3, 4, 5, 6, 7, 8]) btc.get_address(client, "Bitcoin", [x, 2, 3, 4, 5, 6, 7, 8])

View File

@ -18,6 +18,7 @@ import pytest
from trezorlib import debuglink, device, messages, misc from trezorlib import debuglink, device, messages, misc
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
from trezorlib.transport import udp from trezorlib.transport import udp
from ..common import MNEMONIC12 from ..common import MNEMONIC12
@ -40,7 +41,7 @@ def test_mnemonic(client: Client):
@pytest.mark.skip_t2 @pytest.mark.skip_t2
@pytest.mark.setup_client(mnemonic=MNEMONIC12, pin="1234", passphrase="") @pytest.mark.setup_client(mnemonic=MNEMONIC12, pin="1234", passphrase="")
def test_pin(client: Client): def test_pin(client: Client):
resp = client.call_raw(messages.GetAddress()) resp = client.call_raw(messages.GetAddress(address_n=parse_path("m/44'/0'/0'/0/0")))
assert isinstance(resp, messages.PinMatrixRequest) assert isinstance(resp, messages.PinMatrixRequest)
state = client.debug.state() state = client.debug.state()

View File

@ -19,6 +19,7 @@ import pytest
from trezorlib import device, exceptions, messages from trezorlib import device, exceptions, messages
from trezorlib.client import MAX_PIN_LENGTH from trezorlib.client import MAX_PIN_LENGTH
from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
PinType = messages.PinMatrixRequestType PinType = messages.PinMatrixRequestType
@ -177,7 +178,7 @@ def test_set_pin_to_wipe_code(client: Client):
# Check that there is no PIN protection. # Check that there is no PIN protection.
client.init_device() client.init_device()
assert client.features.pin_protection is False assert client.features.pin_protection is False
resp = client.call_raw(messages.GetAddress()) resp = client.call_raw(messages.GetAddress(address_n=parse_path("m/44'/0'/0'/0/0")))
assert isinstance(resp, messages.Address) assert isinstance(resp, messages.Address)

View File

@ -158,21 +158,7 @@ def test_ping(client: Client):
client.ping("msg", True) client.ping("msg", True)
@pytest.mark.skip_t2 def test_get_entropy(client: Client):
def test_get_entropy_t1(client: Client):
_assert_protection(client)
with client:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ProtectCall),
messages.Entropy,
]
)
misc.get_entropy(client, 10)
@pytest.mark.skip_t1
def test_get_entropy_t2(client: Client):
_assert_protection(client) _assert_protection(client)
with client: with client:
client.use_pin_sequence([PIN4]) client.use_pin_sequence([PIN4])

View File

@ -17,6 +17,7 @@
"amount": 998060, "amount": 998060,
"script_type": "SPENDP2SHWITNESS", "script_type": "SPENDP2SHWITNESS",
"script_sig": "160014209297fb46272a0b7e05139440dbd39daea3e25a", "script_sig": "160014209297fb46272a0b7e05139440dbd39daea3e25a",
"witness":"0247304402206f5b2032601278685921b397416cecd5f70cde48b7203bf2a953283c0cd05f4002205fd21a2c318330f11e8433f8fae6b1c4c257a79da55be9712961214ba2752a1f012103c2c2e65556ca4b7371549324b99390725493c8a6792e093a0bdcbb3e2d7df4ab",
"sequence": 4294967295 "sequence": 4294967295
} }
], ],

View File

@ -17,7 +17,8 @@
"prev_index": 0, "prev_index": 0,
"script_sig": "", "script_sig": "",
"script_type": "SPENDTAPROOT", "script_type": "SPENDTAPROOT",
"sequence": 4294967293 "sequence": 4294967293,
"witness": "0140d961a3fc97561c8a305d0ba78488e8d1efed520ac36b226db76277c38e5ab112236a2f821b6dde82dc4013f74148d1ec6e1e471b952755b5265b7ed594afb723"
} }
], ],
"lock_time": 0, "lock_time": 0,

View File

@ -27,6 +27,7 @@
"amount": 839318869, "amount": 839318869,
"script_type": "SPENDP2SHWITNESS", "script_type": "SPENDP2SHWITNESS",
"script_sig": "160014681ea49259abb892460bf3373e8a0b43d877fa18", "script_sig": "160014681ea49259abb892460bf3373e8a0b43d877fa18",
"witness": "02473044022039bb1710e041e9a936bd3adf39f61906ac4363a67d1c29b1a264ff8cf5a0c95102202829307d82141792125480abe6491f0f0c7d675c3d1a06daa383d617afcb53380121028cbc37e1816a23086fa738c8415def477e813e20f484dbbd6f5a33a37c322251",
"sequence": 4294967295 "sequence": 4294967295
} }
], ],

View File

@ -46,7 +46,7 @@
"T1_bitcoin-test_getaddress.py::test_ltc": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_getaddress.py::test_ltc": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_getaddress.py::test_multisig": "09d812e81c04cef37608f181dcdefb6cd186eb5bb111d47ae0e62d1b3fdd64cf", "T1_bitcoin-test_getaddress.py::test_multisig": "09d812e81c04cef37608f181dcdefb6cd186eb5bb111d47ae0e62d1b3fdd64cf",
"T1_bitcoin-test_getaddress.py::test_multisig_missing[False]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_getaddress.py::test_multisig_missing[False]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_getaddress.py::test_multisig_missing[True]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_getaddress.py::test_multisig_missing[True]": "ef912e3aed3113ed37f982568c0eca17c27feeb67e87e0f7e43cf8b3b3e8d199",
"T1_bitcoin-test_getaddress.py::test_public_ckd": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_getaddress.py::test_public_ckd": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_getaddress.py::test_tbtc": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_getaddress.py::test_tbtc": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_getaddress.py::test_tgrs": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_getaddress.py::test_tgrs": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
@ -137,7 +137,7 @@
"T1_bitcoin-test_komodo.py::test_one_one_rewards_claim": "d6ba8441ad5e4bbdb191eef24fd679637e468cf1e7317b290bf89d2588bea214", "T1_bitcoin-test_komodo.py::test_one_one_rewards_claim": "d6ba8441ad5e4bbdb191eef24fd679637e468cf1e7317b290bf89d2588bea214",
"T1_bitcoin-test_multisig.py::test_15_of_15": "ba0a7e6d6a6f37bf553130acc7aa2471d9d7d804f1564837f149b3b5ecec0461", "T1_bitcoin-test_multisig.py::test_15_of_15": "ba0a7e6d6a6f37bf553130acc7aa2471d9d7d804f1564837f149b3b5ecec0461",
"T1_bitcoin-test_multisig.py::test_2_of_3": "4ece08d5a19590a521e22aae038a492796922f26bb8ba70614211a6ade5bce18", "T1_bitcoin-test_multisig.py::test_2_of_3": "4ece08d5a19590a521e22aae038a492796922f26bb8ba70614211a6ade5bce18",
"T1_bitcoin-test_multisig.py::test_attack_change_input": "f4f75000bfc3af0006a767e9afbc7f3930bc6bad864ca0d31656735d4103bddc", "T1_bitcoin-test_multisig.py::test_attack_change_input": "b5beb346018c89e34a219b5466e3f2f257865b8623c356e2b249742d89e04da3",
"T1_bitcoin-test_multisig.py::test_missing_pubkey": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_multisig.py::test_missing_pubkey": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_multisig_change.py::test_external_external": "b31515c4102fe602ed1e951bf67db483ffb8874dacc4b0359e9ef67d16999178", "T1_bitcoin-test_multisig_change.py::test_external_external": "b31515c4102fe602ed1e951bf67db483ffb8874dacc4b0359e9ef67d16999178",
"T1_bitcoin-test_multisig_change.py::test_external_internal": "ceef2445ab95ec10543e320224b13febef2428d240cb01c58dd7ce878b121997", "T1_bitcoin-test_multisig_change.py::test_external_internal": "ceef2445ab95ec10543e320224b13febef2428d240cb01c58dd7ce878b121997",
@ -147,15 +147,15 @@
"T1_bitcoin-test_multisig_change.py::test_multisig_external_external": "988489b219d84517ca69893a8417a480309695549eb83f5821ebd52381e3a3a2", "T1_bitcoin-test_multisig_change.py::test_multisig_external_external": "988489b219d84517ca69893a8417a480309695549eb83f5821ebd52381e3a3a2",
"T1_bitcoin-test_multisig_change.py::test_multisig_mismatch_change": "10dadffd94e286e588a7006906517205384084c493411191422bed806354caf8", "T1_bitcoin-test_multisig_change.py::test_multisig_mismatch_change": "10dadffd94e286e588a7006906517205384084c493411191422bed806354caf8",
"T1_bitcoin-test_multisig_change.py::test_multisig_mismatch_inputs": "dc50b96dc2f819f8bdccdb7b3d404580b0e08628e222f59661f91b13120c4ef5", "T1_bitcoin-test_multisig_change.py::test_multisig_mismatch_inputs": "dc50b96dc2f819f8bdccdb7b3d404580b0e08628e222f59661f91b13120c4ef5",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-1195487518-6-255-script_types3]": "e6cfec9182dc6fc7e5b5ae63edf516a3a8db2bec008608e901c19c40c29b47a3", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-1195487518-6-255-script_types3]": "c968d01488a358a7287c4e631996081bb490ff6e702b5ff84c3addba1c7b0974",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-1195487518-script_types2]": "e6d5e00fc42472a974be05181a0b071e0b78044bceb2fd02058303303e42edda", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-1195487518-script_types2]": "814c415655b03a6757056456544852a7ec88f8e96fbc97728eba7874d03782f8",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-3h-100h-4-255-script_types1]": "f1d7f1ba4255576af0ffe4dd11dbc31dda17fec533495992a26bc1d22ade567a", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-3h-100h-4-255-script_types1]": "f1d7f1ba4255576af0ffe4dd11dbc31dda17fec533495992a26bc1d22ade567a",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-4-255-script_types0]": "df99b6bcfaf86d8ca67713ba64c085fb57b6fc6004494ef1cf83ccb4b57b8d4b", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-4-255-script_types0]": "df99b6bcfaf86d8ca67713ba64c085fb57b6fc6004494ef1cf83ccb4b57b8d4b",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-49-0-63-0-255-script_types4]": "670669469e2de75d1164e7a0d5c8ad9c96520b0bf9d7aef85e5796853a375cdd", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress[m-49-0-63-0-255-script_types4]": "670669469e2de75d1164e7a0d5c8ad9c96520b0bf9d7aef85e5796853a375cdd",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths0-address_index0]": "3f11a25075140b6bb74d03d2eaf67559003cf5f46d07a778e2552bb281eaaaa8", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths0-address_index0]": "3f11a25075140b6bb74d03d2eaf67559003cf5f46d07a778e2552bb281eaaaa8",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths1-address_index1]": "7c888498d7a49466b2ec128d523f5b7a4af29beccab3e4da8fdf3de2cc3e0e1e", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths1-address_index1]": "7c888498d7a49466b2ec128d523f5b7a4af29beccab3e4da8fdf3de2cc3e0e1e",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths2-address_index2]": "44422270a72666e8fd508c86d3654a851aadc797fbb57a1d5c3a7bbc400911d2", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths2-address_index2]": "e8723bbbe5cb2484f3d1eda6fe19c48f23df6e96c36f6ff0d5d9d499ffc034a6",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths3-address_index3]": "e694e40bfe527e0488901d20dce70192f866c7aac61787c2220e79be439793f3", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths3-address_index3]": "3ab904120014545ec65941dbaeb2c12c1659da2db495e62c5b3c4edd0dce3081",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths4-address_index4]": "c245c47efa462d2e6d487d81f131d3ae561c0f30ad90e55b53b48ba8b46ebea1", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths4-address_index4]": "c245c47efa462d2e6d487d81f131d3ae561c0f30ad90e55b53b48ba8b46ebea1",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths5-address_index5]": "81e4b1b39a135814413c96e9fa4e2639a8ab6b2cb9e945445ee12a99e994b523", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths5-address_index5]": "81e4b1b39a135814413c96e9fa4e2639a8ab6b2cb9e945445ee12a99e994b523",
"T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths6-address_index6]": "5d57a31f47490c5738af8136bf9ec8ee9d0db980d1653b9eaedace9d60e74119", "T1_bitcoin-test_nonstandard_paths.py::test_getaddress_multisig[paths6-address_index6]": "5d57a31f47490c5738af8136bf9ec8ee9d0db980d1653b9eaedace9d60e74119",
@ -164,20 +164,20 @@
"T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-3h-100h-4-255-script_types1]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-3h-100h-4-255-script_types1]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-4-255-script_types0]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-4-255-script_types0]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-49-0-63-0-255-script_types4]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_nonstandard_paths.py::test_getpublicnode[m-49-0-63-0-255-script_types4]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-6-255-script_types3]": "ff92edfa61c3e98430d8fd7cd35f67ff237dc2b9d9d0a35256e11a8f81ffa317", "T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-6-255-script_types3]": "4f7ee19b4e43a709ba945eb24d6bd2a9fe4785e33d54be937d8e3a2551b8d397",
"T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-script_types2]": "6e4c9ae9ab940e6b78862405d585cc1ebb42ecff42aff93af83d66e8b38f2a69", "T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-1195487518-script_types2]": "510797e3346491fc7b3eb6f32f05e6b03640ed5cc8f3d70e1d85b77273578972",
"T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-3h-100h-4-255-script_types1]": "bec27ce6240d3be57e2815b00f66db2f3d0da60b3de8429772aed5ce5dc0263a", "T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-3h-100h-4-255-script_types1]": "bec27ce6240d3be57e2815b00f66db2f3d0da60b3de8429772aed5ce5dc0263a",
"T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-4-255-script_types0]": "8501aeef2b2494e3440d4da64f2d08d24284183c1813753e653039797ffe684f", "T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-4-255-script_types0]": "8501aeef2b2494e3440d4da64f2d08d24284183c1813753e653039797ffe684f",
"T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-49-0-63-0-255-script_types4]": "3f99e0c68e5fb612cb48b7d170a5f36a32e1fc5d8fe7d98f391ef4900411731d", "T1_bitcoin-test_nonstandard_paths.py::test_signmessage[m-49-0-63-0-255-script_types4]": "3f99e0c68e5fb612cb48b7d170a5f36a32e1fc5d8fe7d98f391ef4900411731d",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-6-255-script_types3]": "aa7cafa3fdf34ece7b4d7e1ad20c11d40e3582ed0473e1ea82b3fe6d02b151fa", "T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-6-255-script_types3]": "c8993b0932893430323d4fd34311ff6e13dc69c74247485ee1363fbc8c5b86c6",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-script_types2]": "aa7cafa3fdf34ece7b4d7e1ad20c11d40e3582ed0473e1ea82b3fe6d02b151fa", "T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-1195487518-script_types2]": "c8993b0932893430323d4fd34311ff6e13dc69c74247485ee1363fbc8c5b86c6",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-3h-100h-4-255-script_types1]": "aa7cafa3fdf34ece7b4d7e1ad20c11d40e3582ed0473e1ea82b3fe6d02b151fa", "T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-3h-100h-4-255-script_types1]": "aa7cafa3fdf34ece7b4d7e1ad20c11d40e3582ed0473e1ea82b3fe6d02b151fa",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-4-255-script_types0]": "aa7cafa3fdf34ece7b4d7e1ad20c11d40e3582ed0473e1ea82b3fe6d02b151fa", "T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-4-255-script_types0]": "aa7cafa3fdf34ece7b4d7e1ad20c11d40e3582ed0473e1ea82b3fe6d02b151fa",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-49-0-63-0-255-script_types4]": "682610dc7fcea1c969063ed71b33ab36a62e165b331a713aa3b8148ddb5f6e95", "T1_bitcoin-test_nonstandard_paths.py::test_signtx[m-49-0-63-0-255-script_types4]": "682610dc7fcea1c969063ed71b33ab36a62e165b331a713aa3b8148ddb5f6e95",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths0-address_index0]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths0-address_index0]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths1-address_index1]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths1-address_index1]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths2-address_index2]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths2-address_index2]": "95cf4ccb223cbe1171033da91fdc05802ceafed068fe7a56fedec000b8c421b4",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths3-address_index3]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths3-address_index3]": "95cf4ccb223cbe1171033da91fdc05802ceafed068fe7a56fedec000b8c421b4",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths4-address_index4]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths4-address_index4]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths5-address_index5]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths5-address_index5]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079",
"T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths6-address_index6]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079", "T1_bitcoin-test_nonstandard_paths.py::test_signtx_multisig[paths6-address_index6]": "5c310bb0a73dc35ae1f2910b47ba319893b9039f1ed9762e67e9a97ebf1b6079",
@ -249,12 +249,13 @@
"T1_bitcoin-test_signtx_amount_unit.py::test_signtx[AmountUnit.MILLIBITCOIN]": "8081910bd937c704fb65226dc85e8fc58a7331102aa036a5a97652638a0bde20", "T1_bitcoin-test_signtx_amount_unit.py::test_signtx[AmountUnit.MILLIBITCOIN]": "8081910bd937c704fb65226dc85e8fc58a7331102aa036a5a97652638a0bde20",
"T1_bitcoin-test_signtx_amount_unit.py::test_signtx[AmountUnit.SATOSHI]": "a565b50e63776aad284c42c71bf53e5844dff12f159cbd00679b97d2fb74fd20", "T1_bitcoin-test_signtx_amount_unit.py::test_signtx[AmountUnit.SATOSHI]": "a565b50e63776aad284c42c71bf53e5844dff12f159cbd00679b97d2fb74fd20",
"T1_bitcoin-test_signtx_amount_unit.py::test_signtx[None]": "367fd3c75f30f7224435e3309562d210d9ac6809ce2fae392f7fe75070fda094", "T1_bitcoin-test_signtx_amount_unit.py::test_signtx[None]": "367fd3c75f30f7224435e3309562d210d9ac6809ce2fae392f7fe75070fda094",
"T1_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "9640b4d0bdcde8fec8a4c077e1d12da211d02989458ad34115731237b3dad4f9", "T1_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "e09b404a39a4594f1b7bdc146bc6b8783f62f6c4568785f1ee8e1eae2d902c65",
"T1_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "c478ef5c111b38c0fee1f79df1e0d00ecd5c0e81913e59051047cb0db65ece69", "T1_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "3502ca6c3112b694161713d573a5547d3801894a990c108096900b95f689a734",
"T1_bitcoin-test_signtx_invalid_path.py::test_attack_path_segwit": "473636ae4c43d8a349db09187b74eb4c1aa2b7fe02742d5fa928cdbc2a9e4cfd", "T1_bitcoin-test_signtx_invalid_path.py::test_attack_path_segwit": "473636ae4c43d8a349db09187b74eb4c1aa2b7fe02742d5fa928cdbc2a9e4cfd",
"T1_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail": "75e45c0b6039244afae5cb138aeb4eec2c01e71b91a3ce0d73797ca3b04ca94a", "T1_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_signtx_invalid_path.py::test_invalid_path_pass_forkid": "9c35bfcc194afff453802147f5b4a2d033492e564835be649c3293a62857de59", "T1_bitcoin-test_signtx_invalid_path.py::test_invalid_path_fail_asap": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_signtx_invalid_path.py::test_invalid_path_prompt": "57b20ea95e26ee7f675cbb177b9c781389ef36a02f2815f7939ab83c2d0f8f36", "T1_bitcoin-test_signtx_invalid_path.py::test_invalid_path_pass_forkid": "c83cf4f8324514dba0831e96fa2c20c30cef4121dcd594cf3232af19e4721a07",
"T1_bitcoin-test_signtx_invalid_path.py::test_invalid_path_prompt": "cbf1b6f66151a120689757e6dcd7af9483ceb19e5aa1dff3e1fb5c6f4be8d292",
"T1_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_inputs": "5b980d57e7d1707d8802bff0665e0460fe5c594c91d758b05ee59020b72e3941", "T1_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_inputs": "5b980d57e7d1707d8802bff0665e0460fe5c594c91d758b05ee59020b72e3941",
"T1_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_non_segwit_inputs": "704181fd5aa2ff33a9e5a27874187585ef946f7cdf6e690a7316a33921f25602", "T1_bitcoin-test_signtx_mixed_inputs.py::test_non_segwit_segwit_non_segwit_inputs": "704181fd5aa2ff33a9e5a27874187585ef946f7cdf6e690a7316a33921f25602",
"T1_bitcoin-test_signtx_mixed_inputs.py::test_segwit_non_segwit_inputs": "5b980d57e7d1707d8802bff0665e0460fe5c594c91d758b05ee59020b72e3941", "T1_bitcoin-test_signtx_mixed_inputs.py::test_segwit_non_segwit_inputs": "5b980d57e7d1707d8802bff0665e0460fe5c594c91d758b05ee59020b72e3941",
@ -263,10 +264,10 @@
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[hello world]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[hello world]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[x]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[x]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[]": "9ec64a9408664533abd9b11973f15194a6e4df51abd77a549da8bf67a1f2577c", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[]": "0aaaaec8e263055e9b26e93e839f8fa57c54f7dcebef9d7f94bc175712317c1d",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[hello world]": "9ec64a9408664533abd9b11973f15194a6e4df51abd77a549da8bf67a1f2577c", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[hello world]": "0aaaaec8e263055e9b26e93e839f8fa57c54f7dcebef9d7f94bc175712317c1d",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[x]": "9ec64a9408664533abd9b11973f15194a6e4df51abd77a549da8bf67a1f2577c", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[x]": "0aaaaec8e263055e9b26e93e839f8fa57c54f7dcebef9d7f94bc175712317c1d",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]": "9ec64a9408664533abd9b11973f15194a6e4df51abd77a549da8bf67a1f2577c", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_attack[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]": "0aaaaec8e263055e9b26e93e839f8fa57c54f7dcebef9d7f94bc175712317c1d",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_in_prevtx[]": "92816fb27c0fc626348c8ac485990305a824890ea5c862723d6cd53c7b558516", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_in_prevtx[]": "92816fb27c0fc626348c8ac485990305a824890ea5c862723d6cd53c7b558516",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_in_prevtx[hello world]": "92816fb27c0fc626348c8ac485990305a824890ea5c862723d6cd53c7b558516", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_in_prevtx[hello world]": "92816fb27c0fc626348c8ac485990305a824890ea5c862723d6cd53c7b558516",
"T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_in_prevtx[x]": "92816fb27c0fc626348c8ac485990305a824890ea5c862723d6cd53c7b558516", "T1_bitcoin-test_signtx_prevhash.py::test_invalid_prev_hash_in_prevtx[x]": "92816fb27c0fc626348c8ac485990305a824890ea5c862723d6cd53c7b558516",
@ -275,11 +276,11 @@
"T1_bitcoin-test_signtx_replacement.py::test_attack_steal_change": "3a79850fe95abbd2ca342fc62ba7f6404c4f437977764839a8146789ac811fce", "T1_bitcoin-test_signtx_replacement.py::test_attack_steal_change": "3a79850fe95abbd2ca342fc62ba7f6404c4f437977764839a8146789ac811fce",
"T1_bitcoin-test_signtx_replacement.py::test_p2pkh_fee_bump": "a386c4c61f5fe3b073db6b8725193f5bf7881d7b6fae175ff7d879f7c94eb795", "T1_bitcoin-test_signtx_replacement.py::test_p2pkh_fee_bump": "a386c4c61f5fe3b073db6b8725193f5bf7881d7b6fae175ff7d879f7c94eb795",
"T1_bitcoin-test_signtx_replacement.py::test_p2tr_fee_bump": "00f4f745f88f1072f495e513d6a7747db494bb7c785c32d167652fe1d79509be", "T1_bitcoin-test_signtx_replacement.py::test_p2tr_fee_bump": "00f4f745f88f1072f495e513d6a7747db494bb7c785c32d167652fe1d79509be",
"T1_bitcoin-test_signtx_replacement.py::test_p2tr_invalid_signature": "1f46b6ec89be1742dc4649153e6d13b172c9e93be7c91063650f814e631b757c", "T1_bitcoin-test_signtx_replacement.py::test_p2tr_invalid_signature": "00f4f745f88f1072f495e513d6a7747db494bb7c785c32d167652fe1d79509be",
"T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_finalize": "7b3b6ebee78fed0e64c9689c64c5fbf73099812c5452b2fd2dbe2c23cd69b669", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_finalize": "7b3b6ebee78fed0e64c9689c64c5fbf73099812c5452b2fd2dbe2c23cd69b669",
"T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_fee_bump_from_external": "3d7d6ad6eafe3399f9d3aa5bb358e4c04f83a98b68ccfa8a124b7e63c0f6df78", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_fee_bump_from_external": "3d7d6ad6eafe3399f9d3aa5bb358e4c04f83a98b68ccfa8a124b7e63c0f6df78",
"T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_remove_change": "14544554428651eb22515a88418c71f66de68e581ea2df5b2259843b1931e8fc", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_in_p2sh_remove_change": "14544554428651eb22515a88418c71f66de68e581ea2df5b2259843b1931e8fc",
"T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_invalid_signature": "ee190f0d9f972f005c706a818872a18778aa570737b65e5bebbe6edbe6396008", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_invalid_signature": "7b3b6ebee78fed0e64c9689c64c5fbf73099812c5452b2fd2dbe2c23cd69b669",
"T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_op_return_fee_bump": "897291942225c61f9b641c7a1287960e7c21d80ada6c75414399d76edc41054c", "T1_bitcoin-test_signtx_replacement.py::test_p2wpkh_op_return_fee_bump": "897291942225c61f9b641c7a1287960e7c21d80ada6c75414399d76edc41054c",
"T1_bitcoin-test_signtx_segwit.py::test_attack_change_input_address": "fb82fd6c801028181ea89cc517c1401ca78d319b1b9d60e19e5b1cc0107a083f", "T1_bitcoin-test_signtx_segwit.py::test_attack_change_input_address": "fb82fd6c801028181ea89cc517c1401ca78d319b1b9d60e19e5b1cc0107a083f",
"T1_bitcoin-test_signtx_segwit.py::test_attack_mixed_inputs": "614206bc232bf0a07846361bd21a2a0520c8384f052ba362d227e17c33013270", "T1_bitcoin-test_signtx_segwit.py::test_attack_mixed_inputs": "614206bc232bf0a07846361bd21a2a0520c8384f052ba362d227e17c33013270",
@ -421,7 +422,7 @@
"T1_misc-test_msg_getentropy.py::test_entropy[8]": "10ee154b5db5599420a26df724cf2301afafb9bdb55429e4e091046881d589cb", "T1_misc-test_msg_getentropy.py::test_entropy[8]": "10ee154b5db5599420a26df724cf2301afafb9bdb55429e4e091046881d589cb",
"T1_misc-test_msg_getentropy.py::test_entropy[9]": "10ee154b5db5599420a26df724cf2301afafb9bdb55429e4e091046881d589cb", "T1_misc-test_msg_getentropy.py::test_entropy[9]": "10ee154b5db5599420a26df724cf2301afafb9bdb55429e4e091046881d589cb",
"T1_misc-test_msg_signidentity.py::test_sign": "237d6575385f61c32b574a23168330e40025eb9351202698f2d115db0e146934", "T1_misc-test_msg_signidentity.py::test_sign": "237d6575385f61c32b574a23168330e40025eb9351202698f2d115db0e146934",
"T1_nem-test_getaddress.py::test_nem_getaddress": "e7ee9d6d0fdcf2009703fac7dcde878f72531587c20586d82cd9ca090647fb98", "T1_nem-test_getaddress.py::test_nem_getaddress": "647dbd437bc7d5913ca7cbc8027d0230c3617e8b266f2c89e63c2d1342313557",
"T1_nem-test_signtx_mosaics.py::test_nem_signtx_mosaic_creation": "74f2ed8d59e1aa1185ef7f06fe63e49380dfb185faa0e0d01656bc2bc8ff8c0c", "T1_nem-test_signtx_mosaics.py::test_nem_signtx_mosaic_creation": "74f2ed8d59e1aa1185ef7f06fe63e49380dfb185faa0e0d01656bc2bc8ff8c0c",
"T1_nem-test_signtx_mosaics.py::test_nem_signtx_mosaic_creation_levy": "d782d6c77ea579238f6c5fc81adef34e0a47b4006ee4cf4556747ddc4afa998b", "T1_nem-test_signtx_mosaics.py::test_nem_signtx_mosaic_creation_levy": "d782d6c77ea579238f6c5fc81adef34e0a47b4006ee4cf4556747ddc4afa998b",
"T1_nem-test_signtx_mosaics.py::test_nem_signtx_mosaic_creation_properties": "014cdfbc54f7e6861fbc74e2c5729d6d5ce75b9774130d2cc62de413a96d05c9", "T1_nem-test_signtx_mosaics.py::test_nem_signtx_mosaic_creation_properties": "014cdfbc54f7e6861fbc74e2c5729d6d5ce75b9774130d2cc62de413a96d05c9",
@ -432,9 +433,9 @@
"T1_nem-test_signtx_others.py::test_nem_signtx_importance_transfer": "7a63646daa836594fc56ba6f4d1af5deb0f1ea50c4e931a084ab1636a11546d8", "T1_nem-test_signtx_others.py::test_nem_signtx_importance_transfer": "7a63646daa836594fc56ba6f4d1af5deb0f1ea50c4e931a084ab1636a11546d8",
"T1_nem-test_signtx_others.py::test_nem_signtx_provision_namespace": "11bb4a8dc8991471d8ed896691ebaecd0023b29432976ad519e92ece8f91a96a", "T1_nem-test_signtx_others.py::test_nem_signtx_provision_namespace": "11bb4a8dc8991471d8ed896691ebaecd0023b29432976ad519e92ece8f91a96a",
"T1_nem-test_signtx_transfers.py::test_nem_signtx_encrypted_payload": "b1fd9c5250032b2decce6ef18cb1c6e06e9daea279039b58e01cc28a3f4dc765", "T1_nem-test_signtx_transfers.py::test_nem_signtx_encrypted_payload": "b1fd9c5250032b2decce6ef18cb1c6e06e9daea279039b58e01cc28a3f4dc765",
"T1_nem-test_signtx_transfers.py::test_nem_signtx_known_mosaic": "1efa8ae57b72b7a8220b4b9ad02dd0c08e3c9692673ebe7384144ad07fbc20c3", "T1_nem-test_signtx_transfers.py::test_nem_signtx_known_mosaic": "4de1af57a9f25b81ab5d29a6c96e2cd117145aae6cba3fedd083cac7f8cff1d7",
"T1_nem-test_signtx_transfers.py::test_nem_signtx_known_mosaic_with_levy": "2b55781a0940df17fefdaf12ea46d5f794b15c7898f95a3ed82ccf86ec60871f", "T1_nem-test_signtx_transfers.py::test_nem_signtx_known_mosaic_with_levy": "ebc2635064a4469a98c2a4db2608ec3d006a69386d47e78bbbb8800bb0e7dc2a",
"T1_nem-test_signtx_transfers.py::test_nem_signtx_multiple_mosaics": "cbd7f2a804a0d5c7014ee1bc484d23009ca78c1e220755ece35e99d444dd6f9d", "T1_nem-test_signtx_transfers.py::test_nem_signtx_multiple_mosaics": "483194e161e010b5860086054fd1527ab79bbdfbf6fcbf556047a3e7f9d66d6d",
"T1_nem-test_signtx_transfers.py::test_nem_signtx_simple": "3dac9a2abaab139be3b670d6f8e22d34db0171d09101b447c9de7011ad59b465", "T1_nem-test_signtx_transfers.py::test_nem_signtx_simple": "3dac9a2abaab139be3b670d6f8e22d34db0171d09101b447c9de7011ad59b465",
"T1_nem-test_signtx_transfers.py::test_nem_signtx_unknown_mosaic": "0fd4bec396dc0850d42a43fb6bd23a751565ef143870ae21e1c1ff5faaeb1bef", "T1_nem-test_signtx_transfers.py::test_nem_signtx_unknown_mosaic": "0fd4bec396dc0850d42a43fb6bd23a751565ef143870ae21e1c1ff5faaeb1bef",
"T1_nem-test_signtx_transfers.py::test_nem_signtx_xem_as_mosaic": "9cc4d7978b6296e0d107a20912e4dedcc9ed6eb3229749a7fb515827c85f18ee", "T1_nem-test_signtx_transfers.py::test_nem_signtx_xem_as_mosaic": "9cc4d7978b6296e0d107a20912e4dedcc9ed6eb3229749a7fb515827c85f18ee",
@ -517,9 +518,9 @@
"T1_test_basic.py::test_device_id_same": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_test_basic.py::test_device_id_same": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_test_basic.py::test_features": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_test_basic.py::test_features": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_test_basic.py::test_ping": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_test_basic.py::test_ping": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"T1_test_bip32_speed.py::test_cache": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_test_bip32_speed.py::test_cache": "55f043b3e286b778a02baea8f7c3547208849e2e18f90837bd9374a4a14c5c0b",
"T1_test_bip32_speed.py::test_private_ckd": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_test_bip32_speed.py::test_private_ckd": "55f043b3e286b778a02baea8f7c3547208849e2e18f90837bd9374a4a14c5c0b",
"T1_test_bip32_speed.py::test_public_ckd": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_test_bip32_speed.py::test_public_ckd": "55f043b3e286b778a02baea8f7c3547208849e2e18f90837bd9374a4a14c5c0b",
"T1_test_cancel.py::test_cancel_message_via_cancel[message0]": "de7fc40b2f35e82fa486f1b97ee3e34a96d0a67412537e8a0fddacc0b0b1649d", "T1_test_cancel.py::test_cancel_message_via_cancel[message0]": "de7fc40b2f35e82fa486f1b97ee3e34a96d0a67412537e8a0fddacc0b0b1649d",
"T1_test_cancel.py::test_cancel_message_via_cancel[message1]": "af93b5d0a8ae6b297391a43ff3f6382d0bea1109f4f411f5b306e2e7ced6e814", "T1_test_cancel.py::test_cancel_message_via_cancel[message1]": "af93b5d0a8ae6b297391a43ff3f6382d0bea1109f4f411f5b306e2e7ced6e814",
"T1_test_cancel.py::test_cancel_message_via_initialize[message0]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "T1_test_cancel.py::test_cancel_message_via_initialize[message0]": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
@ -568,7 +569,7 @@
"T1_test_protection_levels.py::test_apply_settings": "af9edc0f8ab95a119d48f8ba3dad120dd95459eb8ec01a7cc8e3e5d8cd615981", "T1_test_protection_levels.py::test_apply_settings": "af9edc0f8ab95a119d48f8ba3dad120dd95459eb8ec01a7cc8e3e5d8cd615981",
"T1_test_protection_levels.py::test_change_pin_t1": "b03b10c08880556b5b5b3909f9d55486b4b27079eb0b56ce339ca020521ab664", "T1_test_protection_levels.py::test_change_pin_t1": "b03b10c08880556b5b5b3909f9d55486b4b27079eb0b56ce339ca020521ab664",
"T1_test_protection_levels.py::test_get_address": "5b7419ecda88e49d9a55af9e7c7dc6d96e5992e11515299c7794de6e2e834eef", "T1_test_protection_levels.py::test_get_address": "5b7419ecda88e49d9a55af9e7c7dc6d96e5992e11515299c7794de6e2e834eef",
"T1_test_protection_levels.py::test_get_entropy_t1": "8af3b06ff6827712110f4fb4a5ae6dced3d96022065aba9cbcfc50ca769fef19", "T1_test_protection_levels.py::test_get_entropy": "a858148a335093e46872966e6411cd1bf9d0899bba6a3c798d7535e01461273b",
"T1_test_protection_levels.py::test_get_public_key": "5b7419ecda88e49d9a55af9e7c7dc6d96e5992e11515299c7794de6e2e834eef", "T1_test_protection_levels.py::test_get_public_key": "5b7419ecda88e49d9a55af9e7c7dc6d96e5992e11515299c7794de6e2e834eef",
"T1_test_protection_levels.py::test_initialize": "3f4bd58ada8b2be88ceaf45b3653e940bd52f48eb42bd5ee43c0a8c7dd392139", "T1_test_protection_levels.py::test_initialize": "3f4bd58ada8b2be88ceaf45b3653e940bd52f48eb42bd5ee43c0a8c7dd392139",
"T1_test_protection_levels.py::test_passphrase_cached": "79a607736c6833a04561231c8db1df8cf6ac186715fc3798be9cc3456a588e24", "T1_test_protection_levels.py::test_passphrase_cached": "79a607736c6833a04561231c8db1df8cf6ac186715fc3798be9cc3456a588e24",
@ -897,9 +898,9 @@
"TT_bitcoin-test_signtx_external.py::test_p2pkh_presigned": "8dd8089941ceb0d82c9425c69d54240f99e3ae7932ef24acd49313d28389b683", "TT_bitcoin-test_signtx_external.py::test_p2pkh_presigned": "8dd8089941ceb0d82c9425c69d54240f99e3ae7932ef24acd49313d28389b683",
"TT_bitcoin-test_signtx_external.py::test_p2pkh_with_proof": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "TT_bitcoin-test_signtx_external.py::test_p2pkh_with_proof": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1",
"TT_bitcoin-test_signtx_external.py::test_p2tr_external_presigned": "c714c4a4ea8b98dfbdd8185925adafbafc62570f415688972d6003a19d7b4d23", "TT_bitcoin-test_signtx_external.py::test_p2tr_external_presigned": "c714c4a4ea8b98dfbdd8185925adafbafc62570f415688972d6003a19d7b4d23",
"TT_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "9919d854e2702be375ad09c74af4fbaaa92d36cc2ed360cc7cfbd196c21a39a6", "TT_bitcoin-test_signtx_external.py::test_p2tr_external_unverified": "b398085c2fa6d4fa1ba97d872f95f3ac2268e9455f5831344f6e34a4badf7a17",
"TT_bitcoin-test_signtx_external.py::test_p2tr_with_proof": "d6723e2243bc38231ec4eb9ed63afd39610460c0d859b4c576b12db1f7915d02", "TT_bitcoin-test_signtx_external.py::test_p2tr_with_proof": "d6723e2243bc38231ec4eb9ed63afd39610460c0d859b4c576b12db1f7915d02",
"TT_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "7780f69c321c49c97ee970617ba91a63a7c375dd5ca3d13dec943c28511fb128", "TT_bitcoin-test_signtx_external.py::test_p2wpkh_external_unverified": "d178bd3e2cadbe0992904b3d32df963b75e1da801804a471e45a1c5c0a9f2581",
"TT_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_presigned": "8313bff77e41aef142c3b25818ab58dcc7e9d658d38e2e8fc50629ebbe05869b", "TT_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_presigned": "8313bff77e41aef142c3b25818ab58dcc7e9d658d38e2e8fc50629ebbe05869b",
"TT_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_with_proof": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", "TT_bitcoin-test_signtx_external.py::test_p2wpkh_in_p2sh_with_proof": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1",
"TT_bitcoin-test_signtx_external.py::test_p2wpkh_presigned": "4608478b1d61415cf0ec93a0ea4397c35d17a91d4b6d25e9c024b77330e398eb", "TT_bitcoin-test_signtx_external.py::test_p2wpkh_presigned": "4608478b1d61415cf0ec93a0ea4397c35d17a91d4b6d25e9c024b77330e398eb",
@ -1560,7 +1561,7 @@
"TT_test_protection_levels.py::test_apply_settings": "2451a804df4a867fef29adf4b71445352a3bface95a797f65bb87cf58c1ef34f", "TT_test_protection_levels.py::test_apply_settings": "2451a804df4a867fef29adf4b71445352a3bface95a797f65bb87cf58c1ef34f",
"TT_test_protection_levels.py::test_change_pin_t2": "d414bdebe6ea6b0f754aec1cdde61133b87fd27cf791ab1bdfdb61866a400d6c", "TT_test_protection_levels.py::test_change_pin_t2": "d414bdebe6ea6b0f754aec1cdde61133b87fd27cf791ab1bdfdb61866a400d6c",
"TT_test_protection_levels.py::test_get_address": "f0ac110de788b3112e04dc2ef131fca011a8dea1c309df37adeb23066729e273", "TT_test_protection_levels.py::test_get_address": "f0ac110de788b3112e04dc2ef131fca011a8dea1c309df37adeb23066729e273",
"TT_test_protection_levels.py::test_get_entropy_t2": "539ac09590d3252f0ccb2eb836c703a16be5a219c8bd46add57be8319a336ae9", "TT_test_protection_levels.py::test_get_entropy": "539ac09590d3252f0ccb2eb836c703a16be5a219c8bd46add57be8319a336ae9",
"TT_test_protection_levels.py::test_get_public_key": "f0ac110de788b3112e04dc2ef131fca011a8dea1c309df37adeb23066729e273", "TT_test_protection_levels.py::test_get_public_key": "f0ac110de788b3112e04dc2ef131fca011a8dea1c309df37adeb23066729e273",
"TT_test_protection_levels.py::test_initialize": "59e518cba8589979f0af46e2acb211d37c96312f1d1a63a899d138ebb2f3ca29", "TT_test_protection_levels.py::test_initialize": "59e518cba8589979f0af46e2acb211d37c96312f1d1a63a899d138ebb2f3ca29",
"TT_test_protection_levels.py::test_passphrase_cached": "7fe34cc300a6f3547eaf72ab4339b758469f1e2722244d2a14d06e55ab1a3716", "TT_test_protection_levels.py::test_passphrase_cached": "7fe34cc300a6f3547eaf72ab4339b758469f1e2722244d2a14d06e55ab1a3716",