coinZdense deep-dive: Level-Keys

One-time signatures
index
KDF-index-space allocation

Level-keys



In the previous post, we discussed one-time signing keys and one-time signatures. In this post we are going to look into how to combine a collection of one-time signing keys, each derived from the same master key, into a multi-use level-key. A level key, in escence is nothing more than a so called Merkle tree of one-time signing keys. In the image below, we have a level key with a height parameter of four, giving us a level key that could sign up to 2^4 = 16 transactions (or other level keys one level down).

In order to create a usable level key, we need the OTS pubkey for each of the signing operations we hope to do in the future. As we know, depending on the otsbits parameter, OTS-Pubkey calculation can be CPU intensive, so we need to choose height carefully, as we did with otsbits, given that key generation might end up taking quite a lot of time, and non-top-level level keys may need to be re-generated during operations where a multi-minute long delay in signing may not be acceptable. Remember, coinZdense provides parameterized hash based signatures, so it's up to the user, the blochchain project setting out to use coinZdense, to find the most apropriate values for all the parameters involved.

mtpubkey.png

In the image above we see a depiction of the calculation of the lpubkey or level-key pubkey. Our collection of OTS-pubkeys is ordered into pairs that are concatenated and hashed using the level-key salt. The results, a collection of merkle tree nodes, is half the size of the input. This process is repeated untill only one digest remains. That digest is our lpubkey.

mtsig.png

So how is this merkle tree, and importantly the lpubkey used in signing. Well one thing we need to realize is that because all the inpur pubkeys are one-time use, each signing uses up a key and moves an index forward. Lets say we already used up the first six (0..5) OTS keys and now want to use the 7th (OTS-pubkey6). Basically what we do is we take our one time signature and we prefix it with a merkle header structure. This merkle header structure should contain all the yellow nodes depicted above, as those nodes are what is needed to validate the signature.

Now if the receiver wants to validate the signature, she calculates the value of OTS-pubkey6, then uses the header-included values OTS-pubkey7, mn010, mn00 and mn1 to calculate lpubkey. If the value calculated is the same as the actual value of lpubkey, the signature is valid. If not, it isn't.

level-signature.png

Here is a pseudo structure for the actual signature. We have lpubkey that designates what level key has created the signature. The lksalt or levelkey salt is the hashing salt that has been used throughout the key creation and signing process. The transaction salt, if this is a transaction signature, contains the salt used while hashing the transaction. The next field is the index of the one time signing key used. After that we see the merkle header consisting of the merkle tree nodes needed to re-calculate the pubkey, and we end with the actual one-time signature consisting of up/down pairs.

Note that this is just a rough draft of a level-key signature, not exactly the line protocol for serialization. We will come to that when discussing signing-key signatures, realizing a signing key consists of multiple level keys, and might also contain an attenuation signature chain. More on that later.

In the next post we are going to be having a first look at the multi-level nature of the coinZdense signing key when we dive into index-space allocation and the use of the libsodium key derivation functions.

One-time signatures
index
KDF-index-space allocation

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now
Logo
Center