def __init__(
        self,
        non_witness_utxo: Optional[Tx] = None,
        witness_utxo: Optional[TxOut] = None,
        partial_sigs: Optional[Mapping[Octets, Octets]] = None,
        sig_hash_type: Optional[int] = None,
        redeem_script: Octets = b"",
        witness_script: Octets = b"",
        hd_key_paths: Optional[Mapping[Octets, BIP32KeyOrigin]] = None,
        final_script_sig: Octets = b"",
        final_script_witness: Witness = Witness(),
        unknown: Optional[Mapping[Octets, Octets]] = None,
        check_validity: bool = True,
    ) -> None:

        self.non_witness_utxo = non_witness_utxo
        self.witness_utxo = witness_utxo
        # https://docs.python.org/3/tutorial/controlflow.html#default-argument-values
        self.partial_sigs = (decode_dict_bytes_bytes(partial_sigs)
                             if partial_sigs else {})
        self.sig_hash_type = sig_hash_type
        self.redeem_script = bytes_from_octets(redeem_script)
        self.witness_script = bytes_from_octets(witness_script)
        self.hd_key_paths = decode_hd_key_paths(hd_key_paths)
        self.final_script_sig = bytes_from_octets(final_script_sig)
        self.final_script_witness = final_script_witness
        self.unknown = dict(sorted(decode_dict_bytes_bytes(unknown).items()))

        if check_validity:
            self.assert_valid()
Esempio n. 2
0
def check_output_pubkey(q: Octets,
                        script: Octets,
                        control: Octets,
                        ec: Curve = secp256k1) -> bool:
    q = bytes_from_octets(q)
    script = bytes_from_octets(script)
    control = bytes_from_octets(control)
    if len(control) > 4129:  # 33 + 32 * 128
        raise BTClibValueError("Control block too long")
    m = (len(control) - 33) // 32
    if len(control) != 33 + 32 * m:
        raise BTClibValueError("Invalid control block length")
    leaf_version = control[0] & 0xFE
    preimage = leaf_version.to_bytes(1, "big") + var_bytes.serialize(script)
    k = tagged_hash(b"TapLeaf", preimage)
    for j in range(m):
        e = control[33 + 32 * j:65 + 32 * j]
        if k < e:
            k = tagged_hash(b"TapBranch", k + e)
        else:
            k = tagged_hash(b"TapBranch", e + k)
    p_bytes = control[1:33]
    t_bytes = tagged_hash(b"TapTweak", p_bytes + k)
    p = int.from_bytes(p_bytes, "big")
    t = int.from_bytes(t_bytes, "big")
    # edge case that cannot be reproduced in the test suite
    if t >= ec.n:
        raise BTClibValueError("Invalid script tree hash")  # pragma: no cover
    P = (p, secp256k1.y_even(p))
    Q = secp256k1.add(P, mult(t))
    return Q[0] == int.from_bytes(q, "big") and control[0] & 1 == Q[1] % 2
Esempio n. 3
0
 def test_utils(self):
     s_spaces = " 0C 28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D  "
     b = bytes_from_octets(s_spaces)
     s = b.hex()  # lower case, no spaces
     self.assertNotEqual(s, s_spaces)
     self.assertEqual(hash160(s_spaces), hash160(bytes_from_octets(s)))
     self.assertEqual(hash256(s_spaces), hash256(bytes_from_octets(s)))
Esempio n. 4
0
def _rootxprv_from_seed(
    seed: Octets, version: Octets = NETWORKS["mainnet"].bip32_prv
) -> BIP32KeyData:
    """Return BIP32 root master extended private key from seed."""

    seed = bytes_from_octets(seed)
    bitlenght = len(seed) * 8
    if bitlenght < 128:
        raise BTClibValueError(
            f"too few bits for seed: {bitlenght} in '{hex_string(seed)}'"
        )
    if bitlenght > 512:
        raise BTClibValueError(
            f"too many bits for seed: {bitlenght} in '{hex_string(seed)}'"
        )
    hmac_ = hmac.new(b"Bitcoin seed", seed, "sha512").digest()
    k = b"\x00" + hmac_[:32]
    v = bytes_from_octets(version, 4)

    return BIP32KeyData(
        version=v,
        depth=0,
        parent_fingerprint=b"\x00" * 4,
        index=0,
        chain_code=hmac_[32:],
        key=k,
    )
