I did some digging into Coinbase's offline multisig tool to figure out what the user seed actually was and their derivation paths. It turns out the user seed is just that, a seed that is used to get the Master Private Key. This is done by hashing it with HMAC-SHA512 where the key is the string Bitcoin seed
and the data is the binary form of the user seed (which means decode it and get the binary data).
To do this with pybitcointools
, we first decode the WIF user seed with decode_privkey
and re-encode it with encode_privkey
in order to get the binary form. Then we pass that into bip32_master_key
to get the bip32 master key from that seed. And from there, just call bip32_ckd
to get the extended private key of whichever index child we want. Lastly we extract the actual private key with bip32_bin_extract_key
and encode that with encode_privkey
in order to get it into WIF form which can be imported and used in other wallet software.
The below code does all of this for you and will print out the extended master private key derived from the seed, the extended public key derived from the master key (which should match the public key you have), the extended child private key, and that child key in WIF.
Replace the values for USER_PUB, USER_SEED, and I with your actual user public key, user seed, and child index that you want. The values I have here are real and will work.
from pybitcointools.deterministic import bip32_ckd, bip32_master_key, bip32_bin_extract_key, bip32_privtopub
from pybitcointools.main import decode_privkey, encode_privkey
USER_PUB = 'xpub661MyMwAqRbcEzdGMFKZXuVwbyHY2zKhiw6YFTrULfNBF53QVfXyoAiKMZKWkQA5444NXtLj9HPmgyN1xLCiJJ9dKAy4nLZJ87trHRnwUDb'
USER_SEED = 'L3XCYcQ2pwY3YTuwyPXzWsvVtGnCp4zL2ajP3XQt2pVhPuKiVM7r'
I = 14
user_seed = encode_privkey(decode_privkey(USER_SEED), 'bin')
priv = bip32_master_key(user_seed)
derived_pub = bip32_privtopub(priv)
child_priv = bip32_ckd(priv, I)
assert USER_PUB == derived_pub
print "Master Private Key corresponding to seed: " + priv
print "Master Public Key corresponding to seed: " + derived_pub
print "Extended Private key at index " + str(I) + ": " + child_priv
print "WIF format of that key: " + encode_privkey(bip32_bin_extract_key(child_priv), 'wif_compressed')