From 05da4603fb8155f89fc24dcebf880938a0394e85 Mon Sep 17 00:00:00 2001 From: Determinant Date: Sun, 16 Aug 2020 02:31:31 -0400 Subject: freeze the dependencies --- freezed_deps/mnemonic/mnemonic.py | 291 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 freezed_deps/mnemonic/mnemonic.py (limited to 'freezed_deps/mnemonic/mnemonic.py') diff --git a/freezed_deps/mnemonic/mnemonic.py b/freezed_deps/mnemonic/mnemonic.py new file mode 100644 index 0000000..935620a --- /dev/null +++ b/freezed_deps/mnemonic/mnemonic.py @@ -0,0 +1,291 @@ +# +# Copyright (c) 2013 Pavol Rusnak +# Copyright (c) 2017 mruddy +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import binascii +import bisect +import hashlib +import hmac +import itertools +import os +import sys +import unicodedata + +PBKDF2_ROUNDS = 2048 + + +class ConfigurationError(Exception): + pass + + +# From +def binary_search(a, x, lo=0, hi=None): # can't use a to specify default for hi + hi = hi if hi is not None else len(a) # hi defaults to len(a) + pos = bisect.bisect_left(a, x, lo, hi) # find insertion position + return pos if pos != hi and a[pos] == x else -1 # don't walk off the end + + +# Refactored code segments from +def b58encode(v): + alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + + p, acc = 1, 0 + for c in reversed(v): + if sys.version < "3": + c = ord(c) + acc += p * c + p = p << 8 + + string = "" + while acc: + acc, idx = divmod(acc, 58) + string = alphabet[idx : idx + 1] + string + return string + + +class Mnemonic(object): + def __init__(self, language): + self.radix = 2048 + if sys.version < "3": + with open("%s/%s.txt" % (self._get_directory(), language), "r") as f: + self.wordlist = [w.strip().decode("utf8") for w in f.readlines()] + else: + with open( + "%s/%s.txt" % (self._get_directory(), language), "r", encoding="utf-8" + ) as f: + self.wordlist = [w.strip() for w in f.readlines()] + if len(self.wordlist) != self.radix: + raise ConfigurationError( + "Wordlist should contain %d words, but it contains %d words." + % (self.radix, len(self.wordlist)) + ) + + @classmethod + def _get_directory(cls): + return os.path.join(os.path.dirname(__file__), "wordlist") + + @classmethod + def list_languages(cls): + return [ + f.split(".")[0] + for f in os.listdir(cls._get_directory()) + if f.endswith(".txt") + ] + + @classmethod + def normalize_string(cls, txt): + if isinstance(txt, str if sys.version < "3" else bytes): + utxt = txt.decode("utf8") + elif isinstance(txt, unicode if sys.version < "3" else str): # noqa: F821 + utxt = txt + else: + raise TypeError("String value expected") + + return unicodedata.normalize("NFKD", utxt) + + @classmethod + def detect_language(cls, code): + code = cls.normalize_string(code) + first = code.split(" ")[0] + languages = cls.list_languages() + + for lang in languages: + mnemo = cls(lang) + if first in mnemo.wordlist: + return lang + + raise ConfigurationError("Language not detected") + + def generate(self, strength=128): + if strength not in [128, 160, 192, 224, 256]: + raise ValueError( + "Strength should be one of the following [128, 160, 192, 224, 256], but it is not (%d)." + % strength + ) + return self.to_mnemonic(os.urandom(strength // 8)) + + # Adapted from + def to_entropy(self, words): + if not isinstance(words, list): + words = words.split(" ") + if len(words) not in [12, 15, 18, 21, 24]: + raise ValueError( + "Number of words must be one of the following: [12, 15, 18, 21, 24], but it is not (%d)." + % len(words) + ) + # Look up all the words in the list and construct the + # concatenation of the original entropy and the checksum. + concatLenBits = len(words) * 11 + concatBits = [False] * concatLenBits + wordindex = 0 + if self.detect_language(" ".join(words)) == "english": + use_binary_search = True + else: + use_binary_search = False + for word in words: + # Find the words index in the wordlist + ndx = ( + binary_search(self.wordlist, word) + if use_binary_search + else self.wordlist.index(word) + ) + if ndx < 0: + raise LookupError('Unable to find "%s" in word list.' % word) + # Set the next 11 bits to the value of the index. + for ii in range(11): + concatBits[(wordindex * 11) + ii] = (ndx & (1 << (10 - ii))) != 0 + wordindex += 1 + checksumLengthBits = concatLenBits // 33 + entropyLengthBits = concatLenBits - checksumLengthBits + # Extract original entropy as bytes. + entropy = bytearray(entropyLengthBits // 8) + for ii in range(len(entropy)): + for jj in range(8): + if concatBits[(ii * 8) + jj]: + entropy[ii] |= 1 << (7 - jj) + # Take the digest of the entropy. + hashBytes = hashlib.sha256(entropy).digest() + if sys.version < "3": + hashBits = list( + itertools.chain.from_iterable( + ( + [ord(c) & (1 << (7 - i)) != 0 for i in range(8)] + for c in hashBytes + ) + ) + ) + else: + hashBits = list( + itertools.chain.from_iterable( + ([c & (1 << (7 - i)) != 0 for i in range(8)] for c in hashBytes) + ) + ) + # Check all the checksum bits. + for i in range(checksumLengthBits): + if concatBits[entropyLengthBits + i] != hashBits[i]: + raise ValueError("Failed checksum.") + return entropy + + def to_mnemonic(self, data): + if len(data) not in [16, 20, 24, 28, 32]: + raise ValueError( + "Data length should be one of the following: [16, 20, 24, 28, 32], but it is not (%d)." + % len(data) + ) + h = hashlib.sha256(data).hexdigest() + b = ( + bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8) + + bin(int(h, 16))[2:].zfill(256)[: len(data) * 8 // 32] + ) + result = [] + for i in range(len(b) // 11): + idx = int(b[i * 11 : (i + 1) * 11], 2) + result.append(self.wordlist[idx]) + if ( + self.detect_language(" ".join(result)) == "japanese" + ): # Japanese must be joined by ideographic space. + result_phrase = u"\u3000".join(result) + else: + result_phrase = " ".join(result) + return result_phrase + + def check(self, mnemonic): + mnemonic = self.normalize_string(mnemonic).split(" ") + # list of valid mnemonic lengths + if len(mnemonic) not in [12, 15, 18, 21, 24]: + return False + try: + idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic) + b = "".join(idx) + except ValueError: + return False + l = len(b) # noqa: E741 + d = b[: l // 33 * 32] + h = b[-l // 33 :] + nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip("L").zfill(l // 33 * 8)) + nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[: l // 33] + return h == nh + + def expand_word(self, prefix): + if prefix in self.wordlist: + return prefix + else: + matches = [word for word in self.wordlist if word.startswith(prefix)] + if len(matches) == 1: # matched exactly one word in the wordlist + return matches[0] + else: + # exact match not found. + # this is not a validation routine, just return the input + return prefix + + def expand(self, mnemonic): + return " ".join(map(self.expand_word, mnemonic.split(" "))) + + @classmethod + def to_seed(cls, mnemonic, passphrase=""): + mnemonic = cls.normalize_string(mnemonic) + passphrase = cls.normalize_string(passphrase) + passphrase = "mnemonic" + passphrase + mnemonic = mnemonic.encode("utf-8") + passphrase = passphrase.encode("utf-8") + stretched = hashlib.pbkdf2_hmac("sha512", mnemonic, passphrase, PBKDF2_ROUNDS) + return stretched[:64] + + @classmethod + def to_hd_master_key(cls, seed): + if len(seed) != 64: + raise ValueError("Provided seed should have length of 64") + + # Compute HMAC-SHA512 of seed + seed = hmac.new(b"Bitcoin seed", seed, digestmod=hashlib.sha512).digest() + + # Serialization format can be found at: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Serialization_format + xprv = b"\x04\x88\xad\xe4" # Version for private mainnet + xprv += b"\x00" * 9 # Depth, parent fingerprint, and child number + xprv += seed[32:] # Chain code + xprv += b"\x00" + seed[:32] # Master key + + # Double hash using SHA256 + hashed_xprv = hashlib.sha256(xprv).digest() + hashed_xprv = hashlib.sha256(hashed_xprv).digest() + + # Append 4 bytes of checksum + xprv += hashed_xprv[:4] + + # Return base58 + return b58encode(xprv) + + +def main(): + import binascii + import sys + + if len(sys.argv) > 1: + data = sys.argv[1] + else: + data = sys.stdin.readline().strip() + data = binascii.unhexlify(data) + m = Mnemonic("english") + print(m.to_mnemonic(data)) + + +if __name__ == "__main__": + main() -- cgit v1.2.3