def _prv_keyinfo_from_xprv(xprv: BIP32Key,
                           network: Optional[str] = None,
                           compressed: Optional[bool] = None) -> PrvkeyInfo:
    """Return prv_key tuple (int, compressed, network) from BIP32 xprv.

    BIP32Key is always compressed and includes network information:
    here the 'network, compressed' input parameters are passed
    only to allow consistency checks.
    """

    compressed = True if compressed is None else compressed
    if not compressed:
        raise BTClibValueError("uncompressed SEC / compressed BIP32 mismatch")

    if isinstance(xprv, BIP32KeyData):
        xprv.assert_valid()
    else:
        xprv = BIP32KeyData.b58decode(xprv)

    if xprv.key[0] != 0:
        err_msg = f"not a private key: {xprv.b58encode()}"
        raise BTClibValueError(err_msg)

    if network is None:
        network = network_from_xkeyversion(xprv.version)

    allowed_versions = xprvversions_from_network(network)
    if xprv.version not in allowed_versions:
        err_msg = f"not a {network} key: "
        err_msg += f"{xprv.b58encode()}"
        raise BTClibValueError(err_msg)

    q = int.from_bytes(xprv.key[1:], byteorder="big")
    return q, network, True
예제 #2
0
def mult_sliding_window(m: int,
                        Q: JacPoint,
                        ec: CurveGroup,
                        w: int = 4) -> JacPoint:
    """Scalar multiplication using "sliding window".

    It has the benefit that the pre-computation stage
    is roughly half as complex as the normal windowed method.
    It is not constant time.
    For 256-bit scalars choose w=4 or w=5.

    The input point is assumed to be on curve and
    the m coefficient is assumed to have been reduced mod n
    if appropriate (e.g. cyclic groups of order n).
    """

    if m < 0:
        raise BTClibValueError(f"negative m: {hex(m)}")

    # a number cannot be written in basis 1 (ie w=0)
    if w <= 0:
        raise BTClibValueError(f"non positive w: {w}")

    k = w - 1
    p = pow(2, k)

    # at each step one of the points in T will be added
    P = Q
    for _ in range(k):
        P = ec.double_jac(P)
    T = [P]
    for i in range(1, p):
        T.append(ec.add_jac(T[i - 1], Q))

    digits = convert_number_to_base(m, 2)

    R = INFJ
    i = 0
    while i < len(digits):
        if digits[i] == 0:
            R = ec.double_jac(R)
            i += 1
        else:
            j = len(digits) - i if (len(digits) - i) < w else w
            t = digits[i]
            for a in range(1, j):
                t = 2 * t + digits[i + a]

            if j < w:
                for b in range(i, (i + j)):
                    R = ec.double_jac(R)
                    if digits[b] == 1:
                        R = ec.add_jac(R, Q)
                return R
            for _ in range(w):
                R = ec.double_jac(R)
            R = ec.add_jac(R, T[t - p])
            i += j

    return R
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
예제 #4
0
    def assert_signable(self) -> None:

        self.assert_valid()

        for i, tx_in in enumerate(self.tx.vin):

            non_witness_utxo = self.inputs[i].non_witness_utxo
            witness_utxo = self.inputs[i].witness_utxo
            redeem_script = self.inputs[i].redeem_script

            if witness_utxo:
                script_pub_key = witness_utxo.script_pub_key
                script_type, payload = type_and_payload(script_pub_key.script)
                if script_type == "p2sh":
                    script_type, _ = type_and_payload(redeem_script)
                if script_type not in ("p2wpkh", "p2wsh"):
                    raise BTClibValueError(
                        "script type not it ('p2wpkh', 'p2wsh')")
            elif non_witness_utxo:
                script_pub_key = non_witness_utxo.vout[
                    tx_in.prev_out.vout].script_pub_key
                _, payload = type_and_payload(script_pub_key.script)
            else:
                err_msg = "missing script_pub_key"
                raise BTClibValueError(err_msg)

            if redeem_script and payload != hash160(redeem_script):
                raise BTClibValueError("invalid redeem script hash160")

            if self.inputs[i].witness_script:
                if redeem_script:
                    _, payload = type_and_payload(redeem_script)
                if payload != sha256(self.inputs[i].witness_script):
                    raise BTClibValueError("invalid witness script sha256")
