Ejemplo n.º 1
0
def get_schemas_for_coin(coin: coininfo.CoinInfo) -> Iterable[PathSchema]:
    # basic patterns
    patterns = [
        PATTERN_BIP44,
        PATTERN_BIP48_RAW,
    ]

    # patterns without coin_type field must be treated as if coin_type == 0
    if coin.slip44 == SLIP44_BITCOIN or (
        coin.fork_id is not None and coin.slip44 != SLIP44_TESTNET
    ):
        patterns.append(PATTERN_BIP45)

    if coin.slip44 == SLIP44_BITCOIN:
        patterns.extend(
            (
                PATTERN_GREENADDRESS_A,
                PATTERN_GREENADDRESS_B,
                PATTERN_GREENADDRESS_SIGN_A,
                PATTERN_GREENADDRESS_SIGN_B,
            )
        )

    # compatibility patterns
    if coin.coin_name in BITCOIN_NAMES:
        patterns.extend(
            (
                PATTERN_CASA,
                PATTERN_UNCHAINED_HARDENED,
                PATTERN_UNCHAINED_UNHARDENED,
                PATTERN_UNCHAINED_DEPRECATED,
            )
        )

    # segwit patterns
    if coin.segwit:
        patterns.extend(
            (
                PATTERN_BIP49,
                PATTERN_BIP84,
                PATTERN_BIP48_P2SHSEGWIT,
                PATTERN_BIP48_SEGWIT,
            )
        )

    schemas = [PathSchema.parse(pattern, coin.slip44) for pattern in patterns]

    # Some wallets such as Electron-Cash (BCH) store coins on Bitcoin paths.
    # We can allow spending these coins from Bitcoin paths if the coin has
    # implemented strong replay protection via SIGHASH_FORKID. However, we
    # cannot allow spending any testnet coins from Bitcoin paths, because
    # otherwise an attacker could trick the user into spending BCH on a Bitcoin
    # path by signing a seemingly harmless BCH Testnet transaction.
    if coin.fork_id is not None and coin.slip44 != SLIP44_TESTNET:
        schemas.extend(
            PathSchema.parse(pattern, SLIP44_BITCOIN) for pattern in patterns
        )

    gc.collect()
    return [schema.copy() for schema in schemas]
    def test_verify_path(self):
        schemas = (
            PathSchema("m/44'/coin_type'", slip44_id=134),
            PathSchema("m/44'/coin_type'", slip44_id=11),
        )
        keychain = Keychain(b"", "secp256k1", schemas)

        correct = (
            [H_(44), H_(134)],
            [H_(44), H_(11)],
        )
        for path in correct:
            keychain.verify_path(path)

        fails = (
            [H_(44), 134],  # path does not match
            [44, 134],  # path does not match (non-hardened items)
            [H_(44), H_(13)],  # invalid second item
        )
        for f in fails:
            with self.assertRaises(wire.DataError):
                keychain.verify_path(f)

        # turn off restrictions
        safety_checks.apply_setting(SafetyCheckLevel.PromptTemporarily)
        for path in correct + fails:
            keychain.verify_path(path)
Ejemplo n.º 3
0
async def _fail_or_warn_if_invalid_path(ctx: wire.Context, schema: PathSchema,
                                        path: List[int],
                                        path_name: str) -> None:
    if not schema.match(path):
        if safety_checks.is_strict():
            raise wire.DataError("Invalid %s" % path_name.lower())
        else:
            await show_warning_path(ctx, path, path_name)
Ejemplo n.º 4
0
def get_schemas_for_coin(coin: coininfo.CoinInfo) -> Iterable[PathSchema]:
    # basic patterns
    patterns = [
        PATTERN_BIP44,
        PATTERN_BIP45,
        PATTERN_PURPOSE48_RAW,
    ]

    # compatibility patterns
    if coin.coin_name in BITCOIN_NAMES:
        patterns.extend(
            (
                PATTERN_GREENADDRESS_A,
                PATTERN_GREENADDRESS_B,
                PATTERN_GREENADDRESS_SIGN_A,
                PATTERN_GREENADDRESS_SIGN_B,
                PATTERN_CASA,
                PATTERN_UNCHAINED_HARDENED,
                PATTERN_UNCHAINED_UNHARDENED,
                PATTERN_UNCHAINED_DEPRECATED,
            )
        )

    # segwit patterns
    if coin.segwit:
        patterns.extend(
            (
                PATTERN_BIP49,
                PATTERN_BIP84,
                PATTERN_PURPOSE48_P2SHSEGWIT,
                PATTERN_PURPOSE48_SEGWIT,
            )
        )

    schemas = [PathSchema.parse(pattern, coin.slip44) for pattern in patterns]

    # some wallets such as Electron-Cash (BCH) store coins on Bitcoin paths
    # we can allow spending these coins from Bitcoin paths if the coin has
    # implemented strong replay protection via SIGHASH_FORKID
    if coin.fork_id is not None:
        schemas.extend(PathSchema.parse(pattern, 0) for pattern in patterns)

    gc.collect()
    return [schema.copy() for schema in schemas]
