A quick little blog post on how I haven't forgotten about HIVE completely for my coinZdense project. The project isn't a HIVE project any more right now for well known reasons, but that doesn't mean I won't work hard to make it possible for others to make coinZdense work well with HIVE for if others want to use coinZdense, once finished, as part of a solution for making HIVE quantum resistant in the future.
For now, coinZdense is projected to have three sub-APIs:
- signing
- validation
- wallet
Signing provides the code for making quantum-secure multi-level hash-based signatures. Validation provides functionality for validating these signatures. and wallet contains the code for securing signing keys with a password, and for the sake of HIVE (and possibly other projects), to deterministically decompose wallets.
For Python, the signing API is almost functional. Some fixes are needed to adhere to the latest binary signature format specification. Validation is yet to be implemented. The wallet API though has now been implemented using libsodium argon2id, xsalsa20 and poly1305.
So let's look at how this API is supposed to be used. Let's assume we are porting HIVE to use coinZdense. In hive you have an owner key, an active key and a posting key. In coinZdense, a wallet contains just one, but the important thing we need to support to accomodate a setup like HIVE is to make it possible to always reconstruct the same version of a sub key from the higher level key.
Let's do a little walktrhough of some demo code. First, lets set some paths and hard coded passphrases.
# here we store our wallets
owner_wallet_path = "./coinzdense-owner.wallet"
posting_wallet_path = "./coinzdense-posting.wallet"
# hard-coded passphrase, bad idea, but this is just a demo
owner_passphrase = b"This is a stupid passphrase"
posting_passphrase = b"Another dumb password"
# Something we want to sign
message = "Ok, ok, I admit it, it was me, I did it."
Now what we want is the posting key. If it's on disk we open the posting wallet, if it isn't we derive it from the owner wallet. If the owner wallet isn't there either, we create that one first.
if os.path.exists(posting_wallet_path):
# Use existing posting wallet if it exists
print("Reading posting wallet")
with open(posting_wallet_path,"rb") as wfil:
subwallet = coinzdense.wallet.open_wallet(wfil.read(),
posting_passphrase)
else:
# Ok, no posting wallet, check if the owner wallet does exist
if os.path.exists(owner_wallet_path):
# If it does, open it
print("Reading owner wallet")
with open(owner_wallet_path,"rb") as wfil:
wallet = coinzdense.wallet.open_wallet(wfil.read(),
owner_passphrase)
else:
print("Creating owner wallet")
# Get some randomness
salt = random(SALTBYTES)
key = random(SecretBox.KEY_SIZE)
# create the new wallet
wallet = coinzdense.wallet.create_wallet(salt,
key,
owner_passphrase)
# save it to disk
print("Saving owner wallet")
with open(owner_wallet_path,"wb") as wfil:
wfil.write(bytes(wallet))
# Create a derived wallet with it's own passphrase
print("Deriving posting wallet")
subwallet = wallet["ACTIVE"]["POSTING"].create_wallet(posting_passphrase)
# Write it to disk
print("Saving posting wallet")
with open(posting_wallet_path,"wb") as wfil:
wfil.write(bytes(subwallet))
Note that the wallet needs a key to start out with. It won't make one up by itself. There are multiple reasons for this but a major one is the desire to port to Monte in the future. An object capability language, and we really want to keep our library code free from ambient authority, for example the authority to exhaust the system's entropy sources.
But what is the interesting bit about the API? Well, it's this bit:
subwallet = wallet["ACTIVE"]["POSTING"].create_wallet(posting_passphrase)
The coinZdense wallet allows for deterministic derivation of sub-key wallets using the square bracket operator. I hope this feature will prove valuable if in the future when the project is more mature and supports more languages, we get to the point where it is a viable foundation for moving HIVE towards the post-quantum erra.
So now that we have the wallet, we can create a signing key and use it:
# Use the open wallet to make ourselves a 3-level signing-key
key = coinzdense.signing.SigningKey(hashlen=24, otsbits=6, heights=[2, 3, 4], wallet=subwallet)
# Mah
sig = key.sign_string(message)
There is a bit more to the signing API than this, what I will post about later, but as far as the wallet API is concerned, this is about it.
Is this API OK like this?
I would be interested in hearing from e @emrebeyler and @holger80 (authors of two main Python libraries for HIVE) if they feel this API would need any other considerations that I haven't considered yet.
tippingjar and discord
Again, if you think I'm doing good work with this slow-moving unfunded project, consider upvoting my posts, paying a visit to my tippingjar page or joining me on discord.