예제 #1
0
 async def process_host_command(self, stream, show_screen):
     """
     If command with one of the prefixes is received
     it will be passed to this method.
     Should return a tuple: 
     - stream (file, BytesIO etc) 
     - meta object with title and note
     """
     # reads prefix from the stream (until first space)
     prefix = self.get_prefix(stream)
     if prefix != b"signmessage":
         # WTF? It's not our data...
         raise AppError("Prefix is not valid: %s" % prefix.decode())
     # data format: message to sign<space>derivation_path
     # read all and delete all crap at the end (if any)
     # also message should be utf-8 decodable
     data = stream.read().strip().decode()
     if " " not in data:
         raise AppError("Invalid data encoding")
     arr = data.split(" ")
     derivation_path = arr[-1]
     message = " ".join(arr[:-1])
     # if we have fingerprint
     if not derivation_path.startswith("m/"):
         fingerprint = unhexlify(derivation_path[:8])
         if fingerprint != self.keystore.fingerprint:
             raise AppError("Not my fingerprint")
         derivation_path = "m" + derivation_path[8:]
     derivation_path = bip32.parse_path(derivation_path)
     # ask the user if he really wants to sign this message
     scr = Prompt(
         "Sign message with private key at %s?" %
         bip32.path_to_str(derivation_path), "Message:\n%s" % message)
     res = await show_screen(scr)
     if res is False:
         return None
     sig = self.sign_message(derivation_path, message.encode())
     # for GUI we can also return an object with helpful data
     xpub = self.keystore.get_xpub(derivation_path)
     address = script.p2pkh(xpub.key).address()
     obj = {
         "title": "Signature for the message:",
         "note": "using address %s" % address
     }
     return BytesIO(sig), obj
예제 #2
0
    async def showaddr(self,
                       paths: list,
                       script_type: str,
                       redeem_script=None,
                       show_screen=None) -> str:
        net = self.Networks[self.network]
        if redeem_script is not None:
            redeem_script = script.Script(unhexlify(redeem_script))
        # first check if we have corresponding wallet:
        address = None
        if redeem_script is not None:
            if script_type == b"wsh":
                address = script.p2wsh(redeem_script).address(net)
            elif script_type == b"sh-wsh":
                address = script.p2sh(script.p2wsh(redeem_script)).address(net)
            elif script_type == b"sh":
                address = script.p2sh(redeem_script).address(net)
            else:
                raise WalletError("Unsupported script type: %s" % script_type)

        else:
            if len(paths) != 1:
                raise WalletError("Invalid number of paths, expected 1")
            path = paths[0]
            if not path.startswith("m/"):
                path = "m" + path[8:]
            derivation = bip32.parse_path(path)
            pub = self.keystore.get_xpub(derivation)
            if script_type == b"wpkh":
                address = script.p2wpkh(pub).address(net)
            elif script_type == b"sh-wpkh":
                address = script.p2sh(script.p2wpkh(pub)).address(net)
            elif script_type == b"pkh":
                address = script.p2pkh(pub).address(net)
            else:
                raise WalletError("Unsupported script type: %s" % script_type)

        w, (idx, branch_idx) = self.find_wallet_from_address(address,
                                                             paths=paths)
        if show_screen is not None:
            await show_screen(
                WalletScreen(w, self.network, idx, branch_index=branch_idx))
        addr, _ = w.get_address(idx, self.network, branch_idx)
        return addr
예제 #3
0
    def derive_wallet_xpubs(self, was_recovered: bool):
        xpub_keypath_tuples = []
        if was_recovered:
            address_type_keypaths = [BIP_44_KEYPATH,
                                     BIP_49_KEYPATH,
                                     BIP_84_KEYPATH]
        else:
            address_type_keypaths = [BIP_84_KEYPATH]
        network_keypath = get_keypath_by_network(self.network)
        for address_type_keypath in address_type_keypaths:
            path_str = "{address_type_keypath}/{network_keypath}/{account_keypath}".format(
                address_type_keypath=address_type_keypath,
                network_keypath=network_keypath,
                account_keypath=FIRST_ACCOUNT_KEYPATH
            )
            path = parse_path(path_str)
            xpub = self.master_xpriv.derive(path).to_public()
            xpub_keypath_tuples.append((xpub, path_str))

        return xpub_keypath_tuples
