コード例 #1
0
 def _copy_kv(self, fout, psbtv, key):
     # find offset of the key if it exists
     off = psbtv.seek_to_value(key, from_current=True)
     if off is None:
         return
     # we found it - copy over
     ser_string(fout, key)
     l = compact.read_from(psbtv.stream)
     fout.write(compact.to_bytes(l))
     read_write(psbtv.stream, fout, l)
     return off
コード例 #2
0
ファイル: wallet.py プロジェクト: nolim1t/specter-diy
 def fill_pset_scope(self,
                     scope,
                     desc,
                     stream=None,
                     rangeproof_offset=None,
                     surj_proof_offset=None):
     # if we don't have a rangeproof offset - nothing we can really do
     if rangeproof_offset is None:
         return True
     # pointer and length of preallocated memory for rangeproof rewind
     memptr, memlen = get_preallocated_ram()
     # for inputs we check if rangeproof is there
     # check if we actually need to rewind
     if None not in [
             scope.asset, scope.value, scope.asset_blinding_factor,
             scope.value_blinding_factor
     ]:
         # verify that asset and value blinding factors lead to value and asset commitments
         return True
     stream.seek(rangeproof_offset)
     l = compact.read_from(stream)
     vout = scope.utxo if isinstance(scope,
                                     LInputScope) else scope.blinded_vout
     blinding_key = desc.blinding_key.get_blinding_key(
         vout.script_pubkey).secret
     # get the nonce for unblinding
     pub = secp256k1.ec_pubkey_parse(vout.ecdh_pubkey)
     secp256k1.ec_pubkey_tweak_mul(pub, blinding_key)
     sec = secp256k1.ec_pubkey_serialize(pub)
     nonce = hashlib.sha256(hashlib.sha256(sec).digest()).digest()
     commit = secp256k1.pedersen_commitment_parse(vout.value)
     gen = secp256k1.generator_parse(vout.asset)
     try:
         value, vbf, msg, _, _ = secp256k1.rangeproof_rewind_from(
             stream, l, memptr, memlen, nonce, commit,
             vout.script_pubkey.data, gen)
     except ValueError as e:
         raise RewindError(str(e))
     asset = msg[:32]
     abf = msg[32:64]
     scope.value = value
     scope.value_blinding_factor = vbf
     scope.asset = asset
     scope.asset_blinding_factor = abf
     return True
コード例 #3
0
ファイル: helpers.py プロジェクト: swipswaps/specter-diy
def aead_decrypt(ciphertext: bytes, key: bytes) -> tuple:
    """
    Verifies MAC and decrypts ciphertext with associated data.
    Inverse to aead_encrypt
    Returns a tuple adata, plaintext
    """
    mac = ciphertext[-32:]
    ct = ciphertext[:-32]

    aes_key = tagged_hash("aes", key)
    hmac_key = tagged_hash("hmac", key)
    assert mac == hmac.new(hmac_key, msg=ct, digestmod="sha256").digest()
    b = BytesIO(ct)
    l = compact.read_from(b)
    adata = b.read(l)
    assert len(adata) == l
    ct = b.read()
    if len(ct) == 0:
        return adata, b""
    return adata, decrypt(ct, aes_key)
コード例 #4
0
def aead_decrypt(ciphertext: bytes, key: bytes) -> tuple:
    """
    Verifies MAC and decrypts ciphertext with associated data.
    Inverse to aead_encrypt
    Returns a tuple adata, plaintext
    """
    mac = ciphertext[-32:]
    ct = ciphertext[:-32]

    aes_key = tagged_hash("aes", key)
    hmac_key = tagged_hash("hmac", key)
    if mac != hmac.new(hmac_key, ct, digestmod="sha256").digest():
        raise Exception("Invalid HMAC")
    b = BytesIO(ct)
    l = compact.read_from(b)
    adata = b.read(l)
    if len(adata) != l:
        raise Exception("Invalid length")
    ct = b.read()
    if len(ct) == 0:
        return adata, b""
    return adata, decrypt(ct, aes_key)
