Lisk’s key generation system uses a 12 word long secret as a seed for generating a key pair, which is later used for signing. Lisk refers to this secret as “passphrase”, but I won’t and I’ll tell you why. For now we call it a “secret”.

An example of a secret is “quantum cattle festival oil force south size sniff labor surround either shove”. On the first glance it looks like a super long mega strong password. But there is structure in it: exactly 12 words, separated by spaces, no spacial characters. And even the choice of words is not endless but limited to one of 2048 words from a fixed word list. This structure is called BIP39.

What is BIP39?

BIP39 is a standard defined within the Bitcoin community that makes it possible to represent computer-generated randomness as words, and the motivation is that “the sentence could be written on paper or spoken over the telephone.”. The key point is that BIP39 is only one possible representation of a random array of bytes.

The following formats all represent the exact same 16 random bytes:

28, 199, 39, 203, 143, 198, 155, 205, 88, 45, 96, 38, 102, 122, 179, 136 (unsigned char list)

1cc727cb8fc69bcd582d6026667ab388 (hex)

HMcny4/Gm81YLWAmZnqziA== (Base64)

9lfCIKh^Wlss)]cw{Q!t (Base85)

broom decrease very buzz have trade gaze project chapter critic provide baby (Bip39)

Any of those five formats has its advantages and disadvantages but what they have in common is they represent the exact same data.

What is in that data represented as BIP39?

As we saw before, the 12 word BIP39 representation can be as compact as 20 printable characters in Base85. But what BIP39 contains depends on what we put into it. BIP39 supports multiple data lengths:

| Data length | Words in BIP39 mnemonic |

|-------------|-------------------------|

| 128 bit | 12 |

| 160 bit | 15 |

| 192 bit | 18 |

| 224 bit | 21 |

| 256 bit | 24 |

Lisk uses 128 bit (16 bytes) of data, which leads to the 12 word representation we know and love from our wallet logins. Those 128 bit are randomly generated during the account creation process.

Why this secret is not a “passphrase”

A passphrase is usually a collection of words or a sentence used as a secret. It can either be generated by a human or by a computer. While especially humans are not so good at generating passphrases without structure, the overall idea is to make it as hard to break as possible. If you have 12 words for a passphrase, you would pump as much entropy into it as possible. You could chose 20,000 simple non-offensive words from the ~400,000 words you find in an English wordlist making it 20,000¹² possible passphrases, which is about 170 bit entropy — using the same space on paper.

Why Lisk is abusing BIP39

When I say “abuse” I am referring to using a tool for a task it was not made for. You could also say “hack” or most positively “creatively evolve” depending on your point of view.

We saw that BIP39 is a representation of binary data but Lisk uses BIP39 as a (not so) random string generator:

The Lisk network does not care how you come to state 5. If you have a custom wallet software and don’t need to be compatible with Lisk Hub, you can generate your keypair directly and derive the Lisk address from there. Lisk-JS on the other hand, does not care where you get your string secret from. So you could start at state 2 by using Lisk-JS with a custom user interface that generates secret strings entirely different. Again, incompatible to Lisk Hub. However, we assume people use the official wallet and start in state 1.

If you have a super strong secret in state 3 you are good to go: SHA-256 will normalize the length to 32 bytes and ensure you cannot go back from 5 to 3. But how strong is the secret that Lisk Hub puts into state 3? It exactly 128 bit of entropy. State 1 to 3 are fully interchangeable. You can always go back and forth using close to no computation power.

Final secret (left) and key generator states (right)

Converting back and forth can be simulated using node and the library bip39:

> const bip39 = require('bip39');

> bip39.entropyToMnemonic('f4b2e507280beecc0ce4f96d18fe4b4b')

'visa now dove exotic same gravity crime palace home side name oak'

> bip39.mnemonicToEntropy('visa now dove exotic same gravity crime palace home side name oak')

'f4b2e507280beecc0ce4f96d18fe4b4b'

What’s the point? Can this be abused?

Our Lisk Hub passphrase contains exactly 128 bit of entropy but Lisk creates the impression of a 256 bit security level. This is not stated explicitly, but the security documentation as well as the blog article write about a 256 bit seed coming from a magic passphrase, which leaves out important numbers in the earlier steps of the key generation. So this is at the core a communication issue.

I don’t think this has practical implication or can be abused because 128 bit should be still hard enough to brute force. However, creating a secret to address rainbow table seems to be much more promising with 128 bit secrets than with 256 bit secrets. Why not implement this a Lisk sidechain project ;)