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
示例#2
0
def test_conversions() -> None:

    test_vectors = [
        "10101011" * 32,
        "00101011" * 32,
        "00000000" + "10101011" * 31,
    ]

    for raw in test_vectors:
        assert bin_str_entropy_from_str(raw) == raw
        i = int(raw, 2)
        assert bin_str_entropy_from_int(i) == raw
        assert bin_str_entropy_from_int(bin(i).upper()) == raw
        assert bin_str_entropy_from_int(hex(i).upper()) == raw
        b = i.to_bytes(32, byteorder="big", signed=False)
        assert bin_str_entropy_from_bytes(b) == raw
        assert bin_str_entropy_from_bytes(b.hex()) == raw

        assert bin_str_entropy_from_entropy(raw) == raw
        assert bin_str_entropy_from_entropy(i) == raw
        assert bin_str_entropy_from_entropy(b) == raw

    max_bits = max(_bits)

    raw = "10" + "11111111" * (max_bits // 8)
    assert bin_str_entropy_from_entropy(raw) == bin_str_entropy_from_entropy(
        raw[:-2])

    # entr integer has its leftmost bit set to 0
    i = 1 << max_bits - 1
    bin_str_entropy = bin_str_entropy_from_entropy(i)
    assert len(bin_str_entropy) == max_bits

    # entr integer has its leftmost bit set to 1
    i = 1 << max_bits
    bin_str_entropy = bin_str_entropy_from_entropy(i)
    assert len(bin_str_entropy) == max_bits

    exp_i = i >> 1
    i = int(bin_str_entropy, 2)
    assert i == exp_i

    i = secrets.randbits(255)
    raw = bin_str_entropy_from_int(i)
    assert int(raw, 2) == i
    assert len(raw) == 256

    assert bin_str_entropy_from_str(raw) == raw
    assert bin_str_entropy_from_int(hex(i).upper()) == raw

    b = i.to_bytes(32, byteorder="big", signed=False)
    assert bin_str_entropy_from_bytes(b) == raw

    raw2 = bin_str_entropy_from_int(i, 255)
    assert int(raw2, 2) == i
    assert len(raw2) == 255
    assert bin_str_entropy_from_str("0" + raw2) == raw
    raw2 = bin_str_entropy_from_str(raw, 128)
    assert len(raw2) == 128
    assert raw2 == raw[:128]
示例#3
0
def _entropy_checksum(entropy: Entropy) -> Tuple[BinStr, BinStr]:
    """Return the checksum of the binary string input entropy.

    Entropy must be expressed as binary 0/1 string and
    must be 128, 160, 192, 224, or 256 bits.
    Leading zeros are considered genuine entropy, not redundant padding.
    """

    bin_str_entropy = bin_str_entropy_from_entropy(entropy)
    bytes_entropy = bytes_entropy_from_str(bin_str_entropy)

    # 256-bit checksum
    bytes_checksum = sha256(bytes_entropy).digest()
    # integer checksum (leading zeros are lost)
    int_checksum = int.from_bytes(bytes_checksum,
                                  byteorder="big",
                                  signed=False)
    # convert checksum to binary '01' string
    checksum = bin(int_checksum)[2:]  # remove '0b'
    checksum = checksum.zfill(256)  # pad with leading lost zeros
    # leftmost bits
    checksum_bits = len(bytes_entropy) // 4
    return bin_str_entropy, checksum[:checksum_bits]
示例#4
0
def test_exceptions() -> None:
    bin_str_entropy216 = "00011010" * 27  # 216 bits
    bin_str_entropy214 = bin_str_entropy216[:-2]  # 214 bits

    entropy = bin_str_entropy_from_entropy(bin_str_entropy214, 214)
    assert entropy == bin_str_entropy214

    # 214 is not in [128, 160, 192, 224, 256, 512]
    err_msg = "invalid number of bits: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bin_str_entropy_from_entropy(bin_str_entropy214)

    # 214 is not in [216]
    err_msg = "invalid number of bits: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bin_str_entropy_from_entropy(bin_str_entropy214, 216)

    int_entropy211 = int(bin_str_entropy214, 2)  # 211 bits
    assert int_entropy211.bit_length() == 211

    entropy = bin_str_entropy_from_entropy(int_entropy211, 214)
    assert entropy == bin_str_entropy214

    entropy = bin_str_entropy_from_entropy(int_entropy211, 256)
    assert len(entropy) == 256
    assert int(entropy, 2) == int_entropy211

    entropy = bin_str_entropy_from_entropy(int_entropy211)
    assert len(entropy) == 224
    assert int(entropy, 2) == int_entropy211

    err_msg = "Negative entropy: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bin_str_entropy_from_entropy(-1 * int_entropy211)

    bytes_entropy216 = int_entropy211.to_bytes(27,
                                               byteorder="big",
                                               signed=False)
    entropy = bin_str_entropy_from_entropy(bytes_entropy216, 214)
    assert entropy == bin_str_entropy214

    entropy = bin_str_entropy_from_entropy(bytes_entropy216, 216)
    assert entropy != bin_str_entropy216

    err_msg = "invalid number of bits: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bin_str_entropy_from_entropy(bytes_entropy216, 224)

    with pytest.raises(BTClibValueError, match=err_msg):
        bin_str_entropy_from_entropy(tuple())  # type: ignore

    with pytest.raises(ValueError):
        bin_str_entropy_from_int("not an int")  # type: ignore

    with pytest.raises(TypeError):
        bin_str_entropy_from_str(3)  # type: ignore

    err_msg = "invalid number of bits: "
    with pytest.raises(BTClibValueError, match=err_msg):
        bin_str_entropy = "01" * 65  # 130 bits
        bytes_entropy_from_str(bin_str_entropy)