Skip to content

Latest commit

 

History

History
415 lines (337 loc) · 16.9 KB

File metadata and controls

415 lines (337 loc) · 16.9 KB

BIP-0032 library

The BIP-0032 library allows deriving children keys as defined by BIP-0032 and SLIP-0010.

Since this library is wrapped inside the BIP-0044, BIP-0049 and BIP-0084 libraries, there is no need to use it alone unless you need to derive some non-standard paths.
The library currently supports the following elliptic curves for key derivation, each one is implemented by a specific class:

  • Ed25519 (based on SLIP-0010): Bip32Slip10Ed25519 class
  • Ed25519-Blake2b (based on SLIP-0010): Bip32Slip10Ed25519Blake2b class
  • BIP32-Ed25519 (Khovratovich/Law): Bip32KholawEd25519 class
  • Nist256p1 (based on SLIP-0010): Bip32Slip10Nist256p1 class
  • Secp256k1: Bip32Slip10Secp256k1 class

They all inherit from the generic Bip32Base class, which can be extended to implement new elliptic curves derivation.
The curve depends on the specific coin and it's automatically selected if you use the Bip44 library.

Construction from seed

The class can be constructed from a seed. The seed can be specified manually or generated by Bip39SeedGenerator.
The constructed class is the master path, so printing the private key will result in printing the master key.

Code example

import binascii
from bip_utils import Bip39SeedGenerator, Bip32Slip10Secp256k1

# Generate from mnemonic
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
seed_bytes = Bip39SeedGenerator(mnemonic).Generate()
# Specify seed manually
seed_bytes = binascii.unhexlify(b"5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4")

# Construct from seed, derivation path returned: m
bip32_ctx = Bip32Slip10Secp256k1.FromSeed(seed_bytes)
# Print master key in extended format
print(bip32_ctx.PrivateKey().ToExtended())

In addition to a seed, it's also possible to specify a derivation path.

Code example

import binascii
from bip_utils import Bip32Slip10Secp256k1

# Derivation path returned: m/0'/1'/2
seed_bytes = binascii.unhexlify(b"5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4")
bip32_ctx = Bip32Slip10Secp256k1.FromSeedAndPath(seed_bytes, "m/0'/1'/2")
# Print private key for derivation path m/0'/1'/2 in extended format
print(bip32_ctx.PrivateKey().ToExtended())

Construction from extended key

The class can be constructed directly from an extended key.
The returned object will be at the same depth of the specified key.

Code example

from bip_utils import Bip32Slip10Secp256k1

# Private extended key from derivation path m/0'/1 (depth 2)
key_str = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"
# Construct from key (return object has depth 2)
bip32_ctx = Bip32Slip10Secp256k1.FromExtendedKey(key_str)
# Return false
print(bip32_ctx.IsPublicOnly())
# Print keys
print(bip32_ctx.PrivateKey().ToExtended())
print(bip32_ctx.PublicKey().ToExtended())

# Public extended key from derivation path m/0'/1 (depth 2)
key_str = "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
# Construct from key (return object has depth 2)
# The object will be public-only and support only public derivation
bip32_ctx = Bip32Slip10Secp256k1.FromExtendedKey(key_str)
# Return true
print(bip32_ctx.IsPublicOnly())
# Print public key
print(bip32_ctx.PublicKey().ToExtended())

Construction from private key

The class can be constructed directly from a private key, with the possibility to specify the derivation data. If only the key bytes are specified, it will be considered a master key since there is no way to recover the key derivation data from the key bytes.
Therefore, the returned object will have a depth and index equal to zero, a zero chain code and parent fingerprint.

Code example

import binascii
from bip_utils import Bip32KeyData, Bip32Slip10Secp256k1, Secp256k1PrivateKey

