Esempio n. 1
0
def derive_child_sec_from_xpub(xpub, path):
    child_xpub = bip32.derive(xpub, path)
    child_xpub_bytes = base58.decode_check(child_xpub)
    # assertion about length?
    child_sec_bytes = child_xpub_bytes[-33:]
    child_sec_hex = child_sec_bytes.hex()
    return child_sec_hex
def xpub_from_xprv(xprv: octets) -> bytes:
    """Neutered Derivation (ND)

    Computation of the extended public key corresponding to an extended
    private key (“neutered” as it removes the ability to sign transactions)
    """

    xprv = base58.decode_check(xprv, 78)
    if xprv[45] != 0:
        raise ValueError("extended key is not a private one")

    i = PRV.index(xprv[:4])

    # serialization data
    xpub = PUB[i]  # version
    # unchanged serialization data
    xpub += xprv[4:5]  # depth
    xpub += xprv[5:9]  # parent pubkey fingerprint
    xpub += xprv[9:13]  # child index
    xpub += xprv[13:45]  # chain code

    p = int_from_octets(xprv[46:])
    P = mult(ec, p, ec.G)
    xpub += octets_from_point(ec, P, True)  # public key
    return base58.encode_check(xpub)
def derive(xkey: octets, path: Union[str, Sequence[int]]) -> bytes:
    """derive an extended key according to path like
       "m/44'/0'/1'/0/10" (absolute) or "./0/10" (relative)
    """

    if isinstance(path, str):
        steps = path.split('/')
        if steps[0] not in {'m', '.'}:
            raise ValueError(f'Invalid derivation path: {path}')
        if steps[0] == 'm':
            decoded = base58.decode_check(xkey, 78)
            t = b'\x00' * 9
            if decoded[4:13] != t:
                raise ValueError("Absolute derivation path for non-master key")

        indexes: List[int] = list()
        for step in steps[1:]:
            hardened = False
            if step[-1] == "'" or step[-1] == "H":
                hardened = True
                step = step[:-1]
            index = int(step)
            index += 0x80000000 if hardened else 0
            indexes.append(index)
    else:
        indexes = path

    for index in indexes:
        xkey = ckd(xkey, index)

    return xkey
Esempio n. 4
0
    def test_wif(self):
        # https://en.bitcoin.it/wiki/Wallet_import_format
        prvkey = 0xC28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D

        uncompressedKey = b'\x80' + prvkey.to_bytes(32, byteorder='big')
        uncompressedWIF = b'5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ'
        wif = base58.encode_check(uncompressedKey)
        self.assertEqual(wif, uncompressedWIF)
        key = base58.decode_check(uncompressedWIF)
        self.assertEqual(key, uncompressedKey)

        compressedKey = b'\x80' + prvkey.to_bytes(32,
                                                  byteorder='big') + b'\x01'
        compressedWIF = b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617'
        wif = base58.encode_check(compressedKey)
        self.assertEqual(wif, compressedWIF)
        key = base58.decode_check(compressedWIF)
        self.assertEqual(key, compressedKey)

        # string
        compressedWIF = 'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617'
        key = base58.decode_check(compressedWIF)
        self.assertEqual(key, compressedKey)

        # string with leading space
        compressedWIF = ' KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617'
        base58.decode_check(compressedWIF)
        self.assertEqual(key, compressedKey)

        # string with trailing space
        compressedWIF = 'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617 '
        base58.decode_check(compressedWIF)
        self.assertEqual(key, compressedKey)
def crack(parent_xpub: octets, child_xprv: octets) -> bytes:
    parent_xpub = base58.decode_check(parent_xpub, 78)
    if parent_xpub[45] not in (2, 3):
        raise ValueError("extended parent key is not a public one")

    child_xprv = base58.decode_check(child_xprv, 78)
    if child_xprv[45] != 0:
        raise ValueError("extended child key is not a private one")

    # check depth
    if child_xprv[4] != parent_xpub[4] + 1:
        raise ValueError("wrong child/parent depth relation")

    # check fingerprint
    Parent_bytes = parent_xpub[45:]
    if child_xprv[5:9] != _h160(Parent_bytes)[:4]:
        raise ValueError("not a child for the provided parent")

    # check normal derivation
    child_index = child_xprv[9:13]
    if child_index[0] >= 0x80:
        raise ValueError("hardened derivation")

    parent_xprv = child_xprv[:4]  # version
    parent_xprv += parent_xpub[4:5]  # depth
    parent_xprv += parent_xpub[5:9]  # parent pubkey fingerprint
    parent_xprv += parent_xpub[9:13]  # child index

    parent_chain_code = parent_xpub[13:45]
    parent_xprv += parent_chain_code  # chain code

    h = HMAC(parent_chain_code, Parent_bytes + child_index, sha512).digest()
    offset = int.from_bytes(h[:32], 'big')
    child = int.from_bytes(child_xprv[46:], 'big')
    parent = (child - offset) % ec.n
    parent_bytes = b'\x00' + parent.to_bytes(32, 'big')
    parent_xprv += parent_bytes  # private key

    return base58.encode_check(parent_xprv)