Esempio n. 5
0
def decode_dict_bytes_bytes(
    map_: Optional[Mapping[Octets, Octets]]
) -> Dict[bytes, bytes]:
    "Return the dataclass element from its json representation."
    # unknown could be sorted, partial_sigs cannot
    if map_ is None:
        return {}
    return {bytes_from_octets(k): bytes_from_octets(v) for k, v in map_.items()}
Esempio n. 6
0
def _decode_from_bip32_deriv(
        bip32_deriv: Mapping[str, str]) -> Tuple[bytes, BIP32KeyOrigin]:
    # FIXME remove size checks to allow
    # the instantiation of invalid master_fingerprint and pub_key
    master_fingerprint = bytes_from_octets(bip32_deriv["master_fingerprint"],
                                           4)
    der_path = indexes_from_bip32_path(bip32_deriv["path"])
    key_origin = BIP32KeyOrigin(master_fingerprint, der_path)
    return bytes_from_octets(bip32_deriv["pub_key"]), key_origin
Esempio n. 7
0
def check_witness(wit_ver: int, wit_prg: Octets) -> bytes:

    if not 0 <= int(wit_ver) < 17:
        err_msg = "invalid witness version: "
        err_msg += f"{wit_ver} not in 0..16"
        raise BTClibValueError(err_msg)

    if wit_ver == 0:
        return bytes_from_octets(wit_prg, (20, 32))

    return bytes_from_octets(wit_prg, list(range(2, 41)))
Esempio n. 8
0
    def __init__(self,
                 script: Octets = b"",
                 check_validity: bool = True) -> None:

        self.script = bytes_from_octets(script)
        if check_validity:
            self.assert_valid()
Esempio n. 9
0
def legacy(script_: Octets, tx: Tx, vin_i: int, hash_type: int) -> bytes:
    script_ = bytes_from_octets(script_)

    new_tx = deepcopy(tx)
    for txin in new_tx.vin:
        txin.script_sig = b""
    # TODO: delete sig from script_ (even if non standard)
    new_tx.vin[vin_i].script_sig = script_
    if hash_type & 0x1F == NONE:
        new_tx.vout = []
        for i, txin in enumerate(new_tx.vin):
            if i != vin_i:
                txin.sequence = 0

    if hash_type & 0x1F == SINGLE:
        # sig_hash single bug
        if vin_i >= len(new_tx.vout):
            return (256**31).to_bytes(32, byteorder="big", signed=False)
        new_tx.vout = new_tx.vout[:vin_i + 1]
        for txout in new_tx.vout[:-1]:
            txout.script_pub_key = ScriptPubKey(b"")
            txout.value = 0xFFFFFFFFFFFFFFFF
        for i, txin in enumerate(new_tx.vin):
            if i != vin_i:
                txin.sequence = 0

    if hash_type & 0x80:
        new_tx.vin = [new_tx.vin[vin_i]]

    preimage = new_tx.serialize(include_witness=False, check_validity=False)
    preimage += hash_type.to_bytes(4, byteorder="little", signed=False)

    return hash256(preimage)