예제 #4
0
 def parse(cls, s):
     fingerprint = None
     parent_derivation = None
     address_derivation = "_"
     m = re.match("\[(.*)\](.*)", s)
     if m:
         parent_derivation = m.group(1)
         if not parent_derivation.startswith("m/"):
             arr = parent_derivation.split("/")
             fingerprint = unhexlify(arr[0])
             if len(fingerprint) != 4:
                 raise ValueError("Invalid fingerprint in derivation path")
             parent_derivation = "m/" + "/".join(arr[1:])
         parent_derivation = bip32.parse_path(parent_derivation)
         s = m.group(2)
     if "/" in s:
         arr = s.split("/")
         address_derivation = "/".join(arr[1:])
         s = arr[0]
     key = bip32.HDKey.from_base58(s)
     return cls(key, fingerprint, parent_derivation, address_derivation)
예제 #5
0
 async def process_host_command(self, stream, show_screen):
     if self.keystore.is_locked:
         raise AppError("Device is locked")
     # reads prefix from the stream (until first space)
     prefix = self.get_prefix(stream)
     # get device fingerprint, data is ignored
     if prefix == b"fingerprint":
         return BytesIO(hexlify(self.keystore.fingerprint)), {}
     # get xpub,
     # data: derivation path in human-readable form like m/44h/1h/0
     elif prefix == b"xpub":
         try:
             path = stream.read().strip()
             # convert to list of indexes
             path = bip32.parse_path(path.decode())
         except:
             raise AppError('Invalid path: "%s"' % path.decode())
         # get xpub
         xpub = self.keystore.get_xpub(bip32.path_to_str(path))
         # send back as base58
         return BytesIO(xpub.to_base58(NETWORKS[self.network]["xpub"]).encode()), {}
     raise AppError("Unknown command")
예제 #6
0
def parse_cc_wallet_txt(stream):
    """Parse coldcard wallet format"""
    name = "Imported wallet"
    script_type = None
    sigs_required = None
    global_derivation = None
    sigs_total = None
    cosigners = []
    current_derivation = None
    # cycle until we read everything
    char = b"\n"
    while char is not None:
        line, char = read_until(stream, b"\r\n", max_len=300)
        # skip comments
        while char is not None and (line.startswith(b"#")
                                    or len(line.strip()) == 0):
            # BW comment on derivation
            if line.startswith(b"# derivation:"):
                current_derivation = bip32.parse_path(
                    line.split(b":")[1].decode().strip())
            line, char = read_until(stream, b"\r\n", max_len=300)
        if b":" not in line:
            continue
        arr = line.split(b":")
        if len(arr) > 2:
            raise AppError("Invalid file format")
        k, v = [a.strip().decode() for a in arr]
        if k == "Name":
            name = v
        elif k == "Policy":
            nums = [int(num) for num in v.split(" of ")]
            assert len(nums) == 2
            m, n = nums
            assert m > 0 and n >= m
            sigs_required = m
            sigs_total = n
        elif k == "Format":
            assert v in CC_TYPES
            script_type = CC_TYPES[v]
        elif k == "Derivation":
            der = bip32.parse_path(v)
            if len(cosigners) == 0:
                global_derivation = der
            else:
                current_derivation = der
        # fingerprint
        elif len(k) == 8:
            cosigners.append(
                (unhexlify(k), current_derivation
                 or global_derivation, bip32.HDKey.from_string(v)))
            current_derivation = None
    assert None not in [
        global_derivation, sigs_total, sigs_required, script_type, name
    ]
    assert len(cosigners) == sigs_total
    xpubs = [
        "[%s]%s/{0,1}/*" % (bip32.path_to_str(der, fingerprint=fgp), xpub)
        for fgp, der, xpub in cosigners
    ]
    desc = "sortedmulti(%d,%s)" % (sigs_required, ",".join(xpubs))
    for sc in reversed(script_type.split("-")):
        desc = "%s(%s)" % (sc, desc)
    return name, desc
예제 #7
0
def get_address_type_keypath(keypath: str):
    return path_to_str(parse_path(keypath)[:1])