def address_from_xpub(xpub: octets, version: Optional[octets] = None) -> bytes:
    xpub = base58.decode_check(xpub, 78)
    if xpub[45] not in (2, 3):
        raise ValueError("extended key is not a public one")
    # bitcoin: address version can be derived from xkey version
    # altcoin: address version cannot be derived from xkey version
    #          if xkey version bytes have not been specialized
    # FIXME use BIP44 here
    if version is None:
        xversion = xpub[:4]
        i = PUB.index(xversion)
        version = ADDRESS[i]
    P = point_from_octets(ec, xpub[45:])
    return address_from_pubkey(P, True, version)
def prvkey_from_wif(wif: octets) -> Tuple[int, bool]:
    """Wallet Import Format to (bytes) private key"""

    payload = base58.decode_check(wif)
    if payload[0] != 0x80:
        raise ValueError("Not a private key WIF: missing leading 0x80")

    if len(payload) == ec.psize + 2:       # compressed WIF
        compressed = True
        if payload[ec.psize + 1] != 0x01:  # must have a trailing 0x01
            raise ValueError("Not a compressed WIF: missing trailing 0x01")
        prvkey = int_from_octets(payload[1:-1])
    elif len(payload) == ec.psize + 1:     # uncompressed WIF
        compressed = False
        prvkey = int_from_octets(payload[1:])
    else:
        raise ValueError(f"Not a WIF: wrong size ({len(payload)})")
    
    if not 0 < prvkey < ec.n:
        raise ValueError(f"Not a WIF: private key {hex(prvkey)} not in (0, n)")

    return prvkey, compressed
def ckd(xparentkey: octets, index: Union[octets, int]) -> bytes:
    """Child Key Derivation (CDK)

    Key derivation is normal if the extended parent key is public or
    child_index is less than 0x80000000.

    Key derivation is hardened if the extended parent key is private and
    child_index is not less than 0x80000000.
    """

    if isinstance(index, int):
        index = index.to_bytes(4, 'big')
    elif isinstance(index, str):  # hex string
        index = bytes.fromhex(index)

    if len(index) != 4:
        raise ValueError(f"a 4 bytes int is required, not {len(index)}")

    xparent = base58.decode_check(xparentkey, 78)

    version = xparent[:4]

    # serialization data
    xkey = version  # version
    xkey += (xparent[4] + 1).to_bytes(1, 'big')  # (increased) depth

    if (version in PUB):
        if xparent[45] not in (2, 3):  # not a compressed public key
            raise ValueError("version/key mismatch in extended parent key")
        Parent_bytes = xparent[45:]
        Parent = point_from_octets(ec, Parent_bytes)
        xkey += _h160(Parent_bytes)[:4]  # parent pubkey fingerprint
        if index[0] >= 0x80:
            raise ValueError("no private/hardened derivation from pubkey")
        xkey += index  # child index
        parent_chain_code = xparent[13:45]  # normal derivation
        # actual extended key (key + chain code) derivation
        h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest()
        offset = int.from_bytes(h[:32], 'big')
        Offset = mult(ec, offset, ec.G)
        Child = ec.add(Parent, Offset)
        Child_bytes = octets_from_point(ec, Child, True)
        xkey += h[32:]  # chain code
        xkey += Child_bytes  # public key
    elif (version in PRV):
        if xparent[45] != 0:  # not a private key
            raise ValueError("version/key mismatch in extended parent key")
        parent = int.from_bytes(xparent[46:], 'big')
        Parent = mult(ec, parent, ec.G)
        Parent_bytes = octets_from_point(ec, Parent, True)
        xkey += _h160(Parent_bytes)[:4]  # parent pubkey fingerprint
        xkey += index  # child index
        # actual extended key (key + chain code) derivation
        parent_chain_code = xparent[13:45]
        if (index[0] < 0x80):  # normal derivation
            h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest()
        else:  # hardened derivation
            h = HMAC(parent_chain_code, xparent[45:] + index, sha512).digest()
        offset = int.from_bytes(h[:32], 'big')
        child = (parent + offset) % ec.n
        child_bytes = b'\x00' + child.to_bytes(32, 'big')
        xkey += h[32:]  # chain code
        xkey += child_bytes  # private key
    else:
        raise ValueError("invalid extended key version")

    return base58.encode_check(xkey)
def child_index(xkey: octets) -> bytes:
    xkey = base58.decode_check(xkey, 78)
    if xkey[4] == 0:
        raise ValueError("master key provided")
    return xkey[9:13]
Esempio n. 10
0
print("\n*** [7] Base58 encoding")
wif = base58.encode(checksummed_payload)
print(wif)
assert wif == b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617', "failure"
assert base58.encode_check(
    payload
) == b'KwdMAjGmerYanjeui5SHS7JkmpZvVipYvB2LJGU1ZxJwYvP98617', "failure"

print("\n****** WIF to private key ******")