예제 #5
0
    def parse(cls: Type["Sig"],
              data: BinaryData,
              check_validity: bool = True) -> "Sig":
        """Return a Sig by parsing binary data.

        Deserialize a strict ASN.1 DER representation of an ECDSA signature.
        """

        stream = bytesio_from_binarydata(data)
        ec = secp256k1

        # [0x30] [data-size][0x02][r-size][r][0x02][s-size][s]
        marker = stream.read(1)
        if marker != _DER_SIG_MARKER:
            err_msg = f"invalid compound header: {marker.hex()}"
            err_msg += f", instead of DER sequence tag {_DER_SIG_MARKER.hex()}"
            raise BTClibValueError(err_msg)

        # [data-size][0x02][r-size][r][0x02][s-size][s]
        sig_data = var_bytes.parse(stream, forbid_zero_size=True)

        # [0x02][r-size][r][0x02][s-size][s]
        sig_data_substream = bytesio_from_binarydata(sig_data)
        r = _deserialize_scalar(sig_data_substream)
        s = _deserialize_scalar(sig_data_substream)

        # to prevent malleability
        # the sig_data_substream must have been consumed entirely
        if sig_data_substream.read(1) != b"":
            err_msg = "invalid DER sequence length"
            raise BTClibValueError(err_msg)

        return cls(r, s, ec, check_validity)
예제 #6
0
파일: dsa.py 프로젝트: btclib-org/btclib
def crack_prv_key_(
    msg_hash1: Octets,
    sig1: Union[Sig, Octets],
    msg_hash2: Octets,
    sig2: Union[Sig, Octets],
    hf: HashF = sha256,
) -> Tuple[int, int]:

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

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

    ec = sig2.ec
    if sig1.ec != ec:
        raise BTClibValueError("not the same curve in signatures")
    if sig1.r != sig2.r:
        raise BTClibValueError("not the same r in signatures")
    if sig1.s == sig2.s:
        raise BTClibValueError("identical signatures")

    c_1 = challenge_(msg_hash1, ec, hf)
    c_2 = challenge_(msg_hash2, ec, hf)

    nonce = (c_1 - c_2) * mod_inv(sig1.s - sig2.s, ec.n) % ec.n
    q = (sig2.s * nonce - c_2) * mod_inv(sig1.r, ec.n) % ec.n
    return q, nonce
예제 #7
0
def b58decode(v: String, out_size: Optional[int] = None) -> bytes:
    """Decode a Base58Check encoded bytes-like object or ASCII string.

    Optionally, it also ensures required output size.
    """

    if isinstance(v, str):
        # do not trim spaces
        v = v.encode("ascii")

    result = _b58decode(v)
    if len(result) < 4:
        err_msg = "not enough bytes for checksum, "
        err_msg += f"invalid base58 decoded size: {len(result)}"
        raise BTClibValueError(err_msg)

    result, checksum = result[:-4], result[-4:]
    h256 = hash256(result)
    if checksum != h256[:4]:
        err_msg = f"invalid checksum: 0x{checksum.hex()} instead of 0x{h256[:4].hex()}"
        raise BTClibValueError(err_msg)

    if out_size is None or len(result) == out_size:
        return result

    err_msg = "valid checksum, invalid decoded size: "
    err_msg += f"{len(result)} bytes instead of {out_size}"
    raise BTClibValueError(err_msg)
예제 #8
0
    def load_lang(self, lang: str, filename: Optional[str] = None) -> None:
        """Load/add a language word-list if not loaded/added yet.

        The language file has to be provided for adding new languages
        beyond those already provided.
        """

        # a new language, unknown before
        if lang not in self.languages:
            if filename is None:
                raise BTClibValueError(f"Missing file for language '{lang}'")
            # initialize the new language
            self.languages.append(lang)
            self.language_files[lang] = filename
            self._wordlist[lang] = []
            self._bits_per_word[lang] = 0
            self._language_length[lang] = 0

        # language has not been loaded yet
        if self._language_length[lang] == 0:
            with open(self.language_files[lang], "r",
                      encoding="ascii") as file_:
                lines = file_.readlines()

            nwords = len(lines)
            # http://www.graphics.stanford.edu/~seander/bithacks.html
            if nwords & (nwords - 1) != 0:
                err_msg = f"invalid wordlist length: {nwords}, not a power of two"
                raise BTClibValueError(err_msg)

            self._language_length[lang] = nwords
            # clean up and normalization are missing, but removal of \n
            self._wordlist[lang] = [line[:-1] for line in lines]
예제 #9
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,
    )
