12 parser = argparse.ArgumentParser("Encode and decode files as a mnemonic")
13 parser.add_argument("wordlist", type=str, help="The wordlist to use")
14 parser.add_argument("--decode", action="store_true")
16 "--input", type=str, help="The input file when encoding"
19 "--output", type=str, help="The file to write to when decoding"
22 "--length", type=int, help="Length in bytes of the decoded output"
24 return parser.parse_args()
27 def bits_to_int(bits):
28 """Convert passed bits into int
30 Least significant bit first
33 for i, bit in enumerate(bits):
39 def byte_to_bits(byte):
40 """Convert byte into bit array
42 Least significant bit first
44 return [byte >> i & 1 for i in range(8)]
47 def create_mnemonic(bites, words, bits_per_word=BITS_PER_WORD):
48 """Create mnemonic from bytes
50 Create mnemonic phrase from an input byte array. Each byte
51 is convert into a bit array (least significant bit first) and
52 all such bit arrays are concatenated in the order of the input
53 bytes. BITS_PER_WORD many bits are consumed from the beginning
54 of the array and converted into an integer (least significant
55 bit first) which is used as an index to look up a word in the
56 given wordlist. A list of so looked up words is returned.
58 If necessary, the concatenated bit array is padded with the
59 beginning bits of the sha256 hash of the input byte array
60 to get to the next multiple of the word size.
62 :param bites: The bytes to convert.
63 :param words: The word list to use, must have 2**n many words
64 :param bits_per_word: The number of bits to consume per word. The
65 word list should be 2**bits_per_word long
66 :retrun: Mnemonic phrase
68 digest = hashlib.sha256(bites).digest()
72 bits += byte_to_bits(b)
76 checksum_bits += byte_to_bits(b)
79 smallest_n_bits = math.floor((n_bits/BITS_PER_WORD) + 1) * BITS_PER_WORD
80 bits_missing = smallest_n_bits - n_bits
81 bits += checksum_bits[0:bits_missing]
84 for i in range(0, len(bits), 11):
85 word_int = bits_to_int(bits[i:i+11])
86 mnemonic.append(words[word_int])
91 def parse_mnemonic(mnemonic, words):
92 """Parse mnemonic into bytearray using wordlist
94 For each word in the mnemonic, find it's 0 indexed position
95 in the wordlist, convert the the position into a bit array
96 (lest significant bit first) and concatenate all such bit
97 arrays. Pad it with 7 * [0] to ensure the last bits fit into the
98 last byte. Convert the bit array into a byte array (least
99 significant bit first)
101 :param mnemonic: A list of words from the mnemonic
102 :param words: The (ordered) word list
103 :return: Decoded bytes
106 for word in mnemonic:
107 i = words.index(word)
108 bits += [i >> j & 1 for j in range(11)]
111 # Add padding bits to ensure the last chunck has 8 bits
114 for i in range(0, n_bits, 8):
115 bites.append(bits_to_int(bits[i:i+8]))
117 return bytearray(bites)
120 def run(word_file, encode, in_file, out_file, length):
121 with open(word_file, "r") as wordlist:
122 words = [word.strip() for word in wordlist.readlines() if word.strip()]
125 with open(in_file, "rb") as in_file:
126 bites = bytearray(in_file.read())
127 mnemonic = create_mnemonic(bites, words)
128 print("\n".join(mnemonic))
132 for line in sys.stdin.readlines():
133 mnemonic += line.split()
135 bites = parse_mnemonic(mnemonic, words)
136 with open(out_file, "wb") as out_file:
137 out_file.write(bites[:length])
140 if __name__ == "__main__":
147 args.length if args.decode else None,