예제 #1
0
def randFieldElement():
    """
    randFieldElement returns a random element of the field underlying the given
    curve using the procedure given in [NSA] A.2.1.
    """
    b = ByteArray(generateSeed(curve.BitSize // 8 + 8))
    n = curve.N - 1
    k = b.int()
    k = k % n
    k = k + 1
    return k
예제 #2
0
class ExtendedKey:
    """
    ExtendedKey houses all the information needed to support a BIP0044
    hierarchical deterministic extended key.
    """
    def __init__(
        self,
        privVer,
        pubVer,
        key,
        pubKey,
        chainCode,
        parentFP,
        depth,
        childNum,
        isPrivate,
    ):
        """
        Args:
            privVer (byte-like): Network version bytes for extended priv keys.
            pubVer (byte-like): Network version bytes for extended pub keys.
            key (byte-like): The key.
            pubKey (byte-like): Will be the same as `key` for public key. Will
                be generated from key if zero is provided.
            chainCode (byte-like): Chain code for key derivation.
            parentFP (ByteArray): parent key fingerprint.
            depth (int): Key depth.
            childNum (int): Child number.
            isPrivate (bool): Whether the key is a private or public key.
        """
        if len(privVer) != 4 or len(pubVer) != 4:
            msg = "Network version bytes of incorrect lengths {} and {}"
            raise DecredError(msg.format(len(privVer), len(pubVer)))
        self.privVer = ByteArray(privVer)
        self.pubVer = ByteArray(pubVer)
        self.key = ByteArray(key)
        self.pubKey = ByteArray(pubKey)
        if self.pubKey.iszero():
            if isPrivate:
                self.pubKey = Curve.publicKey(
                    self.key.int()).serializeCompressed()
            else:
                self.pubKey = self.key
        self.chainCode = ByteArray(chainCode)
        self.parentFP = ByteArray(parentFP)
        self.depth = depth
        self.childNum = childNum
        self.isPrivate = isPrivate

    @staticmethod
    def new(seed):
        """
        new creates a new crypto.ExtendedKey. Implementation based on dcrd
        hdkeychain newMaster. The ExtendedKey created does not have a network
        specified. The extended key returned from newMaster can be used to
        generate coin-type and account keys in accordance with BIP-0032 and
        BIP-0044.

        Args:
            seed (bytes-like): A random seed from which the extended key is made.

        Returns:
            crypto.ExtendedKey: A master hierarchical deterministic key.
        """
        rando.checkSeedLength(len(seed))

        # First take the HMAC-SHA512 of the master key and the seed data:
        # SHA512 hash is 64 bytes.
        lr = hmacDigest(MASTER_KEY, seed)

        # Split "I" into two 32-byte sequences Il and Ir where:
        #   Il = master secret key
        #   Ir = master chain code
        lrLen = int(len(lr) / 2)
        secretKey = lr[:lrLen]
        chainCode = lr[lrLen:]

        # Ensure the key is usable.
        secretInt = int.from_bytes(secretKey, byteorder="big")
        if secretInt > MAX_SECRET_INT or secretInt <= 0:
            raise KeyLengthError("generated key was outside acceptable range")

        parentFp = bytes.fromhex("00 00 00 00")

        return ExtendedKey(
            privVer=ByteArray([0, 0, 0, 0]),
            pubVer=ByteArray([0, 0, 0, 0]),
            key=secretKey,
            pubKey="",
            chainCode=chainCode,
            parentFP=parentFp,
            depth=0,
            childNum=0,
            isPrivate=True,
        )

    def setNetwork(self, netParams):
        """
        Sets the privVer and pubVer fields. This should be used when deriving
        the coin-type extended keys from the root wallet key.

        Args:
            netParams (module): The network parameters.
        """
        self.privVer = netParams.HDPrivateKeyID
        self.pubVer = netParams.HDPublicKeyID

    def deriveCoinTypeKey(self, netParams):
        """
        First two hardened child derivations in accordance with BIP0044.

        Args:
            netParams (module): The network parameters.

        Returns:
            ExtendedKey: The coin-type key.
        """
        coinType = netParams.SLIP0044CoinType
        if coinType > MAX_COIN_TYPE:
            raise ParameterRangeError("coinType too high. %i > %i" %
                                      (coinType, MAX_COIN_TYPE))

        purpose = self.child(44 + HARDENED_KEY_START)

        # Derive the purpose key as a child of the master node.
        coinKey = purpose.child(coinType + HARDENED_KEY_START)
        coinKey.privVer = netParams.HDPrivateKeyID
        coinKey.pubVer = netParams.HDPublicKeyID
        return coinKey

    def child(self, i):
        """
        Child returns a derived child extended key at the given index.  When
        this extended key is a private extended key (as determined by the
        IsPrivate function (TODO: implement IsPrivate)), a private extended key
        will be derived. Otherwise, the derived extended key will also be a
        public extended key.

        When the index is greater than or equal to the HardenedKeyStart
        constant, the derived extended key will be a hardened extended key. It
        is only possible to derive a hardended extended key from a private
        extended key. Consequently, this function will throw an exception if a
        hardened child extended key is requested from a public extended key.

        A hardened extended key is useful since, as previously mentioned, it
        requires a parent private extended key to derive. In other words, normal
        child extended public keys can be derived from a parent public extended
        key (no knowledge of the parent private key) whereas hardened extended
        keys may not be.

        NOTE: There is an extremely small chance (< 1 in 2^127) the specific
        child index does not derive to a usable child. An exception will happen
        if this should occur, and the caller is expected to ignore the invalid
        child and simply increment to the next index.

        There are four scenarios that could happen here:
        1) Private extended key -> Hardened child private extended key
        2) Private extended key -> Non-hardened child private extended key
        3) Public extended key -> Non-hardened child public extended key
        4) Public extended key -> Hardened child public extended key (INVALID!)

        Args:
            i (int): Child number.

        Returns:
            ExtendedKey: The child key.
        """

        # Case #4 is invalid, so error out early.
        # A hardened child extended key may not be created from a public
        # extended key.
        isChildHardened = i >= HARDENED_KEY_START
        if not self.isPrivate and isChildHardened:
            raise ParameterRangeError(
                "cannot generate hardened child from public extended key")

        # The data used to derive the child key depends on whether or not the
        # child is hardened per [BIP32].
        #
        # For hardened children:
        #   0x00 || ser256(parentKey) || ser32(i)
        #
        # For normal children:
        #   serP(parentPubKey) || ser32(i)
        keyLen = 33
        data = ByteArray(bytearray(keyLen + 4))

        if isChildHardened:
            # Case #1.
            # When the child is a hardened child, the key is known to be a
            # private key due to the above early return.  Pad it with a
            # leading zero as required by [BIP32] for deriving the child.
            data[1] = self.key
        else:
            # Case #2 or #3.
            # This is either a public or private extended key, but in
            # either case, the data which is used to derive the child key
            # starts with the secp256k1 compressed public key bytes.
            data[0] = ByteArray(self.pubKey)
        data[keyLen] = ByteArray(i, length=len(data) - keyLen)

        data |= i  # ByteArray will handle the type conversion.

        # Take the HMAC-SHA512 of the current key's chain code and the derived
        # data:
        #   I = HMAC-SHA512(Key = chainCode, Data = data)
        ilr = ByteArray(hmacDigest(self.chainCode.b, data.b, hashlib.sha512))

        # Split "I" into two 32-byte sequences Il and Ir where:
        #   Il = intermediate key used to derive the child
        #   Ir = child chain code
        il = ilr[:len(ilr) // 2]
        childChainCode = ilr[len(ilr) // 2:]

        # See CrazyKeyError docs for an explanation of this condition.
        if il.int() >= Curve.N or il.iszero():
            raise CrazyKeyError(
                "ExtendedKey.child: generated Il outside valid range")

        # The algorithm used to derive the child key depends on whether or not
        # a private or public child is being derived.
        #
        # For private children:
        #   childKey = parse256(Il) + parentKey
        #
        # For public children:
        #   childKey = serP(point(parse256(Il)) + parentKey)
        isPrivate = False
        if self.isPrivate:
            # Case #1 or #2.
            # Add the parent private key to the intermediate private key to
            # derive the final child key.
            #
            # childKey = parse256(Il) + parenKey
            childKey = ByteArray((self.key.int() + il.int()) % Curve.N)
            isPrivate = True
        else:
            # Case #3.
            # Calculate the corresponding intermediate public key for
            # intermediate private key.

            x, y = Curve.scalarBaseMult(il.int())  # Curve.G as ECPointJacobian
            if x == 0 or y == 0:
                raise ParameterRangeError(
                    "ExtendedKey.child: generated pt outside valid range")
            # Convert the serialized compressed parent public key into X
            # and Y coordinates so it can be added to the intermediate
            # public key.
            pubKey = Curve.parsePubKey(self.key)
            # Add the intermediate public key to the parent public key to
            # derive the final child key.
            #
            # childKey = serP(point(parse256(Il)) + parentKey)
            # childX, childY := curve.Add(pt.x, pt.y, pubKey.X, pubKey.Y)
            childX, childY = Curve.add(x, y, pubKey.x, pubKey.y)
            # pk := secp256k1.PublicKey{Curve: secp256k1.S256(), X: childX, Y: childY}
            # childKey = pk.SerializeCompressed()
            childKey = PublicKey(Curve, childX, childY).serializeCompressed()

        # The fingerprint of the parent for the derived child is the first 4
        # bytes of the RIPEMD160(BLAKE256(parentPubKey)).
        parentFP = hash160(self.pubKey.b)[:4]

        return ExtendedKey(
            privVer=self.privVer,
            pubVer=self.pubVer,
            key=childKey,
            pubKey="",
            chainCode=childChainCode,
            parentFP=parentFP,
            depth=self.depth + 1,
            childNum=i,
            isPrivate=isPrivate,
        )

    def deriveAccountKey(self, acct):
        """
        deriveAccountKey derives the extended key for an account according to
        the hierarchy described by BIP0044 given the master node.

        In particular this is the hierarchical deterministic extended key path:
          m/44'/<coin type>'/<account>'

        Args:
            account (int): Account number.

        Returns:
            ExtendedKey: Account key.
        """
        # Enforce maximum account number.
        if acct > MAX_ACCOUNT_NUM:
            raise ParameterRangeError(
                "deriveAccountKey: account number greater than MAX_ACCOUNT_NUM"
            )

        # Derive the account key as a child of the coin type key.
        return self.child(acct + HARDENED_KEY_START)

    def neuter(self):
        """
        neuter returns a new extended public key from this extended private key.
        The same extended key will be returned unaltered if it is already an
        extended public key.

        As the name implies, an extended public key does not have access to the
        private key, so it is not capable of signing transactions or deriving
        child extended private keys. However, it is capable of deriving further
        child extended public keys.

        Returns:
            ExtendedKey: The public extended key.
        """
        # Already an extended public key.
        if not self.isPrivate:
            return self

        # Convert it to an extended public key. The key for the new extended
        # key will simply be the pubkey of the current extended private key.
        #
        # This is the function N((k,c)) -> (K, c) from [BIP32].
        return ExtendedKey(
            privVer=self.privVer,
            pubVer=self.pubVer,
            key=self.pubKey,
            pubKey=self.pubKey,
            chainCode=self.chainCode,
            parentFP=self.parentFP,
            depth=self.depth,
            childNum=self.childNum,
            isPrivate=False,
        )

    def serialize(self):
        """
        Return the extended key in serialized form.

        Returns:
            ByteArray: The serialized extended key.
        """
        if self.key.iszero():
            raise DecredError("unexpected zero key")

        childNumBytes = ByteArray(self.childNum, length=4)
        depthByte = ByteArray(self.depth % 256, length=1)

        # The serialized format is:
        #   version (4) || depth (1) || parent fingerprint (4)) ||
        #   child num (4) || chain code (32) || key data (33) || checksum (4)
        serializedBytes = ByteArray(
            bytearray(0))  # length serializedKeyLen + 4 after appending
        if self.isPrivate:
            serializedBytes += self.privVer
        else:
            serializedBytes += self.pubVer

        serializedBytes += depthByte
        serializedBytes += self.parentFP
        serializedBytes += childNumBytes
        serializedBytes += self.chainCode
        if self.isPrivate:
            serializedBytes += bytearray(1)
            serializedBytes += self.key
        else:
            serializedBytes += self.pubKey

        checkSum = checksum(serializedBytes.b)[:4]
        serializedBytes += checkSum
        return serializedBytes

    def string(self):
        """
        string returns the extended key as a base58-encoded string. See
        `decodeExtendedKey` for decoding.

        Returns:
            str: The encoded extended key.
        """
        return b58encode(self.serialize().bytes()).decode()

    def privateKey(self):
        """
        A PrivateKey structure that can be used for signatures.

        Returns:
            secp256k1.PrivateKey: The private key structure.
        """
        return privKeyFromBytes(self.key)

    def publicKey(self):
        """
        A PublicKey structure of the pubKey.

        Returns:
            secp256k1.PublicKey: The public key structure.
        """
        return Curve.parsePubKey(self.pubKey)