Ejemplo n.º 5
0
def validate_path_against_script_type(
    coin: coininfo.CoinInfo,
    msg: MsgWithAddressScriptType | None = None,
    address_n: Bip32Path | None = None,
    script_type: InputScriptType | None = None,
    multisig: bool = False,
) -> bool:
    patterns = []

    if msg is not None:
        assert address_n is None and script_type is None
        address_n = msg.address_n
        script_type = msg.script_type or InputScriptType.SPENDADDRESS
        multisig = bool(getattr(msg, "multisig", False))

    else:
        assert address_n is not None and script_type is not None

    if script_type == InputScriptType.SPENDADDRESS and not multisig:
        patterns.append(PATTERN_BIP44)
        if coin.coin_name in BITCOIN_NAMES:
            patterns.append(PATTERN_GREENADDRESS_A)
            patterns.append(PATTERN_GREENADDRESS_B)

    elif (script_type
          in (InputScriptType.SPENDADDRESS, InputScriptType.SPENDMULTISIG)
          and multisig):
        patterns.append(PATTERN_BIP45)
        patterns.append(PATTERN_PURPOSE48_RAW)
        if coin.coin_name in BITCOIN_NAMES:
            patterns.append(PATTERN_GREENADDRESS_A)
            patterns.append(PATTERN_GREENADDRESS_B)
            patterns.append(PATTERN_UNCHAINED_HARDENED)
            patterns.append(PATTERN_UNCHAINED_UNHARDENED)
            patterns.append(PATTERN_UNCHAINED_DEPRECATED)

    elif coin.segwit and script_type == InputScriptType.SPENDP2SHWITNESS:
        patterns.append(PATTERN_BIP49)
        if multisig:
            patterns.append(PATTERN_PURPOSE48_P2SHSEGWIT)
        if coin.coin_name in BITCOIN_NAMES:
            patterns.append(PATTERN_GREENADDRESS_A)
            patterns.append(PATTERN_GREENADDRESS_B)
            patterns.append(PATTERN_CASA)

    elif coin.segwit and script_type == InputScriptType.SPENDWITNESS:
        patterns.append(PATTERN_BIP84)
        if multisig:
            patterns.append(PATTERN_PURPOSE48_SEGWIT)
        if coin.coin_name in BITCOIN_NAMES:
            patterns.append(PATTERN_GREENADDRESS_A)
            patterns.append(PATTERN_GREENADDRESS_B)

    return any(
        PathSchema.parse(pattern, coin.slip44).match(address_n)
        for pattern in patterns)
    def test_verify_path_special_ed25519(self):
        schema = PathSchema("m/44'/coin_type'/*", slip44_id=134)
        k = Keychain(b"", "ed25519-keccak", [schema])

        # OK case
        k.verify_path([H_(44), H_(134)])

        # failing case: non-hardened component with ed25519-like derivation
        with self.assertRaises(wire.DataError):
            k.verify_path([H_(44), H_(134), 1])
    def test_get_keychain(self):
        seed = bip39.seed(" ".join(["all"] * 12), "")
        cache.set(cache.APP_COMMON_SEED, seed)

        schema = PathSchema("m/44'/1'", 0)
        keychain = await_result(
            get_keychain(wire.DUMMY_CONTEXT, "secp256k1", [schema]))

        # valid path:
        self.assertIsNotNone(keychain.derive([H_(44), H_(1)]))

        # invalid path:
        with self.assertRaises(wire.DataError):
            keychain.derive([44])
Ejemplo n.º 8
0
from micropython import const

from apps.common import HARDENED
from apps.common.paths import PathSchema

SLIP44_ID = 1815

BYRON_ROOT = [44 | HARDENED, SLIP44_ID | HARDENED]
SHELLEY_ROOT = [1852 | HARDENED, SLIP44_ID | HARDENED]

# fmt: off
SCHEMA_PUBKEY = PathSchema("m/[44,1852]'/coin_type'/account'/*", SLIP44_ID)
SCHEMA_ADDRESS = PathSchema(
    "m/[44,1852]'/coin_type'/account'/[0,1,2]/address_index", SLIP44_ID)