예제 #10
0
    def assert_valid(self) -> None:

        # must be a 4-bytes _signed_ integer
        if not 0 < self.version <= 0x7FFFFFFF:
            raise BTClibValueError(f"invalid version: {hex(self.version)}")

        if self.time.timestamp() < 1231006505:
            err_msg = "invalid timestamp (before genesis)"
            date = datetime.fromtimestamp(self.time.timestamp(), timezone.utc)
            err_msg += f": {date}"
            raise BTClibValueError(err_msg)
        # TODO: check for max 4-bytes timestamp

        for key, size in _KEY_SIZE:
            value = bytes(getattr(self, key))
            if len(value) != size:
                err_msg = f"invalid {key} length: "
                err_msg += f"{len(value)} bytes"
                err_msg += f" instead of {size}"
                raise BTClibValueError(err_msg)

        self.nonce = int(self.nonce)
        if not 0 < self.nonce <= 0xFFFFFFFF:
            raise BTClibValueError(f"invalid nonce: {hex(self.nonce)}")

        self.assert_valid_pow()
예제 #11
0
def _pub_keyinfo_from_xpub(xpub: BIP32Key,
                           network: Optional[str] = None,
                           compressed: Optional[bool] = None) -> PubkeyInfo:
    """Return the pub_key tuple (SEC-bytes, network) from a BIP32 xpub.

    BIP32Key is always compressed and includes network information:
    here the 'network, compressed' input parameters are passed
    only to allow consistency checks.
    """

    compressed = True if compressed is None else compressed
    if not compressed:
        raise BTClibValueError("Uncompressed SEC / compressed BIP32 mismatch")

    if isinstance(xpub, BIP32KeyData):
        xpub.assert_valid()
    else:
        xpub = BIP32KeyData.b58decode(xpub)

    if xpub.key[0] not in (2, 3):
        err_msg = f"not a public key: {xpub.b58encode()}"
        raise BTClibValueError(err_msg)

    if network is None:
        return xpub.key, network_from_xkeyversion(xpub.version)

    allowed_versions = xpubversions_from_network(network)
    if xpub.version not in allowed_versions:
        err_msg = f"Not a {network} key: "
        err_msg += f"{xpub.b58encode()}"
        raise BTClibValueError(err_msg)

    return xpub.key, network
예제 #12
0
def witness_from_address(b32addr: String) -> Tuple[int, bytes, str]:
    "Return the witness from a bech32 native SegWit address."

    if isinstance(b32addr, str):
        b32addr = b32addr.strip()

    # the following check was originally in b32decode
    # but it does not pertain there
    if len(b32addr) > 90:
        raise BTClibValueError(
            f"invalid bech32 address length: {len(b32addr)} > 90")

    hrp, data = b32decode(b32addr)

    # check that it is a known SegWit address type
    network = network_from_key_value("hrp", hrp)
    if network is None:
        raise BTClibValueError(f"invalid hrp: {hrp}")

    if len(data) == 0:
        raise BTClibValueError(f"empty data in bech32 address: {b32addr!r}")

    wit_ver = data[0]
    wit_prg = bytes(power_of_2_base_conversion(data[1:], 5, 8, False))
    return wit_ver, check_witness(wit_ver, wit_prg), network
예제 #13
0
def address_from_xpub(xpub: BIP32Key) -> str:
    """Return the SLIP132 base58/bech32 address.

    The address is always derived from the compressed public key,
    as this is the default public key representation in BIP32.
    """

    if not isinstance(xpub, BIP32KeyData):
        xpub = BIP32KeyData.b58decode(xpub)

    if xpub.key[0] not in (2, 3):
        err_msg = f"not a public key: {xpub.b58encode()}"
        raise BTClibValueError(err_msg)

    function_list: List[Callable[[Any, str], str]] = [
        b58.p2pkh,
        b32.p2wpkh,
        b58.p2wpkh_p2sh,
    ]
    version_list: List[str] = [
        "bip32_pub",
        "slip132_p2wpkh_pub",
        "slip132_p2wpkh_p2sh_pub",
    ]
    for version, function in zip(version_list, function_list):
        # with pytohn>=3.8 use walrus operator
        # if network := network_from_key_value(version, xpub.version):
        network = network_from_key_value(version, xpub.version)
        if network:
            return function(xpub, network)
    err_msg = f"unknown xpub version: {xpub.version.hex()}"  # pragma: no cover
    raise BTClibValueError(err_msg)  # pragma: no cover