Esempio n. 10
0
def point_from_bip340pub_key(x_Q: BIP340PubKey,
                             ec: Curve = secp256k1) -> Point:
    """Return a verified-as-valid BIP340 public key as Point tuple.

    It supports:

    - BIP32 extended keys (bytes, string, or BIP32KeyData)
    - SEC Octets (bytes or hex-string, with 02, 03, or 04 prefix)
    - BIP340 Octets (bytes or hex-string, p-size Point x-coordinate)
    - native tuple
    """

    # BIP 340 key as integer
    if isinstance(x_Q, int):
        return x_Q, ec.y_even(x_Q)

    # (tuple) Point, (dict or str) BIP32Key, or 33/65 bytes
    try:
        x_Q = point_from_pub_key(x_Q, ec)[0]
        return x_Q, ec.y_even(x_Q)
    except BTClibValueError:
        pass

    # BIP 340 key as bytes or hex-string
    if isinstance(x_Q, (str, bytes)):
        Q = bytes_from_octets(x_Q, ec.p_size)
        x_Q = int.from_bytes(Q, "big", signed=False)
        return x_Q, ec.y_even(x_Q)

    raise BTClibTypeError("not a BIP340 public key")
Esempio n. 11
0
def decode_hd_key_paths(
        map_: Optional[Mapping[Octets, BIP32KeyOrigin]]) -> HdKeyPaths:
    "Return the dataclass element from its json representation."

    hd_key_paths = {bytes_from_octets(k): v
                    for k, v in map_.items()} if map_ else {}
    return dict(sorted(hd_key_paths.items()))
Esempio n. 12
0
def recover_pub_key_(
    key_id: int,
    msg_hash: Octets,
    sig: Union[Sig, Octets],
    lower_s: bool = True,
    hf: HashF = sha256,
) -> Point:
    """ECDSA public key recovery (SEC 1 v.2 section 4.1.6).

    See also:
    https://crypto.stackexchange.com/questions/18105/how-does-recovering-the-public-key-from-an-ecdsa-signature-work/18106#18106
    """

    if isinstance(sig, Sig):
        sig.assert_valid()
    else:
        sig = Sig.parse(sig)

    # The message msg_hash: a hf_len array
    hf_len = hf().digest_size
    msg_hash = bytes_from_octets(msg_hash, hf_len)

    c = challenge_(msg_hash, sig.ec, hf)  # 1.5

    QJ = _recover_pub_key_(key_id, c, sig.r, sig.s, lower_s, sig.ec)
    return sig.ec.aff_from_jac(QJ)
Esempio n. 13
0
    def __init__(
        self,
        redeem_script: Octets = b"",
        witness_script: Octets = b"",
        hd_key_paths: Optional[Mapping[Octets, BIP32KeyOrigin]] = None,
        unknown: Optional[Mapping[Octets, Octets]] = None,
        check_validity: bool = True,
    ) -> None:

        self.redeem_script = bytes_from_octets(redeem_script)
        self.witness_script = bytes_from_octets(witness_script)
        self.hd_key_paths = decode_hd_key_paths(hd_key_paths)
        self.unknown = dict(sorted(decode_dict_bytes_bytes(unknown).items()))

        if check_validity:
            self.assert_valid()
Esempio n. 14
0
def op_pushdata(data: Octets) -> bytes:
    """Convert to canonical push: OP_PUSHDATA (if needed) | length | data.

    According to standardness rules (BIP-62) the
    minimum possible PUSHDATA operator must be used.
    Byte vectors on the stack are not allowed to be more than 520 bytes long.
    """

    data = bytes_from_octets(data)
    out: List[bytes] = []
    length = len(data)
    if length < 76:  # 1-byte-length
        out.append(length.to_bytes(1, byteorder="little", signed=False))
    elif length < 256:  # OP_PUSHDATA1 | 1-byte-length
        out.append(OP_CODES["OP_PUSHDATA1"])
        out.append(length.to_bytes(1, byteorder="little", signed=False))
    elif length < 521:  # OP_PUSHDATA2 | 2-byte-length
        out.append(OP_CODES["OP_PUSHDATA2"])
        out.append(length.to_bytes(2, byteorder="little", signed=False))
    else:
        # because of the 520 bytes limit
        # there is no need to use OP_PUSHDATA4
        # out.append(OP_CODES['OP_PUSHDATA4'])
        # out.append(length.to_bytes(4, byteorder="little", signed=False))
        raise BTClibValueError(f"too many bytes for OP_PUSHDATA: {length}")
    out.append(data)
    return b"".join(out)
