Skip to content

Account (re)generation and mnemonic seed

Joshua Thijssen edited this page Nov 16, 2020 · 7 revisions

Each account is accompanied by a private key and public key pair. The public part is used by others to communicate with the account, and the private part is used to decrypt any incoming communication, and the signing of different aspects, like updates on the key-resolver, etc.

Since losing the private key means the account cannot be used anymore, and no recovery is possible, we like to change the way the accounts are generated.

  • We generate a 192bit random number (e)
  • Then we add a 8bit number to e to specify the key type generated. This number will provide 127 different key types. We'll keep the last bit as an extension bit in case we need to add more key types in the future. This number is concatenated on each new key generation so we can regenerate all old keys (TODO)
  • Use PBKDF2 from the account address and extract a key and IV to encrypt e (TODO)
  • From this number (e), we generate a mnemonic seed that is easily readable and writable.

In order to (re)generate the keys, we do the following

  • We get the first 192 bit from e
  • This number is stretched from 192 to 256 bit through a key derivation function (hkdf).
  • We iterate over the next bytes of e to generate each key type (TODO)
  • The number is used as the seed to generate an ED25519 key pair.
  • This keypair is the account key pair.

The mnemonic seed is generated through BIP39 (we need to reimplement this, since e will grow overtime and BIP39 won't allow that. It can only do 128 to 256 bits), which uses English words to generate a random number. Such a mnemonic could be:

spy thank opera firm damp whale hybrid grow brand steak holiday cable

These words are easily written onto a piece of paper and stored offline.

In case the account is lost, all we need is the mnemonic and the account address to regenerate all the keys. This will result in the generation of the SAME keypairs.

We use this system to generate accounts, mail server routing id's, and organizations. The system is only capable to generate RSA and ed25519 (but not ecdsa) keys. Even though RSA and ecdsa can be generated deterministically, the current implementation of key generation in Go does not allow deterministic key generation. Therefore this system cannot be used for generating RSA keys and we rely on a custom implementation (from cloudflare).

Multi key accounts

The current implementation relies on the fact that there is only a single key. This is not the case, there can be multiple keys available. For instance, when sunsetting an old key and getting a (stronger) key as the primary key. However, the old key should be available since messages are encrypted with this key. We can have multi-key system, where we add the needed key types to the seed phrase. There are many ways to do this, but it will result in a larger number of bits (and thus words) of the phrase. The current version is very simplistic, but we can update the version to create different setups.

  • convert mnemonic phrase to the aes encrypted bitstring (AEB)
  • use a PBKDF function with bitmaelum address as password to generate aes bitstring (AB), use hash of the address as salt. Generate 1024 iterations
    • use first 32byte of (AB) for AES key
    • use next 32 bytes of (AB) for AES iv
  • Decrypt the AEB with the given AES key and iv into a seed bitstring (SB)
  • process the seed bitstring (SB)
    • first 4 bits is the version (0-15). There can be multiple versions in a future release
	+--------------------------------+
	|       mnemonic passphrase      |
	+--------------------------------+
	      |
		  | convert via BIP32
		  v
	+-----------------------------------------------------+
	|       AES encrypted bitstring (AEB)                 |
	+-----------------------------------------------------+
	

     +-------------------+       +----------------------------+
     | bitmaelum address |       | sha256(bitmaelum address)  |
     +-------------------+       +----------------------------+
         |                          |
         | password                 | salt
         |                          |
         |    +---------------------+
	 v    v
	 +-------------+      
	/ PBKDF(1024) /
	+------------+
	      |
	      | bitstring
	      v
	   +---------------------+----------------------+
	   |   AES key (32byte)  |   AES iv (32byte)    |
	   +---------------------+----------------------+
	              |                      |
	              |                      |
	              |                      |
	              +----------+-----------+
	                         |
	                         | decrypt AEB stream
	                         |
	                         v
	+-------------+---------------------------------------+
	| ver (4bits) | bitstream                             |
	+-------------+---------------------------------------+

Based on the version, do the following:

Version 1 (0000)

	+-------------+---------------------------------------+----------+----------+     +----------+
	| ver (4bits) | Entropy (192bits)                     | Keytype1 | KeyType2 | ... | KeyTypeN |
	+-------------+---------------------------------------+----------+----------+     +----------+
  • use the first 192 bits (after the version) as entropy / seed for generating keys (ENT)
  • for each next 4 bits:
    • get the key
      • use (ENT) as random seed
      • get N random numbers, where N is the key index (starting with index 0)
      • use this random number as the seed for the given key

Future releases can have higher version numbers. Decoding depends on the version number itself. When we reached version 8, the number of version bits will double to 8

+---+--------------+--------------+
| 1 | ver (3 bits) | ver (4 bits) |
+---+--------------+--------------+
Clone this wiki locally