예제 #14
0
def power_of_2_base_conversion(data: Iterable[int],
                               from_bits: int,
                               to_bits: int,
                               pad: bool = True) -> List[int]:
    "Convert a power-of-two digit sequence to another power-of-two base."
    acc = 0
    bits = 0
    ret = []
    maxv = (1 << to_bits) - 1
    max_acc = (1 << (from_bits + to_bits - 1)) - 1
    for value in data:
        if value < 0 or (value >> from_bits):
            raise BTClibValueError(f"invalid value: {value}")
        acc = ((acc << from_bits) | value) & max_acc
        bits += from_bits
        while bits >= to_bits:
            bits -= to_bits
            ret.append((acc >> bits) & maxv)

    if pad:
        if bits:
            ret.append((acc << (to_bits - bits)) & maxv)
    elif bits >= from_bits:
        err_msg = f"zero padding of more than {from_bits-1} bits"
        err_msg += f" in {from_bits}-to-{to_bits} conversion"
        raise BTClibValueError(err_msg)
    elif (acc << (to_bits - bits)) & maxv:
        err_msg = f"non-zero padding in {from_bits}-to-{to_bits} conversion"
        raise BTClibValueError(err_msg)

    return ret
예제 #15
0
    def assert_valid(self) -> None:
        # r is a scalar, fail if r is not in [1, n-1]
        if not 0 < self.r < self.ec.n:
            err_msg = "scalar r not in 1..n-1: "
            err_msg += f"'{hex_string(self.r)}'" if self.r > 0xFFFFFFFF else f"{self.r}"
            raise BTClibValueError(err_msg)

        # ensure r is congruent to a valid x-coordinate
        r = self.r
        congruence_not_found = True
        while congruence_not_found and r < self.ec.p:
            try:
                self.ec.y(r)
                congruence_not_found = False
            except BTClibValueError:
                r += self.ec.n
        if congruence_not_found:
            err_msg = "r is not (congruent to) a valid x-coordinate: "
            err_msg += f"'{hex_string(self.r)}'" if self.r > 0xFFFFFFFF else f"{self.r}"
            raise BTClibValueError(err_msg)

        # s is a scalar, fail if s is not in [1, n-1]
        if not 0 < self.s < self.ec.n:
            err_msg = "scalar s not in 1..n-1: "
            err_msg += f"'{hex_string(self.s)}'" if self.s > 0xFFFFFFFF else f"{self.s}"
            raise BTClibValueError(err_msg)
예제 #16
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
예제 #17
0
def _assert_valid_version(version: int) -> None:

    # must be a 4-bytes int
    if not 0 <= version <= 0xFFFFFFFF:
        raise BTClibValueError(f"invalid version: {version}")
    # actually the only version that is currently handled is zero
    if version != 0:
        raise BTClibValueError(f"invalid non-zero version: {version}")
예제 #18
0
 def assert_valid(self) -> None:
     if len(self.master_fingerprint) != 4:
         err_msg = "invalid master fingerprint length: "
         err_msg += f"{len(self.master_fingerprint)}"
         raise BTClibValueError(err_msg)
     if len(self) > 255:
         raise BTClibValueError(f"invalid der_path size: {len(self.der_path)}")
     if any(not 0 <= i <= 0xFFFFFFFF for i in self.der_path):
         raise BTClibValueError("invalid der_path element")
예제 #19
0
def str_from_index_int(i: int, hardening: str = _HARDENING) -> str:

    if hardening not in ("'", "h", "H"):
        raise BTClibValueError(f"invalid hardening symbol: {hardening}")
    if not 0 <= i <= 0xFFFFFFFF:
        raise BTClibValueError(f"invalid index: {i}")
    if i < 0x80000000:
        return str(i)
    return str(i - 0x80000000) + hardening
예제 #20
0
 def is_on_curve(self, Q: Point) -> bool:
     """Return True if the point is on the curve."""
     if len(Q) != 2:
         raise BTClibValueError("point must be a tuple[int, int]")
     if Q[1] == 0:  # Infinity point in affine coordinates
         return True
     if not 0 < Q[1] < self.p:  # y cannot be zero
         raise BTClibValueError(
             f"y-coordinate not in 1..p-1: '{hex_string(Q[1])}'")
     return self._y2(Q[0]) == (Q[1] * Q[1] % self.p)