Esempio n. 15
0
def sign_(
    msg_hash: Octets,
    prv_key: PrvKey,
    nonce: Optional[PrvKey] = None,
    lower_s: bool = True,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> Sig:
    """Sign a hf_len bytes message according to ECDSA signature algorithm.

    If the deterministic nonce is not provided,
    the RFC6979 specification is used.
    """

    # the message msg_hash: a hf_len array
    hf_len = hf().digest_size
    msg_hash = bytes_from_octets(msg_hash, hf_len)

    # the secret key q: an integer in the range 1..n-1.
    # SEC 1 v.2 section 3.2.1
    q = int_from_prv_key(prv_key, ec)

    # the challenge
    c = challenge_(msg_hash, ec, hf)  # 4, 5

    # nonce: an integer in the range 1..n-1.
    if nonce is None:
        nonce = _rfc6979_(c, q, ec, hf)  # 1
    else:
        nonce = int_from_prv_key(nonce, ec)

    # second part delegated to helper function
    return _sign_(c, q, nonce, lower_s, ec)
Esempio n. 16
0
def sign_(
    msg_hash: Octets,
    prv_key: PrvKey,
    nonce: Optional[PrvKey] = None,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> Sig:
    """Sign a hf_len bytes message according to BIP340 signature algorithm.

    If the deterministic nonce is not provided,
    the BIP340 specification (not RFC6979) is used.
    """

    # the message msg_hash: a hf_len array
    hf_len = hf().digest_size
    msg_hash = bytes_from_octets(msg_hash, hf_len)

    # private and public keys
    q, x_Q = gen_keys(prv_key, ec)

    # nonce: an integer in the range 1..n-1.
    if nonce is None:
        nonce = _det_nonce_(msg_hash, q, x_Q, secrets.token_bytes(hf_len), ec,
                            hf)

    nonce, x_K = gen_keys(nonce, ec)

    # the challenge
    c = challenge_(msg_hash, x_Q, x_K, ec, hf)

    return _sign_(c, q, nonce, x_K, ec)
def prv_keyinfo_from_prv_key(prv_key: PrvKey,
                             network: Optional[str] = None,
                             compressed: Optional[bool] = None) -> PrvkeyInfo:

    compr = True if compressed is None else compressed
    net = "mainnet" if network is None else network
    ec = NETWORKS[net].curve

    if isinstance(prv_key, int):
        q = prv_key
    elif isinstance(prv_key, BIP32KeyData):
        return _prv_keyinfo_from_xprv(prv_key, network, compressed)
    else:
        try:
            return _prv_keyinfo_from_xprvwif(prv_key, network, compressed)
        # FIXME: except the NotPrvKeyError only, let InvalidPrvKey go through
        except ValueError:
            pass

        # it must be octets
        try:
            prv_key = bytes_from_octets(prv_key, ec.n_size)
            q = int.from_bytes(prv_key, byteorder="big", signed=False)
        except ValueError as e:
            raise BTClibValueError(f"not a private key: {prv_key!r}") from e

    if not 0 < q < ec.n:
        raise BTClibValueError(f"private key not in 1..n-1: {hex(q).upper()}")

    return q, net, compr
Esempio n. 18
0
def bin_str_entropy_from_bytes(bytes_entropy: Octets,
                               bits: OneOrMoreInt = _bits) -> BinStr:
    """Return raw entropy from the input Octets entropy.

    Input entropy can be expressed as hex-string or bytes;
    it is never padded to satisfy the bit-size requirement.

    If more bits than required are provided,
    the leftmost ones are retained.

    Default bit-sizes are 128, 160, 192, 224, 256, or 512 bits.
    """

    bytes_entropy = bytes_from_octets(bytes_entropy)

    # if a single int, make it a tuple
    if isinstance(bits, int):
        bits = (bits, )
    # ascending unique sorting of allowed bits
    bits = sorted(set(bits))

    n_bits = len(bytes_entropy) * 8
    n_bits = min(n_bits, bits[-1])

    if n_bits not in bits:
        err_msg = f"invalid number of bits: {n_bits} instead of {bits}"
        raise BTClibValueError(err_msg)

    int_entropy = int.from_bytes(bytes_entropy, byteorder="big", signed=False)
    # only the leftmost bits will be retained
    return bin_str_entropy_from_int(int_entropy, n_bits)
Esempio n. 19
0
def assert_as_valid(msg: Octets, e0: bytes, s: SValues, pubk_rings: PubkeyRing) -> bool:

    msg = bytes_from_octets(msg)
    m = _get_msg_format(msg, pubk_rings)

    ring_size = len(pubk_rings)
    e: SValues = defaultdict(list)
    e0bytes = m
    for i in range(ring_size):
        keys_size = len(pubk_rings[i])
        e[i] = [0] * keys_size
        e[i][0] = int_from_bits(_hash(m, e0, i, 0), ec.nlen) % ec.n
        # edge case that cannot be reproduced in the test suite
        if e[i][0] == 0:
            err_msg = "implausibile signature failure"  # pragma: no cover
            raise BTClibRuntimeError(err_msg)  # pragma: no cover
        r = b"\0x00"
        for j in range(keys_size):
            t = double_mult(-e[i][j], pubk_rings[i][j], s[i][j], ec.G)
            r = bytes_from_point(t, ec)
            if j != len(pubk_rings[i]) - 1:
                h = _hash(m, r, i, j + 1)
                e[i][j + 1] = int_from_bits(h, ec.nlen) % ec.n
                # edge case that cannot be reproduced in the test suite
                if e[i][j + 1] == 0:
                    err_msg = "implausibile signature failure"  # pragma: no cover
                    raise BTClibRuntimeError(err_msg)  # pragma: no cover
            else:
                e0bytes += r
    e0_prime = hf(e0bytes).digest()
    return e0_prime == e0
Esempio n. 20
0
def reduce_to_hlen(msg: Octets, hf: HashF = hashlib.sha256) -> bytes:

    msg = bytes_from_octets(msg)
    # Step 4 of SEC 1 v.2 section 4.1.3
    h = hf()
    h.update(msg)
    return h.digest()
Esempio n. 21
0
def test_hash160_hash256() -> None:
    test_vectors = (plain_prv_keys + net_unaware_compressed_pub_keys +
                    net_unaware_uncompressed_pub_keys)
    for hexstring in test_vectors:
        b = bytes_from_octets(hexstring)
        s = b.hex()  # lower case, no spaces
        assert hash160(hexstring) == hash160(s)
        assert hash256(hexstring) == hash256(s)
Esempio n. 22
0
    def parse(cls: Type["BIP32KeyOrigin"],
              data: Octets,
              check_validity: bool = True) -> "BIP32KeyOrigin":
        "Return a BIP32KeyOrigin by parsing binary data."

        data = bytes_from_octets(data)
        master_fingerprint = data[:4]
        der_path = indexes_from_bip32_path(data[4:])

        return cls(master_fingerprint, der_path, check_validity)
Esempio n. 23
0
def challenge_(msg_hash: Octets,
               ec: Curve = secp256k1,
               hf: HashF = hashlib.sha256) -> int:

    # the message msg_hash: a hf_len array
    hf_len = hf().digest_size
    msg_hash = bytes_from_octets(msg_hash, hf_len)

    # leftmost ec.nlen bits %= ec.n
    return int_from_bits(msg_hash, ec.nlen) % ec.n
Esempio n. 24
0
    def __init__(self,
                 stack: Optional[Sequence[Octets]] = None,
                 check_validity: bool = True) -> None:

        # https://docs.python.org/3/tutorial/controlflow.html#default-argument-values
        self.stack = [bytes_from_octets(element)
                      for element in stack] if stack else []

        if check_validity:
            self.assert_valid()
Esempio n. 25
0
def det_nonce_(
    msg_hash: Octets,
    prv_key: PrvKey,
    aux: Optional[Octets] = None,
    ec: Curve = secp256k1,
    hf: HashF = sha256,
) -> int:
    "Return a BIP340 deterministic ephemeral key (nonce)."

    # the message msg_hash: a hf_len array
    hf_len = hf().digest_size
    msg_hash = bytes_from_octets(msg_hash, hf_len)

    q, Q = gen_keys(prv_key, ec)

    # the auxiliary random component
    aux = secrets.token_bytes(hf_len) if aux is None else bytes_from_octets(aux)

    return _det_nonce_(msg_hash, q, Q, aux, ec, hf)
Esempio n. 26
0
    def __init__(
        self,
        version: int = 1,
        previous_block_hash: Octets = b"",
        merkle_root_: Octets = b"",
        time: datetime = datetime.fromtimestamp(0),
        bits: Octets = b"",
        nonce: int = 0,
        check_validity: bool = True,
    ) -> None:

        self.version = version
        self.previous_block_hash = bytes_from_octets(previous_block_hash)
        self.merkle_root = bytes_from_octets(merkle_root_)
        self.time = time
        self.bits = bytes_from_octets(bits)
        self.nonce = nonce

        if check_validity:
            self.assert_valid()
Esempio n. 27
0
    def __init__(
        self,
        version: Octets,
        depth: int,
        parent_fingerprint: Octets,
        index: int,
        chain_code: Octets,
        key: Octets,
        check_validity: bool = True,
    ) -> None:

        self.version = bytes_from_octets(version)
        self.depth = depth
        self.parent_fingerprint = bytes_from_octets(parent_fingerprint)
        self.index = index
        self.chain_code = bytes_from_octets(chain_code)
        self.key = bytes_from_octets(key)

        if check_validity:
            self.assert_valid()
Esempio n. 28
0
    def test_utils(self):
        b = bytes_from_octets(int_with_whitespaces)
        s = b.hex()  # lower case, no spaces
        self.assertNotEqual(int_with_whitespaces, s)
        self.assertEqual(hash160(int_with_whitespaces), hash160(s))
        self.assertEqual(hash256(int_with_whitespaces), hash256(s))

        i = secrets.randbits(256)
        self.assertEqual(i, int_from_integer(i))
        self.assertEqual(i, int_from_integer(i.to_bytes(32, "big")))
        self.assertEqual(i, int_from_integer(hex(i)))
def assert_p2wpkh(script_pub_key: Octets) -> None:
    script_pub_key = bytes_from_octets(script_pub_key, 22)
    # p2wpkh [OP_0, pub_key hash]
    # 0x0014{20-byte pub_key hash}
    if script_pub_key[0] != 0:
        err_msg = f"invalid witness version: {script_pub_key[0]}"
        err_msg += f" instead of {0}"
        raise BTClibValueError(err_msg)
    if script_pub_key[1] != 0x14:
        err_msg = f"invalid pub_key hash length marker: {script_pub_key[1]}"
        err_msg += f" instead of {0x14}"
        raise BTClibValueError(err_msg)
def assert_p2wsh(script_pub_key: Octets) -> None:
    script_pub_key = bytes_from_octets(script_pub_key, 34)
    # p2wsh [OP_0, redeem_script hash]
    # 0x0020{32-byte redeem_script hash}
    if script_pub_key[0] != 0:
        err_msg = f"invalid witness version: {script_pub_key[0]}"
        err_msg += f" instead of {0}"
        raise BTClibValueError(err_msg)
    if script_pub_key[1] != 0x20:
        err_msg = f"invalid redeem script hash length marker: {script_pub_key[1]}"
        err_msg += f" instead of {0x20}"
        raise BTClibValueError(err_msg)