LedgerHQ / ledger-nano-s

Ledger Nano S, a personal security device from Ledger (blockchain / bitcoin / ethereum / FIDO)
Apache License 2.0
278 stars 41 forks source link

Proposal for Reversible (Palindromic) Mnemonics #52

Open hatgit opened 5 years ago

hatgit commented 5 years ago

I've developed a method to check if a mnemonic is also valid when the words are put into reverse order (not the entropy), where a given 12 or 24-word mnemonic could be valid both in little endian and big endian format. I've coined these Palindromic Mnemonics, but perhaps more user-friendly is "reversible mnemonics."

Purpose: A checksum-valid reversible mnemonic allows two separate vaults to be connected to the same string of words, where all a users must do is enter the words in reverse order (the last word becomes first, second to last becomes second, and so on) to access the secondary (reversed words) vault. This utility could provide multiple use cases, including related to combinations with passphrases and plausible deniability, as well as conveniences for those wishing to use a separate vault tied to the same string of words.

Security For any randomly generated 12-word mnemonic (128-bits of security) the chances of it also being reversible are 1/16, as a total of 4 bit positions must be identical (4 bits from the normal mnemonic, and another 4 bits from the reversed string). For 24-word mnemonics those values increase to 8 bits that need to match 8 bits from the reversed, leading to about 1 in every 256 mnemonics also being reversible. While the message space of valid reversible mnemonics should be 2^124 for 12 words, that search must still be conducted over a field of 2^128, as the hash-derived checksum values otherwise prevent a way to deterministically find valid reversible mnemonics without first going through invalid reversible ones to check. I think others should chime in on whether they believe there is any security loss, in terms of bits. I estimate at most it would be 4-bits for a 12-word mnemonic, but only if an attacker had a way to search only the space of valid reversible mnemonics (2^124). There could also be errors in my above assumptions, this is a work in progress and sharing it here to solicit feedback.

The following code can be used for testing, and run from terminal/command prompt it is pretty fast to find a valid reversible mnemonics, whereas on IDLE in Python on a 32-bit and 64-bit machine it could take a few seconds for 12 words and sometimes 10 minutes to find a valid 24-word reversible mnemonic: https://github.com/hatgit/BIP39_mnemonic_creation_light_python/blob/master/Palindromic_Mnemonic_experiment.py

Example randomly generated 12-word palindromic (reversible mnemonic):

Entropy length as hex without 0x pad: 32 Initial entropy as hex without pad: 39b2904bf37a267daf618fa03e5395a5 bytearray(b'9\xb2\x90K\xf3z&}\xafa\x8f\xa0>S\x95\xa5') <--- Entropy as bytes Length of initial entropy as bytearray: 16 8dde9837e6f66605b28edb38573b76fff3504d74d94cb605f20a8061c88ddb9a <--- SHA-256 hash digest of entropy bytes 8 <--- Partial fragment of initial "byte" of hash 8 <--- First n bits of hash to convert to hex 1000 <--- Checksum (hex to bits) Initial entropy + checksum = total bits: 001110011011001010010000010010111111001101111010001001100111110110101111011000011000111110100000001111100101001110010101101001011000 Length of total bits: 132 ['00111001101', '10010100100', '00010010111', '11100110111', '10100010011', '00111110110', '10111101100', '00110001111', '10100000001', '11110010100', '11100101011', '01001011000'] Optional backup hex: 0x39b2904bf37a267daf618fa03e5395a5 Optional backup hex with checksum: 0x39b2904bf37a267daf618fa03e5395a58 Hash digest of initial entropy bytes: 8dde9837e6f66605b28edb38573b76fff3504d74d94cb605f20a8061c88ddb9a [461, 1188, 151, 1847, 1299, 502, 1516, 399, 1281, 1940, 1835, 600] defy nest base tragic pen disagree rural cradle parent verb tornado enrich 1101 <--- Last 4 bits of the first word to compare with checksum of mnemonic reverse order (hex to bits) Palindromic mnemonic phase ['01001011000', '11100101011', '11110010100', '10100000001', '00110001111', '10111101100', '00111110110', '10100010011', '11100110111', '00010010111', '10010100100', '00111001101'] enrich tornado verb parent cradle rural disagree pen tragic base nest defy Words reverse order = total bits: 010010110001110010101111110010100101000000010011000111110111101100001111101101010001001111100110111000100101111001010010000111001101 Words reverse order without last 4 bits = total bits: 01001011000111001010111111001010010100000001001100011111011110110000111110110101000100111110011011100010010111100101001000011100 gcc: 32 Entropy length as hex without 0x pad: 32 Initial entropy as hex without pad: 4b1cafca50131f7b0fb513e6e25e521c bytearray(b'K\x1c\xaf\xcaP\x13\x1f{\x0f\xb5\x13\xe6\xe2^R\x1c') <--- Entropy as bytes Length of initial entropy as bytearray: 16 dbbaacbd2043322a3247e7569af740193eb0db03d79923b83fecbf0de0b00465 <--- SHA-256 hash digest of entropy bytes d <--- Partial fragment of initial "byte" of hash d <--- First n bits of hash to convert to hex 1101 <--- Checksum reverse order (hex to bits) 1101 is equal to 1101. It is a palindromic mnemonic

hatgit commented 5 years ago

Here is a working version of the above idea in javascript https://bcaventures.com/reversible-mnemonic.html also have a python version in on my repo. I wonder how fast this would run on the Nano? On Python IDLE it is slow, command line runs much faster. Any interest to add this as an advanced feature and one that a user could manually install on the hardware wallet?