예제 #21
0
 def assert_valid(self) -> None:
     if len(self.tx_id) != 32:
         err_msg = f"invalid OutPoint tx_id: {len(self.tx_id)}"
         err_msg += " instead of 32 bytes"
         raise BTClibValueError(err_msg)
     # must be a 4-bytes int
     if not 0 <= self.vout <= 0xFFFFFFFF:
         raise BTClibValueError(f"invalid vout: {self.vout}")
     # not a coinbase, not a regular OutPoint
     if (self.tx_id == b"\x00" * 32) ^ (self.vout == 0xFFFFFFFF):
         raise BTClibValueError("invalid OutPoint")
예제 #22
0
def deserialize_tx(
    k: bytes, v: bytes, type_: str, include_witness: Optional[bool] = True
) -> Tx:
    "Return the dataclass element from its binary representation."

    if len(k) != 1:
        err_msg = f"invalid {type_} key length: {len(k)}"
        raise BTClibValueError(err_msg)
    tx = Tx.parse(v)
    if not include_witness and tx.serialize(include_witness=False) != v:
        raise BTClibValueError("wrong tx serialization format")
    return tx
예제 #23
0
    def parse(cls: Type["Psbt"],
              psbt_bin: Octets,
              check_validity: bool = True) -> "Psbt":
        "Return a Psbt by parsing binary data."

        # FIXME: psbt_bin should be BinaryData
        # stream = bytesio_from_binarydata(psbt_bin)
        # and the deserialization should happen reading the stream
        # not slicing bytes

        tx = Tx(check_validity=False)
        version = 0
        hd_key_paths: Dict[Octets, BIP32KeyOrigin] = {}
        unknown: Dict[Octets, Octets] = {}

        # psbt_bin = bytes_from_octets(psbt_bin)
        stream = bytesio_from_binarydata(psbt_bin)

        if stream.read(4) != PSBT_MAGIC_BYTES:
            raise BTClibValueError("malformed psbt: missing magic bytes")
        if stream.read(1) != PSBT_SEPARATOR:
            raise BTClibValueError("malformed psbt: missing separator")

        global_map, stream = deserialize_map(stream)
        for k, v in global_map.items():
            if k[:1] == PSBT_GLOBAL_UNSIGNED_TX:
                tx = deserialize_tx(k, v, "global unsigned tx", False)
            elif k[:1] == PSBT_GLOBAL_VERSION:
                version = deserialize_int(k, v, "global version")
            elif k[:1] == PSBT_GLOBAL_XPUB:
                hd_key_paths[k[1:]] = BIP32KeyOrigin.parse(v)
            else:  # unknown
                unknown[k] = v

        inputs: List[PsbtIn] = []
        for _ in tx.vin:
            input_map, stream = deserialize_map(stream)
            inputs.append(PsbtIn.parse(input_map))

        outputs: List[PsbtOut] = []
        for _ in tx.vout:
            output_map, stream = deserialize_map(stream)
            outputs.append(PsbtOut.parse(output_map))

        return cls(
            tx,
            inputs,
            outputs,
            version,
            hd_key_paths,
            unknown,
            check_validity,
        )
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)
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_p2sh(script_pub_key: Octets) -> None:
    script_pub_key = bytes_from_octets(script_pub_key, 23)
    # p2sh [OP_HASH160, redeem_script hash, OP_EQUAL]
    # 0xA914{20-byte redeem_script hash}87
    if script_pub_key[-1] != 0x87:
        raise BTClibValueError("missing final OP_EQUAL")
    if script_pub_key[0] != 0xA9:
        raise BTClibValueError("missing leading OP_HASH160")
    if script_pub_key[1] != 0x14:
        err_msg = f"invalid redeem script hash length marker: {script_pub_key[1]}"
        err_msg += f" instead of {0x14}"
        raise BTClibValueError(err_msg)
def assert_p2pkh(script_pub_key: Octets) -> None:
    script_pub_key = bytes_from_octets(script_pub_key, 25)
    # p2pkh [OP_DUP, OP_HASH160, pub_key hash, OP_EQUALVERIFY, OP_CHECKSIG]
    # 0x76A914{20-byte pub_key_hash}88AC
    if script_pub_key[-2:] != b"\x88\xac":
        raise BTClibValueError("missing final OP_EQUALVERIFY, OP_CHECKSIG")
    if script_pub_key[:2] != b"\x76\xa9":
        raise BTClibValueError("missing leading OP_DUP, OP_HASH160")
    if script_pub_key[2] != 0x14:
        err_msg = f"invalid pub_key hash length marker: {script_pub_key[2]}"
        err_msg += f" instead of {0x14}"
        raise BTClibValueError(err_msg)
