+
+def create_mnemonic(bites, words, bits_per_word=BITS_PER_WORD):
+ """Create mnemonic from bytes
+
+ Create mnemonic phrase from an input byte array. Each byte
+ is convert into a bit array (least significant bit first) and
+ all such bit arrays are concatenated in the order of the input
+ bytes. BITS_PER_WORD many bits are consumed from the beginning
+ of the array and converted into an integer (least significant
+ bit first) which is used as an index to look up a word in the
+ given wordlist. A list of so looked up words is returned.
+
+ If necessary, the concatenated bit array is padded with the
+ beginning bits of the sha256 hash of the input byte array
+ to get to the next multiple of the word size.
+
+ :param bites: The bytes to convert.
+ :param words: The word list to use, must have 2**n many words
+ :param bits_per_word: The number of bits to consume per word. The
+ word list should be 2**bits_per_word long
+ :retrun: Mnemonic phrase
+ """
+ digest = hashlib.sha256(bites).digest()
+
+ bits = []
+ for b in bites:
+ bits += byte_to_bits(b)
+
+ checksum_bits = []
+ for b in digest:
+ checksum_bits += byte_to_bits(b)
+
+ n_bits = len(bits)
+ smallest_n_bits = math.floor((n_bits/BITS_PER_WORD) + 1) * BITS_PER_WORD
+ bits_missing = smallest_n_bits - n_bits
+ bits += checksum_bits[0:bits_missing]
+
+ mnemonic = []
+ for i in range(0, len(bits), 11):
+ word_int = bits_to_int(bits[i:i+11])
+ mnemonic.append(words[word_int])
+
+ return mnemonic
+
+
+def parse_mnemonic(mnemonic, words):
+ """Parse mnemonic into bytearray using wordlist
+
+ For each word in the mnemonic, find it's 0 indexed position
+ in the wordlist, convert the the position into a bit array
+ (lest significant bit first) and concatenate all such bit
+ arrays. Pad it with 7 * [0] to ensure the last bits fit into the
+ last byte. Convert the bit array into a byte array (least
+ significant bit first)
+
+ :param mnemonic: A list of words from the mnemonic
+ :param words: The (ordered) word list
+ :return: Decoded bytes
+ """
+ bits = []
+ for word in mnemonic:
+ i = words.index(word)
+ bits += [i >> j & 1 for j in range(11)]
+
+ n_bits = len(bits)
+ # Add padding bits to ensure the last chunck has 8 bits
+ bits += [0] * 7
+ bites = []
+ for i in range(0, n_bits, 8):
+ bites.append(bits_to_int(bits[i:i+8]))
+
+ return bytearray(bites)
+
+
+def run(word_file, encode, in_file, out_file, length):
+ with open(word_file, "r") as wordlist:
+ words = [word.strip() for word in wordlist.readlines() if word.strip()]
+
+ if encode:
+ with open(in_file, "rb") as in_file:
+ bites = bytearray(in_file.read())
+ mnemonic = create_mnemonic(bites, words)
+ print("\n".join(mnemonic))
+
+ else:
+ mnemonic = []
+ for line in sys.stdin.readlines():
+ mnemonic += line.split()
+
+ bites = parse_mnemonic(mnemonic, words)
+ with open(out_file, "wb") as out_file:
+ out_file.write(bites[:length])
+
+
+if __name__ == "__main__":
+ args = parse_args()
+ run(
+ args.wordlist,
+ not args.decode,
+ args.input,
+ args.output,
+ args.length if args.decode else None,
+ )