print("\n*** [1] Base58 WIF")
print(wif)
compressed = len(wif) - 51
print("compressed" if (compressed == 1) else "uncompressed")

print("\n*** [2] Base58 decoding")
checksummed_payload = base58.decode(wif)
print(checksummed_payload.hex())

print("\n*** [3] Extended key (checksum verified)")
payload, checksum = checksummed_payload[:-4], checksummed_payload[-4:]
verified = (sha256(sha256(payload).digest()).digest()[:4] == checksum)
print(payload.hex() + " (" + ("true" if verified else "false") + ")")
print(base58.decode_check(wif).hex())

print("\n*** [4] Private key")
p2 = payload[1:-1].hex() if compressed else payload[1:].hex()
assert int(p2, 16) == q, "failure"
print(p2)
Esempio n. 11
0
    def test_mainnet(self):
        # bitcoin core derivation style
        rootxprv = b'xprv9s21ZrQH143K2ZP8tyNiUtgoezZosUkw9hhir2JFzDhcUWKz8qFYk3cxdgSFoCMzt8E2Ubi1nXw71TLhwgCfzqFHfM5Snv4zboSebePRmLS'

        # m/0'/0'/463'
        addr1 = b'1DyfBWxhVLmrJ7keyiHeMbt7N3UdeGU4G5'
        indexes = [0x80000000, 0x80000000, 0x80000000 + 463]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(rootxprv, indexes)))
        self.assertEqual(addr, addr1)
        path = "m/0'/0'/463'"
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(rootxprv, path)))
        self.assertEqual(addr, addr1)

        # m/0'/0'/267'
        addr2 = b'11x2mn59Qy43DjisZWQGRResjyQmgthki'
        indexes = [0x80000000, 0x80000000, 0x80000000 + 267]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(rootxprv, indexes)))
        self.assertEqual(addr, addr2)
        path = "m/0'/0'/267'"
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(rootxprv, path)))
        self.assertEqual(addr, addr2)

        xkey_version = bip32.PRV_VERSION[0]
        seed = "bfc4cbaad0ff131aa97fa30a48d09ae7df914bcc083af1e07793cd0a7c61a03f65d622848209ad3366a419f4718a80ec9037df107d8d12c19b83202de00a40ad"
        seed = bytes.fromhex(seed)
        xprv = bip32.rootxprv_from_seed(seed, xkey_version)
        xpub = b'xpub661MyMwAqRbcFMYjmw8C6dJV97a4oLss6hb3v9wTQn2X48msQB61RCaLGtNhzgPCWPaJu7SvuB9EBSFCL43kTaFJC3owdaMka85uS154cEh'
        self.assertEqual(bip32.xpub_from_xprv(xprv), xpub)

        ind = [0, 0]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(xprv, ind)))
        self.assertEqual(addr, b'1FcfDbWwGs1PmyhMVpCAhoTfMnmSuptH6g')

        ind = [0, 1]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(xprv, ind)))
        self.assertEqual(addr, b'1K5GjYkZnPFvMDTGaQHTrVnd8wjmrtfR5x')

        ind = [0, 2]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(xprv, ind)))
        self.assertEqual(addr, b'1PQYX2uN7NYFd7Hq22ECMzfDcKhtrHmkfi')

        ind = [1, 0]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(xprv, ind)))
        self.assertEqual(addr, b'1BvSYpojWoWUeaMLnzbkK55v42DbizCoyq')

        ind = [1, 1]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(xprv, ind)))
        self.assertEqual(addr, b'1NXB59hF4QzYpFrB7o6usLBjbk2D3ZqxAL')

        ind = [1, 2]
        addr = bip32.p2pkh_address_from_xpub(bip32.xpub_from_xprv(bip32.derive(xprv, ind)))
        self.assertEqual(addr, b'16NLYkKtvYhW1Jp86tbocku3gxWcvitY1w')

        # version/key mismatch in extended parent key
        temp = base58.decode_check(rootxprv)
        bad_xprv = base58.encode_check(temp[0:45] + b'\x01' + temp[46:])
        self.assertRaises(ValueError, bip32.ckd, bad_xprv, 1)
        #bip32.ckd(bad_xprv, 1)

        # version/key mismatch in extended parent key
        xpub = bip32.xpub_from_xprv(rootxprv)
        temp = base58.decode_check(xpub)
        bad_xpub = base58.encode_check(temp[0:45] + b'\x00' + temp[46:])
        self.assertRaises(ValueError, bip32.ckd, bad_xpub, 1)
        #bip32.ckd(bad_xpub, 1)

        # no private/hardened derivation from pubkey
        self.assertRaises(ValueError, bip32.ckd, xpub, 0x80000000)
Esempio n. 12
0
def _h160_from_address(addr: octets) -> bytes:
    payload = base58.decode_check(addr, 21)
    # FIXME: this is mainnet only
    if payload[0] != 0x00:
        raise ValueError("not a mainnet address")
    return payload[1:]
Esempio n. 13
0
def hash_160_from_address(addr):
    return base58.decode_check(addr)[1:21]