OCC - Open Crypto Control
How to harmonize the set-up of key material to operate all three of them as hd-wallets.
Start with an USB-connected trustanchor to a host computer. Create a mnemonic on the trustanchor and ask for the WIF of the master key. This is necessary as neither bitcoin core nor the Blockstream elementsd client allow import of mnemonic phrases as they don’t directly support BIP-39
Create a new mnemonic phrase
print("Get a Mnemonic")
msg = OSCMessage('/IHW/bip39Mnemonic', ',is', [0, ""])
send_osc_message(msg)
time.sleep(0.2)
> OSCMessage(addrpattern='/IHW/bip39Mnemonic',
typetags=',iss',
arguments=(0, '', 'focus nature unfair swap kingdom supply weather piano fine just brief maximum federal nature goat cash crystal rally response joy unique drum merit surprise'))
Derive the root or master key in different formats
msg = OSCMessage('/IHW/bip39MnemonicToPrivateKey', ',ss',["focus nature unfair swap kingdom supply weather piano fine just brief maximum federal nature goat cash crystal rally response joy unique drum merit surprise", ""])
send_osc_message(msg)
time.sleep(0.5)
> OSCMessage(addrpattern='/IHW/Bip39MnemonicToPrivateKey',
typetags=',sss',
arguments=('005788b752c703494ca821658ae32bf0c93bf07cb7b6ae71b1dbacaa61c5473d05',
'tprv8ZgxMBicQKsPdE9ixNbtp3Qsf4WJUSc6LEf2nh8bgVPrxPQjfbWy83WxWsAF18QXjNepJoJD9iKvjeZ6nBFhEhzWv2TkQsw7NoHm2pGymsm',
'cMbNCWdLK3eqENuVUcU6oCcdme4fBgNdUEJAb9sTuuMxPRVdCK65'))
In elementsd we set-up first a blank wallet
createwallet 'walletname' false true ""
Then we import the seed via the WIF encoded masterkey we’ve got from the trustanchor
sethdseed true cMbNCWdLK3eqENuVUcU6oCcdme4fBgNdUEJAb9sTuuMxPRVdCK65
Now the hd-wallet inside elementsd (or the same way inside bitcoin core) will transact and sign on top of this key material derived from the newly set seed. to control this we will export the wallet including its Extended Master Private Key.
dumpwallet 'path/filename'
Which delivers a wallet dump file of this kind:
# Wallet dump created by Bitcoin v22.1.1
# * Created on 2023-10-04T16:39:35Z
# * Best block at time of backup was 1072013 (7d074fd656c27c553b770195b9841e22cfa88eae1abd0ecf7898c3052180322f),
# mined on 2023-10-04T16:39:02Z
# extended private masterkey: tprv8ZgxMBicQKsPdWraFbnhmthZQBF472wuTpFzpcKhPBBCBjCYN4kzoScEwCidWctCMwaN7t3W8sBFxuRLYsw2m4VvC4uTProyz62mjScW4dy
# Master private blinding key: d2a32a318d588afcb968fc4a6ca929f4d8b3b3428b36922707964cc528c2d497
cTzM2sYBg9Wztme5N5ENWYSGiquM5HUyRwZvuy3oZP5mhHgCKFhx 2023-10-04T16:39:22Z reserve=1 # addr=tex1qqqga9dzjkedxnwnhkmda42z0ez7u8dwrfx5s6f hdkeypath=m/0'/0'/252'
...
Of relevance is the extended master private key which is the serialization of the core of a hd-wallet object. To deserialize this key we can use the following Python3 code
import base58
import binascii
def deserialize_extended_key(extended_key):
# Decode the extended key using base58
decoded = base58.b58decode_check(extended_key)
# Break down the decoded key into its components
version = decoded[:4]
depth = decoded[4:5]
parent_fingerprint = decoded[5:9]
child_number = decoded[9:13]
chain_code = decoded[13:45]
key_data = decoded[45:]
# Convert to hexadecimal for easier readability
return {
"version": binascii.hexlify(version).decode(),
"depth": binascii.hexlify(depth).decode(),
"parent_fingerprint": binascii.hexlify(parent_fingerprint).decode(),
"child_number": binascii.hexlify(child_number).decode(),
"chain_code": binascii.hexlify(chain_code).decode(),
"key_data": binascii.hexlify(key_data).decode()
}
# Extended master key
extended_key = "tprv8ZgxMBicQKsPdWraFbnhmthZQBF472wuTpFzpcKhPBBCBjCYN4kzoScEwCidWctCMwaN7t3W8sBFxuRLYsw2m4VvC4uTProyz62mjScW4dy"
# Deserialize and print the components
components = deserialize_extended_key(extended_key)
print(components)
Which results is:
python3 '/Users/thomasfuerstner/Downloads/test/occExtendedMasterKey.py'
{'version': '04358394', 'depth': '00', 'parent_fingerprint': '00000000', 'child_number': '00000000', 'chain_code': '4088adb8ebf8ed953ba7bc16e3982460b64fabd896eddd821ff0e4bf4b01ad04', 'key_data': '00b9aab579aa1dc5f73e59450fb7b817b7e7ca583a03e98297296cb49d414d415f'}
Consequently, we can set up our trustanchor to operate on top of the extended master private key - hd wallet object instead of the seed from the mnemonic phrase.
This also means that all signing with key material done from within in elementsd on raw transactions can now also be done on the trustanchor acting as a real HSM - hardwrae security module
But how exactly are elementsd and bitcoin core handling the sethdseed command to get to this result?
It takes the WIF key: ‘cMbNCWdLK3eqENuVUcU6oCcdme4fBgNdUEJAb9sTuuMxPRVdCK65’ turns it back into the raw private key and calculates it public key by multiplying its raw key with the curve base point.
> bx wif-to-public --config ./bx.cfg cMbNCWdLK3eqENuVUcU6oCcdme4fBgNdUEJAb9sTuuMxPRVdCK65
02aa3dc46791b9f99971551e8162f982867246420f35f3f0a473bc688cd09e2b7f
The public key becomes than the seed for calculating the new extended master private key. That’s all