Simon Volpert httpay / master coinaddress / keys.py
master

Tree @master (Download .tar.gz)

keys.py @masterraw · history · blame

import hashlib
import hmac
from binascii import hexlify, unhexlify

from ecdsa.curves import SECP256k1
from ecdsa.ecdsa import Public_key as ECDSAPublicKey

from .utils import int_to_hex, create_verifying_key


class PublicKey:

    def __init__(self, chain_code, verifying_key):
        self.verifying_key = verifying_key
        self.chain_code = chain_code

    def get_child_from_path(self, path: str):
        parts = path.split('/')
        node = self
        for p in parts:
            if 'm' in p or "'" in p:
                raise RuntimeError("Can't be used to generate private keys")
            part_index = int(p)
            if part_index < 0:
                raise ValueError("Index can't be less than 0")
            node = node.get_child(part_index)
        return node

    def get_child(self, child_number):
        """Derive a child key.

        :param child_number: The number of the child key to compute
        :type child_number: int

        This derivation is fully described at
        https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-functions  # noqa
        """

        child_number_hex = int_to_hex(child_number, 8)

        data = self.hex()

        data += child_number_hex

        # Compute a 64 Byte `i_full` that is the HMAC-SHA512, using
        # self.chain_code as the seed, and data as the message.
        i_full = hmac.new(
            unhexlify(self.chain_code),
            msg=unhexlify(data),
            digestmod=hashlib.sha512
        ).digest()
        # split `i_full` into its 32 Byte components.
        i_left, i_right = i_full[:32], i_full[32:]

        c_i = hexlify(i_right)

        # only use public information for this derivation
        g = SECP256k1.generator
        i_left_int = int(hexlify(i_left), 16)
        point = (
            ECDSAPublicKey(g, g * i_left_int).point +
            self.verifying_key.pubkey.point
        )
        # `i_right` is the child's chain code

        child = self.__class__(
            chain_code=c_i,
            verifying_key=create_verifying_key(point.x(), point.y())
        )

        return child

    @property
    def point(self):
        return self.verifying_key.pubkey.point

    def hex(self) -> bytes:
        x, y = self.point.x(), self.point.y()
        parity = 2 + (y & 1)  # 0x02 even, 0x03 odd
        return int_to_hex(parity, 2) + int_to_hex(x, 64)

    def __bytes__(self) -> bytes:
        nbytes = self.verifying_key.curve.baselen
        x = self.point.x().to_bytes(nbytes, 'big')
        y = self.point.y().to_bytes(nbytes, 'big')
        return bytes([0x04]) + x + y