def mnemonic_from_entropy(entropy: Entropy, version_str: str = "standard", lang: str = "en") -> Mnemonic: """Convert input entropy to Electrum versioned mnemonic sentence. Input entropy can be expressed as binary 0/1 string, bytes-like, or integer. In the case of binary 0/1 string and bytes-like, leading zeros are considered redundant padding. """ if version_str not in _MNEMONIC_VERSIONS: err_msg = f"unknown electrum mnemonic version: '{version_str}'; " err_msg += f"not in {list(_MNEMONIC_VERSIONS.keys())}" raise BTClibValueError(err_msg) version = _MNEMONIC_VERSIONS[version_str] bin_str_entropy = bin_str_entropy_from_entropy(entropy) int_entropy = int(bin_str_entropy, 2) base = WORDLISTS.language_length(lang) while True: # electrum considers entropy as integer, losing any leading zero # so the value of bin_str_entropy before the while must be updated nbits = int_entropy.bit_length() bin_str_entropy = bin_str_entropy_from_entropy(int_entropy, nbits) indexes = wordlist_indexes_from_bin_str_entropy(bin_str_entropy, base) mnemonic = mnemonic_from_indexes(indexes, lang) # version validity check s = hmac.new(b"Seed version", mnemonic.encode(), sha512).hexdigest() if s.startswith(version): return mnemonic # next trial int_entropy += 1
def mnemonic_from_entropy(entropy: Optional[Entropy] = None, lang: str = "en") -> Mnemonic: """Convert input entropy to BIP39 checksummed mnemonic sentence. Input entropy can be expressed as binary 0/1 string, bytes-like, or integer; it must be 128, 160, 192, 224, or 256 bits. In the case of binary 0/1 string and bytes-like, leading zeros are not considered redundant padding. In the case of integer, where leading zeros cannot be represented, if the bit length is not an allowed value, then the binary 0/1 string is padded with leading zeros up to the next allowed bit length; if the integer bit length is longer than the maximum length, then only the leftmost bits are retained. """ if entropy is None or entropy == "": entropy = secrets.randbits(128) bin_str_entropy, checksum = _entropy_checksum(entropy) base = WORDLISTS.language_length(lang) indexes = wordlist_indexes_from_bin_str_entropy(bin_str_entropy + checksum, base) return mnemonic_from_indexes(indexes, lang)
def test_indexes() -> None: for entropy in ("0", "00000000000"): indexes = wordlist_indexes_from_bin_str_entropy(entropy, 2048) assert indexes == [0] entropy = "000000000000" indexes = wordlist_indexes_from_bin_str_entropy(entropy, 2048) assert indexes == [0, 0] test_vector = [ [1268, 535, 810, 685, 433, 811, 1385, 1790, 421, 570, 567, 1313], [0, 0, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 0], [0, 0, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 0], ] for indx in test_vector: entropy = bin_str_entropy_from_wordlist_indexes(indx, 2048) indexes = wordlist_indexes_from_bin_str_entropy(entropy, 2048) assert indexes == indx