# Construct from private key bytes
priv_key_bytes = binascii.unhexlify(b"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
bip32_ctx = Bip32Slip10Secp256k1.FromPrivateKey(priv_key_bytes)
# Or key object directly (the curve shall match the one of the Bip32 class, otherwise a Bip32KeyError will be raised)
bip32_ctx = Bip32Slip10Secp256k1.FromPrivateKey(Secp256k1PrivateKey.FromBytes(priv_key_bytes))
# Print keys and data
print(bip32_ctx.PrivateKey().Raw().ToHex())
print(bip32_ctx.PublicKey().RawCompressed().ToHex())
print(bip32_ctx.Depth().ToInt())
print(bip32_ctx.ChainCode().ToHex())
print(bip32_ctx.ParentFingerPrint().ToHex())

# Construct by specifying derivation data
chain_code_bytes = binascii.unhexlify(b"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508")
bip32_ctx = Bip32Slip10Secp256k1.FromPrivateKey(
    priv_key_bytes,
    Bip32KeyData(
        chain_code=chain_code_bytes,
        depth=1,
        index=2,
        parent_fprint=binascii.unhexlify(b"3442193e")
    )
)
# Print keys and data
print(bip32_ctx.PrivateKey().Raw().ToHex())
print(bip32_ctx.PublicKey().RawCompressed().ToHex())
print(bip32_ctx.Depth().ToInt())
print(bip32_ctx.ChainCode().ToHex())
print(bip32_ctx.ParentFingerPrint().ToHex())

Construction from public key

The class can be constructed directly from a public key, with the possibility to specify the derivation data. If only the key bytes are specified, it will be considered a master key since there is no way to recover the key derivation data from the key bytes.
Therefore, the returned object will have a depth and index equal to zero, a zero chain code and parent fingerprint.
The constructed class will be a public-only object (see the example in the next paragraph), so it won't support hardened derivation.

Code example

import binascii
from bip_utils import Bip32KeyError, Bip32KeyData, Bip32Slip10Secp256k1, Secp256k1PublicKey

# Construct from public key bytes
pub_key_bytes = binascii.unhexlify(b"0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2")
bip32_ctx = Bip32Slip10Secp256k1.FromPublicKey(pub_key_bytes)
# Or key object directly (the curve shall match the one of the Bip32 class, otherwise a Bip32KeyError will be raised)
bip32_ctx = Bip32Slip10Secp256k1.FromPublicKey(Secp256k1PublicKey.FromBytes(pub_key_bytes))
# Print keys and data
print(bip32_ctx.PublicKey().RawCompressed().ToHex())
print(bip32_ctx.Depth().ToInt())
print(bip32_ctx.ChainCode().ToHex())
print(bip32_ctx.ParentFingerPrint().ToHex())

# Return true
print(bip32_ctx.IsPublicOnly())
# Getting the private key will raise a Bip32KeyError
try:
    print(bip32_ctx.PrivateKey().Raw().ToHex())
except Bip32KeyError as ex:
    print(ex)

# Construct by specifying derivation data
chain_code_bytes = binascii.unhexlify(b"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508")
bip32_ctx = Bip32Slip10Secp256k1.FromPublicKey(
    pub_key_bytes,
    Bip32KeyData(
        chain_code=chain_code_bytes,
        depth=2,
        index=1,
        parent_fprint=binascii.unhexlify(b"3442193e")
    )
)
# Print keys and data
print(bip32_ctx.IsPublicOnly())
print(bip32_ctx.PublicKey().RawCompressed().ToHex())
print(bip32_ctx.Depth().ToInt())
print(bip32_ctx.ChainCode().ToHex())
print(bip32_ctx.ParentFingerPrint().ToHex())

Keys derivation

Each time a key is derived, a new instance of the class is returned. This allows to chain the methods call or save a specific key pair for future derivation.
The Bip32KeyIndex.HardenIndex method can be used to make an index hardened.

Code example

import binascii
from bip_utils import Bip32Slip10Secp256k1, Bip32KeyIndex

# Seed bytes
seed_bytes = binascii.unhexlify(b"5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4")
# Path: m
bip32_ctx = Bip32Slip10Secp256k1.FromSeed(seed_bytes)
# Derivation path: m/0'/1'/2/3
bip32_ctx = bip32_ctx.ChildKey(Bip32KeyIndex.HardenIndex(0)) \
                     .ChildKey(Bip32KeyIndex.HardenIndex(1)) \
                     .ChildKey(2)                            \
                     .ChildKey(3)
# Print keys in extended format
print(bip32_ctx.PrivateKey().ToExtended())
print(bip32_ctx.PublicKey().ToExtended())

# Print keys bytes
print(bip32_ctx.PrivateKey().Raw().ToBytes())
print(bytes(bip32_ctx.PrivateKey().Raw()))
print(bip32_ctx.PublicKey().RawCompressed().ToBytes())
print(bytes(bip32_ctx.PublicKey().RawCompressed()))
print(bip32_ctx.PublicKey().RawUncompressed().ToBytes())
print(bytes(bip32_ctx.PublicKey().RawUncompressed()))

# Print keys in hex format
print(bip32_ctx.PrivateKey().Raw().ToHex())
print(str(bip32_ctx.PrivateKey().Raw()))
print(bip32_ctx.PublicKey().RawCompressed().ToHex())
print(str(bip32_ctx.PublicKey().RawCompressed()))
print(bip32_ctx.PublicKey().RawUncompressed().ToHex())
print(str(bip32_ctx.PublicKey().RawUncompressed()))

# Print other BIP32 data
print(bip32_ctx.Index().IsHardened())
print(bip32_ctx.Index().ToInt())
print(int(bip32_ctx.Index()))
print(bip32_ctx.Index().ToBytes())
print(bytes(bip32_ctx.Index()))

print(bip32_ctx.Depth().ToInt())
print(int(bip32_ctx.Depth()))
print(bip32_ctx.Depth().ToBytes())
print(bytes(bip32_ctx.Depth()))

print(bip32_ctx.ChainCode().ToHex())
print(str(bip32_ctx.ChainCode()))
print(bip32_ctx.ChainCode().ToBytes())
print(bytes(bip32_ctx.ChainCode()))

print(bip32_ctx.FingerPrint().IsMasterKey())
print(str(bip32_ctx.FingerPrint()))
print(bip32_ctx.FingerPrint().ToBytes())
print(bytes(bip32_ctx.FingerPrint()))

print(bip32_ctx.ParentFingerPrint().IsMasterKey())
print(bip32_ctx.ParentFingerPrint().ToBytes())
print(bytes(bip32_ctx.ParentFingerPrint()))

# Alternative: use DerivePath method
bip32_ctx = Bip32Slip10Secp256k1.FromSeed(seed_bytes)
bip32_ctx = bip32_ctx.DerivePath("0'/1'/2/3")

# DerivePath derives from the current depth, so it can be split
bip32_ctx = Bip32Slip10Secp256k1.FromSeed(seed_bytes)
bip32_ctx = bip32_ctx.DerivePath("0'/1'")   # Derivation path: m/0'/1'
bip32_ctx = bip32_ctx.DerivePath("2/3")     # Derivation path: m/0'/1'/2/3

# Cannot derive an absolute path from a child key
try:
    bip32_ctx = bip32_ctx.DerivePath("m/4/5")
except ValueError:
    pass

It's also possible to use public derivation (i.e. "watch-only" addresses) by:

  • Converting a private object to a public-only using ConvertToPublic method
  • Constructing a public-only object from a public key

In case of a public-only object, only public derivation will be supported (only not-hardened indexes), otherwise a Bip32KeyError exception will be raised.

Code example

from bip_utils import Bip32KeyError, Bip32KeyIndex, Bip32Slip10Secp256k1

# Derive from a public extended key
key_str = "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
bip32_ctx = Bip32Slip10Secp256k1.FromExtendedKey(key_str)

# Return true
print(bip32_ctx.IsPublicOnly())
# Print public key
print(bip32_ctx.PublicKey().RawCompressed().ToHex())

# Public derivation is used to derive a child key
bip32_ctx = bip32_ctx.ChildKey(0)
bip32_ctx = bip32_ctx.DerivePath("1/2")
# Print key
print(bip32_ctx.PublicKey().RawCompressed().ToHex())

# Getting the private key will raise a Bip32KeyError
try:
    print(bip32_ctx.PrivateKey().Raw().ToHex())
except Bip32KeyError as ex:
    print(ex)

# Deriving with hardened indexes will raise a Bip32KeyError
try:
    bip32_ctx = bip32_ctx.ChildKey(Bip32KeyIndex.HardenIndex(0))
    bip32_ctx = bip32_ctx.DerivePath("1'/2")
except Bip32KeyError as ex:
    print(ex)

# Derive from a private extended key
key_str = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"
bip32_ctx = Bip32Slip10Secp256k1.FromExtendedKey(key_str)
# Convert to public object
bip32_ctx.ConvertToPublic()
# Same as before...

The other BIP32 classes work exactly in the same way.
However, the Bip32Slip10Ed25519 and Bip32Slip10Ed25519Blake2b classes have some differences (as written in SLIP-0010):

  • Not-hardened private key derivation is not supported
  • Public key derivation is not supported

For example:

import binascii
from bip_utils import Bip32KeyError, Bip32Slip10Ed25519, Bip32Slip10Ed25519Blake2b

# Seed bytes
seed_bytes = binascii.unhexlify(b"5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4")
# Only hardened private key derivation, fine
bip32_ctx = Bip32Slip10Ed25519.FromSeedAndPath(seed_bytes, "m/0'/1'")

# Public derivation, Bip32KeyError is raised
try:
    bip32_ctx = Bip32Slip10Ed25519.FromSeedAndPath(seed_bytes, "m/0'/1'")
    bip32_ctx.ConvertToPublic()
    bip32_ctx.ChildKey(0)
except Bip32KeyError as ex:
    print(ex)

# Same as before
try:
    bip32_ctx = Bip32Slip10Ed25519Blake2b.FromSeedAndPath(seed_bytes, "m/0'/1'")
    bip32_ctx.ConvertToPublic()
    bip32_ctx.ChildKey(0)
except Bip32KeyError as ex:
    print(ex)

# Not-hardened private key derivation, Bip32KeyError is raised
try:
    bip32_ctx = Bip32Slip10Ed25519.FromSeedAndPath(seed_bytes, "m/0/1")
    bip32_ctx = Bip32Slip10Ed25519Blake2b.FromSeedAndPath(seed_bytes, "m/0/1")
except Bip32KeyError as ex:
    print(ex)

Serialize/Deserialize keys

The Bip32 module allows also to serialize/deserialize public and private keys.

Code example

import binascii
from bip_utils import (
    Bip32KeyData, Bip32PublicKeySerializer, Bip32PrivateKeySerializer, Bip32KeyDeserializer,
    Secp256k1PublicKey, Secp256k1PrivateKey
)

key_data = Bip32KeyData(
    depth=1,
    index=2,
    chain_code=binascii.unhexlify(b"47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"),
    parent_fprint=binascii.unhexlify(b"3442193e")
)

# Serialize public key
pub_key_bytes = binascii.unhexlify(b"035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56")
pub_key_ser = Bip32PublicKeySerializer.Serialize(
    Secp256k1PublicKey.FromBytes(pub_key_bytes),
    key_data
)
print(pub_key_ser)

# Serialize private key
priv_key_bytes = binascii.unhexlify(b"edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea")
priv_key_ser = Bip32PrivateKeySerializer.Serialize(
    Secp256k1PrivateKey.FromBytes(priv_key_bytes),
    key_data
)
print(priv_key_ser)

# Deserialize a key
deser_key = Bip32KeyDeserializer.DeserializeKey(priv_key_ser)
# Print results
print(deser_key.KeyBytes())
print(deser_key.KeyData().Depth().ToInt())
print(deser_key.KeyData().Index().ToInt())
print(deser_key.KeyData().ChainCode().ToHex())
print(deser_key.KeyData().ParentFingerPrint().ToHex())
print(deser_key.IsPublic())

Parse path

The Bip32 module allows also to parse derivation paths.

Code example

from bip_utils import Bip32Path, Bip32PathParser, Bip32KeyIndex

# Parse path, Bip32PathError is raised in case of errors
path = Bip32PathParser.Parse("0'/1'/2")
# 'h' or 'p' can be used as an alternative character instead of '
path = Bip32PathParser.Parse("0h/1h/2")
path = Bip32PathParser.Parse("0p/1p/2")
# "m" can be added at the beginning if the path is absolute, otherwise it'll be considered relative
path = Bip32PathParser.Parse("m/0'/1'/2")
# If multiple slashes are present, they will be removed. The following path is the same of the previous one
path = Bip32PathParser.Parse("m//0'///1'/2")
# A path can also be constructed directly from a list of indexes
# "True" specifies that the path is absolute, "False" if relative
path = Bip32Path([0, 1, Bip32KeyIndex.HardenIndex(2)], True)

# Get if absolute
print(path.IsAbsolute())
# Get length
print(path.Length())
# Get as string
print(path.ToStr())
print(str(path))
# Print elements info and value
for elem in path:
    print(elem.IsHardened())
    print(elem.ToBytes())
    print(bytes(elem))
    print(elem.ToInt())
    print(int(elem))
# Get as list of integers
path_list = path.ToList()
for elem in path_list:
    print(elem)