예제 #28
0
파일: curve.py 프로젝트: btclib-org/btclib
    def __init__(self, p: Integer, a: Integer, b: Integer, G: Point) -> None:

        super().__init__(p, a, b)

        # 2. check that xG and yG are integers in the interval [0, p−1]
        # 4. Check that yG^2 = xG^3 + a*xG + b (mod p)
        if len(G) != 2:
            raise BTClibValueError("Generator must a be a sequence[int, int]")
        self.G = (int_from_integer(G[0]), int_from_integer(G[1]))
        if not self.is_on_curve(self.G):
            raise BTClibValueError("Generator is not on the curve")
        self.GJ = self.G[0], self.G[1], 1  # Jacobian coordinates
예제 #29
0
def mult_w_NAF(m: int, Q: JacPoint, ec: CurveGroup, w: int = 4) -> JacPoint:
    """Scalar multiplication in Jacobian coordinates using wNAF.

    This implementation uses the same method called "w-ary non-adjacent form" (wNAF)
    we make use of the fact that point subtraction is as easy as point addition to perform fewer operations compared to sliding-window
    In fact, on Weierstrass curves, known P, -P can be computed on the fly.

    The input point is assumed to be on curve and
    the m coefficient is assumed to have been reduced mod n
    if appropriate (e.g. cyclic groups of order n).
    'right-to-left' method.

    FIXME:
    - Make it constant time (if necessary)
    - Try to avoid exception in negation for w=1
    """

    if m < 0:
        raise BTClibValueError(f"negative m: {hex(m)}")

    # a number cannot be written in basis 1 (ie w=0)
    if w <= 0:
        raise BTClibValueError(f"non positive w: {w}")

    M = wNAF_of_m(m, w)

    p = len(M)

    b = pow(2, w)

    Q2 = ec.double_jac(Q)
    T = [Q]
    for i in range(1, (b // 4)):
        T.append(ec.add_jac(T[i - 1], Q2))
    for i in range((b // 4), (b // 2)):
        T.append(ec.negate_jac(T[i - (b // 4)]))

    R = INFJ
    for j in range(p - 1, -1, -1):
        R = ec.double_jac(R)
        if M[j] != 0:
            if M[j] > 0:
                # It adds the element jQ
                R = ec.add_jac(R, T[(M[j] - 1) // 2])
            else:
                # In this case it adds the opposite, ie -jQ
                if w != 1:
                    R = ec.add_jac(R, T[(b // 4) - ((M[j] + 1) // 2)])
                else:
                    # Case w=1 must be studied on its own for now
                    R = R = ec.add_jac(R, T[1])
    return R
예제 #30
0
def from_tx(prevouts: List[TxOut], tx: Tx, vin_i: int,
            hash_type: int) -> bytes:

    script = prevouts[vin_i].script_pub_key.script

    if is_p2tr(script):
        annex = b""
        witness = tx.vin[vin_i].script_witness
        if len(witness.stack) >= 2 and witness.stack[-1][0] == 0x50:
            annex = witness.stack[-1]
            witness.stack = witness.stack[:-1]

        if len(witness.stack) == 0:
            raise BTClibValueError("Empty stack")

        ext = b""
        if len(witness.stack) > 1:
            leaf_version = witness.stack[-1][0] & 0xFE
            preimage = leaf_version.to_bytes(1, "big")
            preimage += var_bytes.serialize(witness.stack[-2])
            tapleaf_hash = tagged_hash(b"TapLeaf", preimage)
            ext = tapleaf_hash + b"\x00\xff\xff\xff\xff"

        return taproot(
            tx,
            vin_i,
            [x.value for x in prevouts],
            [x.script_pub_key for x in prevouts],
            hash_type,
            int(bool(ext)),
            annex,
            ext,
        )

    # handle all p2sh-wrapped scripts
    if is_p2sh(script):
        script = tx.vin[vin_i].script_sig

    if is_p2wpkh(script):
        script_ = witness_v0_script(script)[0]
        return segwit_v0(script_, tx, vin_i, hash_type, prevouts[vin_i].value)

    if is_p2wsh(script):
        # the real script is contained in the witness
        script_ = witness_v0_script(tx.vin[vin_i].script_witness.stack[-1])[0]
        return segwit_v0(script_, tx, vin_i, hash_type, prevouts[vin_i].value)

    if is_p2tr(script):
        raise BTClibValueError("Taproot scripts cannot be wrapped in p2sh")

    script_ = legacy_script(script)[0]
    return legacy(script_, tx, vin_i, hash_type)