diff --git a/shared/ccc.py b/shared/ccc.py index ab635180..138cbecf 100644 --- a/shared/ccc.py +++ b/shared/ccc.py @@ -1092,7 +1092,7 @@ async def sssp_enable(): First step is to define a new PIN code that is used when you want to bypass or \ disable this feature. ''', - title="Spending Policy") + title="Spending Policy" if version.has_qwerty else "Spend Policy") if ch != 'y': # just a tourist diff --git a/shared/chains.py b/shared/chains.py index afed090b..a4029a6e 100644 --- a/shared/chains.py +++ b/shared/chains.py @@ -511,4 +511,13 @@ def verify_recover_pubkey(sig, digest): except: raise ValueError('invalid signature') + +def type_from_xpub_version(xpub_ver): + # https://github.com/satoshilabs/slips/blob/master/slip-0132.md + if xpub_ver in [0x0488b21e, 0x049d7cb2, 0x04b24746, 0x0295b43f, 0x02aa7ed3]: + return "BTC" + else: + assert xpub_ver in [0x043587cf, 0x044a5262, 0x045f1cf6, 0x024289ef, 0x02575483] + return "XTN" + # EOF diff --git a/shared/desc_utils.py b/shared/desc_utils.py index 4cdff450..cec7e424 100644 --- a/shared/desc_utils.py +++ b/shared/desc_utils.py @@ -282,26 +282,9 @@ def compile(self): @classmethod def parse_key(cls, key_str): assert key_str[1:4].lower() == b"pub", "only extended pubkeys allowed" - # extended key - # or xpub or tpub as we use descriptors (SLIP-132 NOT allowed) - hint = key_str[0:1].lower() - if hint == b"x": - chain_type = "BTC" - elif hint == b"t": - chain_type = "XTN" - else: - # slip (ignore any implied address format) - chain_type = "BTC" if hint in b"yz" else "XTN" - node = ngu.hdnode.HDNode() - node.deserialize(key_str) - try: - assert node.privkey() is None, "no privkeys" - except ValueError: - # ValueError is thrown from libngu if key is public - pass - - return node, chain_type + version = node.deserialize(key_str) + return node, chains.type_from_xpub_version(version) def validate(self, my_xfp, disable_checks=False): assert self.chain_type == chains.current_key_chain().ctype, "wrong chain" @@ -311,9 +294,16 @@ def validate(self, my_xfp, disable_checks=False): xfp = self.origin.cc_fp is_mine = (xfp == my_xfp) - # raises ValueError on invalid pubkey (should be in libngu) + # raises ValueError on invalid pubkey in libngu + # https://github.com/switck/libngu/blob/master/ngu/hdnode.c#L148 # invalid public key not allowed even with disable checks - ngu.secp256k1.pubkey(self.node.pubkey()) + # ngu.secp256k1.pubkey(self.node.pubkey()) + + try: + assert self.node.privkey() is None, "no privkeys" + except ValueError: + # ValueError is thrown from libngu if key is public + pass if not disable_checks: depth = self.node.depth() @@ -410,7 +400,6 @@ def from_cc_json(cls, vals, af_str): # new firmware, prefer key expression return cls.from_string(vals[key_exp]) - # TODO node, _, _, _ = chains.slip132_deserialize(vals[af_str]) ek = chains.current_chain().serialize_public(node) return cls.from_cc_data(vals["xfp"], vals["%s_deriv" % af_str], ek) @@ -418,13 +407,11 @@ def from_cc_json(cls, vals, af_str): @classmethod def from_psbt_xpub(cls, ek_bytes, xfp_path): xfp, *path = xfp_path - koi = KeyOriginInfo(a2b_hex(xfp2str(xfp)), path) - # TODO this should be done by C code, no need to base58 encode/decode - # byte-serialized key should be decodable - ek = ngu.codecs.b58_encode(ek_bytes) - node, chain_type = cls.parse_key(ek.encode()) - - return cls(node, koi, KeyDerivationInfo(), chain_type=chain_type) + koi = KeyOriginInfo(ustruct.pack("