Intro | Partial one-time-signature chains |
Application RC
The coinZdense project is parametrized. If, in the future, when coinZdense reaches Minimal Viable Product (MVP) status, a blockchain or crypto product will need to parametrize its usage of coinZdense. The idea for these parameterizations is that each blockchain gets its own RC file. This RC file, in first design a JSON file, but it's expected in future versions to by a YAML file, contains the parameterization for an owner key, and next to that, for attenuated-privilege keys.
On a Linux or Unix system, the RC files will be located and searched for in the following places:
- ${HOME}/.coinzdense/etc/coinzdense.d/
- /etc/coinzdense.d/
- ${PWD}/etc/coinzdense.d/
These are in order of priority. So if the file ${HOME}/.coinzdense/etc/coinzdense.d/hive.json and /etc/coinzdense.d/hive.json both exist, the later will be ignored.
So what is in these RC files? Let's start with the simplest of example, a blockchain without any for of key privilege attenuation.
{
"appname": "SILLYCHAIN",
"hashlen": 32,
"otsbits": 10,
"heights" : [14, 12]
}
In the above RC file we define our blockchain as "SILLYCHAIN".
We define that the hash length used as primitive throughout the hash-based-signatures operations is 32 bytes or 256 bits long. We define that atomic one-time signatures always sign 10 bits at a time, and finally we define the merkle-tree height for three different levels inside our signing keys to respectively be 14 and 12.
It is important to realize what these parameters really mean. Larger hash lengths means longer signatures and longer signing and key-creation times, but stronger security at a reasonable price because the increases in signature size and signing/key-creation times are close to linear.
The otsbits parameters is quite something else. Larger values mean shorter signatures, but longer signing times and key-creation times are exponential.
With our hashlen of 32 bytes or 256 bits and our otsbits value of 10, we will have to sign 26 groups of 10 bits to create a signature, and we will have to do roughly 2^10 or 1024 hashing operations per chunk signature, coming to a total of 26,624 hashing operations for a single signature. Now look what happens if we halve the hash size:
{
"appname": "SILLYCHAIN",
"hashlen": 16,
"otsbits": 10,
"heights" : [14, 12]
}
Now we have 128 bits per signature to sign, that is 13 chunks, chunks that still take up 1024 hash operation each, so now half, 13,312 are needed for a single signature while our signature size grows roughly by a factor of two.
But now look what happens if we halve our otsbits value instead:
{
"appname": "SILLYCHAIN",
"hashlen": 32,
"otsbits": 5,
"heights" : [14, 12]
}
Again our signature size doubles. Our 256 bit message digest now requires 52 chunks to be signed. But each chunk now only requires 2^5 or 32 hash operations, adding up to only 1,664 hashing operations per signature. That means that while our signature size went up 2x, our signing time went down 16x instead of 2x.
Now for the last parameters, the heights fields. We have two level keys to every signing key. A main level key with a merkle-tree height of 14 and a transaction signing level key with a merkle tree height of 12. Now key-creation comes in big time. A merkle height of 14 and 12 respectively means that roughly 40960 times the signing operation hashing operations will need to be performed to create the public key, then every 4096 signatures, an extra 8192 times the signing operation hashing operations will need to be performed to replace the transaction signing key with a new one. So if a single signature takes up just 5 msec, one in 4096 signatures will take up 41 seconds, and creation of the initial key takes up three and a half minutes. Again we can change this making some compromises. We have a signing key able to sign 67 million transactions. Let's add an extra layer to our level keys:
{
"appname": "SILLYCHAIN",
"hashlen": 32,
"otsbits": 5,
"heights" : [9, 9, 8]
}
Now key creation will only take up 13 seconds. One in 256 transactions will take an extra 2.6 seconds for the creation of a new lowest level key, and one in 131,072 an extra 7.7 seconds to create a new intermediate level key AND a new low level key.
Note that while in the MVP version, these times will actually reflect on signing times, it should potentially be possible for coinZdense to calculate replacement level-keys ahead of time in the background. We are yet to determine however if this would be a good idea from an API perspective.
So far there has been already quite a bit of information just for our simple RC file. But now things are going to get even more complex as we introduce the possibility of attenuated-privilege signing keys.
Let's make think a little HIVE-ish by adding the possibility for ACTIVE and POSTING keys:
{
"appname": "HIVEISH",
"hashlen": 32,
"otsbits": 5,
"heights" : [9, 9, 8]
"reserved": 12,
"hierarchy": {
"ACTIVE": {
"POSTING": {
"comment": {}
}
},
"recover_account": {}
},
"delegate_space": [
{
"name": "active",
"allocate": 38,
"reserved" : 5,
},
{
"name": "posting"
},
{
"name": rare",
"heights": [6, 6]
}
]
}
The first new field we see here is reserved. Look at reserved as a part of the signing space that is reserved specifically for creating attenuated privileges keys. In our example here, we reserve a 12 bit space (4096 signatures) for the creation of attenuated privilege keys.
The next section we look at is the hierarchy section. Here we first have the concept of an ACTIVE key and a posting key, but we can extend on that. As an example we add two operations to the hierarchy. comment and recover_account. The idea is that we can create an attenuated privilege key that can only be used for those two operations. We will discuss this subject more extensively in later posts on the subject.
The final secion of our RC file is about what the actual keys can look like that are created. In our example here, we define three delegate spaces for key creation. Each entry has a name (for the API) and a number (for the signatures and wallet) , and each has a heights field, either explicityl or implicitly. Without a heights field, the heights is inherited from the top level. If a sub-key should be able to derive sub-sub-keys from itself, than a reserved field will be present. Next to a reserved field, an allocate field is required. The allocate field is used to allocate a chunk of the index space. Note that a total of 64 bits of index space is available. So in this case, every single active key that is derived from the owner key will eat up 2^42 values from the index space in order to be able to do sub derivations.
We will come back to this later, but it is important to realize how much of the available key-space a signing key will take up by itself, separate from any allocate definition. This knowledge will also help determine proper values for allocate and reserved as well.
- A single OTS chunk takes up two values from the index space
- To determine the amount of OTS chunks a single OTS key needs, divide 8 times the hashlen by otsbits and round up. So in our example, 32 * 8 / 5 = 51.2, rounded up is 52 chunks or 104 index-space values.
- To determine the amount of index-space a level key needs, take one plus the amount of OTS chunks a single OTS key times two to the power of the height. So for our [9, 9, 8] heights, that would boil down to 1 + 104 * 2^9 or 53249 for the top two levels and 26625 for the deepest level
- To determine the amount of level-key incarnations per level, take one for the top level key and two to the power of the sum of all the height values of level keys higher than the current one. So in our example, we have 153,249, 51253,249 and 262,144*26,625, adding up to 7,006,900,737 values.
- To determine the amount of values required for a signing key, take the amount needed for all level keys possible combined and add two to the power of the sum of all heights.
- If we take the logarithm of this number and divide it by the logarithm of two and round it up, we get the index space required in bits. In our case, 33. If we add our the 33 bits required for a posting key to the reserved value of five, we end up with the value for allocate of 38 bits.
Thus far the deep-dive into the parameterization of coinZdense with RC files. We'll soon add additional posts to this deep-dive. I hope you are still with me on everything here. If not, a lot of the concepts discussed in this post will come back later on. Oh, and remember, lots of the things discussed here are still partially fluid. For one, we expect to be moving from JSON to YAML for the RC file format soon. This post will get updated when we do.
Intro | Partial one-time-signature chains |
|