# staking is only allowed on Shelley paths with suffix /2/0
SCHEMA_STAKING = PathSchema("m/1852'/coin_type'/account'/2/0", SLIP44_ID)
SCHEMA_STAKING_ANY_ACCOUNT = PathSchema(
    "m/1852'/coin_type'/[0-%s]'/2/0" % (HARDENED - 1), SLIP44_ID)
# fmt: on

# the maximum allowed change address.  this should be large enough for normal
# use and still allow to quickly brute-force the correct bip32 path
MAX_SAFE_CHANGE_ADDRESS_INDEX = const(1_000_000)
MAX_SAFE_ACCOUNT_INDEX = const(100) | HARDENED
ACCOUNT_PATH_INDEX = const(2)
BIP_PATH_LENGTH = const(5)

CHANGE_OUTPUT_PATH_NAME = "Change output path"
CHANGE_OUTPUT_STAKING_PATH_NAME = "Change output staking path"
CERTIFICATE_PATH_NAME = "Certificate path"
POOL_OWNER_STAKING_PATH_NAME = "Pool owner staking path"
Ejemplo n.º 9
0
from micropython import const

from apps.common.paths import HARDENED, PathSchema

SLIP44_ID = 1815

BYRON_ROOT = [44 | HARDENED, SLIP44_ID | HARDENED]
SHELLEY_ROOT = [1852 | HARDENED, SLIP44_ID | HARDENED]
MULTISIG_ROOT = [1854 | HARDENED, SLIP44_ID | HARDENED]
MINTING_ROOT = [1855 | HARDENED, SLIP44_ID | HARDENED]

# fmt: off
SCHEMA_PUBKEY = PathSchema.parse("m/[44,1852,1854]'/coin_type'/account'/*", SLIP44_ID)
# minting has a specific schema for key derivation - see CIP-1855
SCHEMA_MINT = PathSchema.parse(f"m/1855'/coin_type'/[0-{HARDENED - 1}]'", SLIP44_ID)
SCHEMA_PAYMENT = PathSchema.parse("m/[44,1852]'/coin_type'/account'/[0,1]/address_index", SLIP44_ID)
# staking is only allowed on Shelley paths with suffix /2/0
SCHEMA_STAKING = PathSchema.parse("m/1852'/coin_type'/account'/2/0", SLIP44_ID)
SCHEMA_STAKING_ANY_ACCOUNT = PathSchema.parse(f"m/1852'/coin_type'/[0-{HARDENED - 1}]'/2/0", SLIP44_ID)
# fmt: on

ACCOUNT_PATH_INDEX = const(2)
ACCOUNT_PATH_LENGTH = const(3)
CHAIN_STAKING_KEY = const(2)

CHANGE_OUTPUT_PATH_NAME = "Change output path"
CHANGE_OUTPUT_STAKING_PATH_NAME = "Change output staking path"
CERTIFICATE_PATH_NAME = "Certificate path"
POOL_OWNER_STAKING_PATH_NAME = "Pool owner staking path"
WITNESS_PATH_NAME = "Witness path"
Ejemplo n.º 10
0
async def _fail_or_warn_if_invalid_path(ctx: wire.Context, schema: PathSchema,
                                        path: list[int],
                                        path_name: str) -> None:
    if not schema.match(path):
        await _fail_or_warn_path(ctx, path, path_name)
Ejemplo n.º 11
0
from micropython import const

from apps.common import HARDENED
from apps.common.paths import PathSchema

SLIP44_ID = 1815

BYRON_ROOT = [44 | HARDENED, SLIP44_ID | HARDENED]
SHELLEY_ROOT = [1852 | HARDENED, SLIP44_ID | HARDENED]

# fmt: off
SCHEMA_PUBKEY = PathSchema("m/[44,1852]'/coin_type'/account'/*", SLIP44_ID)
SCHEMA_ADDRESS = PathSchema(
    "m/[44,1852]'/coin_type'/account'/[0,1,2]/address_index", SLIP44_ID)
# staking is only allowed on Shelley paths with suffix /2/0
SCHEMA_STAKING = PathSchema("m/1852'/coin_type'/account'/2/0", SLIP44_ID)
# fmt: on

# the maximum allowed change address.  this should be large enough for normal
# use and still allow to quickly brute-force the correct bip32 path
MAX_CHANGE_ADDRESS_INDEX = const(1_000_000)
ACCOUNT_PATH_INDEX = const(2)
BIP_PATH_LENGTH = const(5)


def unharden(item: int) -> int:
    return item ^ (item & HARDENED)
Ejemplo n.º 12
0
 async def _fail_or_warn_if_invalid_path(self, schema: PathSchema,
                                         path: list[int],
                                         path_name: str) -> None:
     if not schema.match(path):
         await self._fail_or_warn_path(path, path_name)