コード例 #5
0
ファイル: manager.py プロジェクト: nolim1t/specter-diy
    def preprocess_psbt(self, stream, fout):
        """
        Processes incoming PSBT, fills missing information and writes to fout.
        Returns:
        - wallets in inputs: dict {wallet: amount}
        - metadata for tx display including warnings that require user confirmation
        - default sighash to use for signing
        """
        self.show_loader(title="Parsing transaction...")

        # compress = True flag will make sure large fields won't be loaded to RAM
        psbtv = self.PSBTViewClass.view(stream, compress=True)

        # Write global scope first
        psbtv.stream.seek(psbtv.offset)
        res = read_write(psbtv.stream, fout, psbtv.first_scope-psbtv.offset)

        # string representation of the Bitcoin for wallet processing
        fee = 0

        # here we will store all wallets that we detect in inputs
        # {wallet: {"amount": amount, "gaps": [gaps]}}
        wallets = {}
        meta = {
            "inputs": [{} for i in range(psbtv.num_inputs)],
            "outputs": [{} for i in range(psbtv.num_outputs)],
            "default_asset": "BTC" if self.network == "main" else "tBTC",
        }

        fingerprint = self.keystore.fingerprint
        # We need to detect wallets owning inputs and outputs,
        # in case of liquid - unblind them.
        # Fill all necessary information:
        # For Bitcoin: bip32 derivations, witness script, redeem script
        # For Liquid: same + values, assets, commitments, proofs etc.
        # At the end we should have the most complete PSBT / PSET possible
        for i in range(psbtv.num_inputs):
            self.show_loader(title="Parsing input %d..." % i)
            # load input to memory, verify it (check prevtx hash)
            inp = psbtv.input(i)
            metainp = meta["inputs"][i]
            # verify, do not require non_witness_utxo if witness_utxo is set
            inp.verify(ignore_missing=True)

            # check sighash in the input
            if inp.sighash_type is not None and inp.sighash_type != self.DEFAULT_SIGHASH:
                metainp["sighash"] = self.get_sighash_info(inp.sighash_type)["name"]

            self.fill_zero_fingerprint(inp)

            # Find wallets owning the inputs and fill scope data:
            # first we check already detected wallet owns the input
            # as in most common case all inputs are owned by the same wallet.
            wallet = None
            gaps = None
            for w in wallets:
                # pass rangeproof offset if it's in the scope
                if w and w.fill_scope(inp, fingerprint):
                    wallet = w
                    break
            if wallet is None:
                # find wallet and append it to wallets
                for w in self.wallets:
                    # pass rangeproof offset if it's in the scope
                    if w.fill_scope(inp, fingerprint):
                        wallet = w
                        break
            if wallet:
                gaps = [g for g in wallet.gaps] # copy
                res = wallet.get_derivation(inp.bip32_derivations)
                if res:
                    idx, branch_idx = res
                    gaps[branch_idx] = max(gaps[branch_idx], idx+wallet.GAP_LIMIT+1)
            # add wallet to tx wallets dict
            if wallet not in wallets:
                wallets[wallet] = {"amount": 0, "gaps": gaps}
            else:
                if wallets[wallet]["gaps"] is not None and gaps is not None:
                    wallets[wallet]["gaps"] = [max(g1,g2) for g1,g2 in zip(gaps, wallets[wallet]["gaps"])]

            value = inp.utxo.value
            fee += value

            wallets[wallet]["amount"] = wallets.get(wallet, {}).get("amount") + value
            metainp.update({
                "label": wallet.name if wallet else "Unknown wallet",
                "value": value,
            })
            if wallet and wallet.is_watchonly:
                metainp["label"] += " (watch-only)"
            # write non_witness_utxo separately if it exists (as we use compressed psbtview)
            non_witness_utxo_off = None
            off = psbtv.seek_to_scope(i)
            non_witness_utxo_off = psbtv.seek_to_value(b'\x00', from_current=True)
            if non_witness_utxo_off:
                non_witness_utxo_off += off
                l = compact.read_from(psbtv.stream)
                fout.write(b"\x01\x00")
                fout.write(compact.to_bytes(l))
                read_write(psbtv.stream, fout, l)
            inp.write_to(fout, version=psbtv.version)

        # parse all outputs
        for i in range(psbtv.num_outputs):
            self.show_loader(title="Parsing output %d..." % i)
            out = psbtv.output(i)
            metaout = meta["outputs"][i]

            self.fill_zero_fingerprint(out)

            wallet = None
            for w in wallets:
                # pass rangeproof offset if it's in the scope
                if w and w.fill_scope(out, fingerprint):
                    wallet = w
                    break
            if wallet is None:
                # find wallet and append it to wallets
                for w in self.wallets:
                    if w.fill_scope(out, fingerprint):
                        wallet = w
                        break
            # Get values and store in metadata and wallets dict
            value = out.value
            fee -= value
            metaout.update({
                "change": (wallet is not None and len(wallets) == 1 and wallet in wallets),
                "value": value,
                "address": self.get_address(out),
            })
            if wallet:
                metaout["label"] = wallet.name
                res = wallet.get_derivation(out.bip32_derivations)
                if res:
                    idx, branch_idx = res
                    branch_txt = ""
                    if branch_idx == 1:
                        "change "
                    elif branch_idx > 1:
                        "branch %d " % branch_idx
                    metaout["label"] = "%s %s#%d" % (wallet.name, branch_txt, idx)
                    if wallet in wallets:
                        allowed_idx = wallets[wallet]["gaps"][branch_idx]
                    else:
                        allowed_idx = wallet.gaps[branch_idx]
                    if allowed_idx <= idx:
                        metaout["warning"] = "Derivation index is by %d larger than last known used index %d!" % (idx-allowed_idx+wallet.GAP_LIMIT, allowed_idx-wallet.GAP_LIMIT)
                if wallet.is_watchonly:
                    metaout["warning"] = "Watch-only wallet!"

            out.write_to(fout, version=psbtv.version)
        meta["fee"] = fee
        return wallets, meta