Пример #1
0
    async def mlsag_prehash_finalize(self):
        H = None
        proof = None
        message = None

        if self.options & IN_OPTION_MORE_COMMAND:
            H = self._fetch()
            self.ctx_h.update(H)
            self.ctx_commitment.update(H)
        else:
            H = self.ctx_commitment.digest()
            if not common.ct_equal(H, self.C):
                raise ValueError(SW_SECURITY_COMMITMENT_CHAIN_CONTROL)

            H = self.ctx_h.digest()
            message = self._fetch()
            proof = self._fetch()

            self.ctx_h = crypto.get_keccak()
            self.ctx_h.update(message)
            self.ctx_h.update(H)
            self.ctx_h.update(proof)
            self.H = self.ctx_h.digest()
            self._insert(self.H)
        return SW_OK
Пример #2
0
    async def hash_vini_pseudo_out(
        self, vini, inp_idx, pseudo_out=None, pseudo_out_hmac=None
    ):
        """
        Incremental hasing of tx.vin[i] and pseudo output
        :param vini:
        :param inp_idx:
        :param pseudo_out:
        :param pseudo_out_hmac:
        :return:
        """
        # Serialize particular input type
        from monero_serialize import xmrserialize
        from monero_serialize.xmrtypes import TxInV

        self.tx_prefix_hasher.refresh(xser=xmrserialize)

        await self.tx_prefix_hasher.ar.field(vini, TxInV)

        # Pseudo_out incremental hashing - applicable only in simple rct
        if not self.use_simple_rct:
            return

        if not self.in_memory():
            idx = self.source_permutation[inp_idx]
            pseudo_out_hmac_comp = crypto.compute_hmac(
                self.hmac_key_txin_comm(idx), pseudo_out
            )
            if not common.ct_equal(pseudo_out_hmac, pseudo_out_hmac_comp):
                raise ValueError("HMAC invalid for pseudo outs")
        else:
            pseudo_out = self.input_pseudo_outs[inp_idx]

        await self.full_message_hasher.set_pseudo_out(pseudo_out)
Пример #3
0
    async def mlsag_prehash_update(self):
        is_subaddress = 0
        Aout = None
        Bout = None
        C = None
        v = None
        k = None
        changed = 0

        is_subaddress = bool(self._fetch_u8())
        Aout = crypto.decodepoint(self._fetch())
        Bout = crypto.decodepoint(self._fetch())
        if self.sig_mode == TRANSACTION_CREATE_REAL:
            if crypto.point_eq(Aout, self.A) and crypto.point_eq(Bout, self.B):
                changed = 1

        AKout = self._fetch_decrypt()
        C = self._fetch()
        k = self._fetch()
        v = self._fetch()

        self.ctx_h.update(k)
        self.ctx_h.update(v)

        self.ctx_amount.update(AKout)

        v, k, AKout = self.unblind_int(v, k, AKout)
        self.ctx_amount.update(crypto.encodeint(k))
        self.ctx_amount.update(crypto.encodeint(v))

        vH = crypto.scalarmult_h(v)
        kG = crypto.scalarmult_base(k)
        k = crypto.point_add(kG, vH)

        if not crypto.point_eq(k, crypto.decodepoint(C)):
            raise ValueError(SW_SECURITY_COMMITMENT_CONTROL)

        self.ctx_commitment.update(C)
        if self.options & IN_OPTION_MORE_COMMAND == 0:
            k = self.ctx_amount.digest()
            if not common.ct_equal(k, self.KV):
                raise ValueError(SW_SECURITY_AMOUNT_CHAIN_CONTROL)

            self.C = self.ctx_commitment.digest()
            self.ctx_commitment = sha256()

        if self.sig_mode == TRANSACTION_CREATE_REAL:
            if not changed:
                await self._req_dst(Aout, Bout, v, is_subaddress)

        return SW_OK
Пример #4
0
    async def input_vini(self, src_entr, vini, hmac, pseudo_out, pseudo_out_hmac):
        """
        Set tx.vin[i] for incremental tx prefix hash computation.
        After sorting by key images on host.
        Hashes pseudo_out to the final_message.

        :param src_entr:
        :param vini: tx.vin[i]
        :param hmac: HMAC of tx.vin[i]
        :param pseudo_out: pseudo_out for the current entry
        :param pseudo_out_hmac: hmac of pseudo_out
        :return:
        """
        from monero_glue.messages.MoneroTransactionInputViniAck import (
            MoneroTransactionInputViniAck
        )

        await self.trezor.iface.transaction_step(
            self.STEP_VINI, self.inp_idx + 1, self.num_inputs()
        )

        if self.in_memory():
            return
        if self.inp_idx >= self.num_inputs():
            raise ValueError("Too many inputs")

        self.state.input_vins()
        self.inp_idx += 1

        # HMAC(T_in,i || vin_i)
        hmac_vini = await self.gen_hmac_vini(
            src_entr, vini, self.source_permutation[self.inp_idx]
        )
        if not common.ct_equal(hmac_vini, hmac):
            raise ValueError("HMAC is not correct")

        await self.hash_vini_pseudo_out(vini, self.inp_idx, pseudo_out, pseudo_out_hmac)
        return MoneroTransactionInputViniAck()
Пример #5
0
    async def sign_input(
        self,
        src_entr,
        vini,
        hmac_vini,
        pseudo_out,
        pseudo_out_hmac,
        alpha_enc,
        spend_enc,
    ):
        """
        Generates a signature for one input.

        :param src_entr: Source entry
        :type src_entr: monero_glue.xmr.serialize_messages.tx_construct.TxSourceEntry
        :param vini: tx.vin[i] for the transaction. Contains key image, offsets, amount (usually zero)
        :param hmac_vini: HMAC for the tx.vin[i] as returned from Trezor
        :param pseudo_out: pedersen commitment for the current input, uses alpha as the mask.
        Only in memory offloaded scenario. Tuple containing HMAC, as returned from the Trezor.
        :param pseudo_out_hmac:
        :param alpha_enc: alpha mask for the current input. Only in memory offloaded scenario,
        tuple as returned from the Trezor
        :param spend_enc:
        :return: Generated signature MGs[i]
        """
        self.state.set_signature()
        await self.trezor.iface.transaction_step(
            self.STEP_SIGN, self.inp_idx + 1, self.num_inputs()
        )

        self.inp_idx += 1
        if self.inp_idx >= self.num_inputs():
            raise ValueError("Invalid ins")
        if self.use_simple_rct and (not self.in_memory() and alpha_enc is None):
            raise ValueError("Inconsistent1")
        if self.use_simple_rct and (not self.in_memory() and pseudo_out is None):
            raise ValueError("Inconsistent2")
        if self.inp_idx >= 1 and not self.use_simple_rct:
            raise ValueError("Inconsistent3")

        inv_idx = self.source_permutation[self.inp_idx]

        # Check HMAC of all inputs
        hmac_vini_comp = await self.gen_hmac_vini(src_entr, vini, inv_idx)
        if not common.ct_equal(hmac_vini_comp, hmac_vini):
            raise ValueError("HMAC is not correct")

        gc.collect()
        self._log_trace(1)

        if self.use_simple_rct and not self.in_memory():
            pseudo_out_hmac_comp = crypto.compute_hmac(
                self.hmac_key_txin_comm(inv_idx), pseudo_out
            )
            if not common.ct_equal(pseudo_out_hmac_comp, pseudo_out_hmac):
                raise ValueError("HMAC is not correct")

            gc.collect()
            self._log_trace(2)

            from monero_glue.xmr.enc import chacha_poly

            alpha_c = crypto.decodeint(
                chacha_poly.decrypt_pack(
                    self.enc_key_txin_alpha(inv_idx), bytes(alpha_enc)
                )
            )
            pseudo_out_c = crypto.decodepoint(pseudo_out)

        elif self.use_simple_rct:
            alpha_c = self.input_alphas[self.inp_idx]
            pseudo_out_c = crypto.decodepoint(self.input_pseudo_outs[self.inp_idx])

        else:
            alpha_c = None
            pseudo_out_c = None

        # Spending secret
        if self.many_inputs():
            from monero_glue.xmr.enc import chacha_poly

            input_secret = crypto.decodeint(
                chacha_poly.decrypt_pack(self.enc_key_spend(inv_idx), bytes(spend_enc))
            )
        else:
            input_secret = self.input_secrets[self.inp_idx]

        gc.collect()
        self._log_trace(3)

        # Basic setup, sanity check
        index = src_entr.real_output
        in_sk = misc.StdObj(dest=input_secret, mask=crypto.decodeint(src_entr.mask))
        kLRki = src_entr.multisig_kLRki if self.multi_sig else None

        # Private key correctness test
        self.assrt(
            crypto.point_eq(
                crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].dest),
                crypto.scalarmult_base(in_sk.dest),
            ),
            "a1",
        )
        self.assrt(
            crypto.point_eq(
                crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].mask),
                crypto.gen_c(in_sk.mask, src_entr.amount),
            ),
            "a2",
        )

        gc.collect()
        self._log_trace(4)

        # RCT signature
        gc.collect()
        from monero_glue.xmr import mlsag2

        mg = None
        if self.use_simple_rct:
            # Simple RingCT
            mix_ring = [x[1] for x in src_entr.outputs]
            mg, msc = mlsag2.prove_rct_mg_simple(
                self.full_message,
                mix_ring,
                in_sk,
                alpha_c,
                pseudo_out_c,
                kLRki,
                None,
                index,
            )

            if __debug__:
                self.assrt(
                    mlsag2.ver_rct_mg_simple(
                        self.full_message, mg, mix_ring, pseudo_out_c
                    )
                )

        else:
            # Full RingCt, only one input
            txn_fee_key = crypto.scalarmult_h(self.get_fee())
            mix_ring = [[x[1]] for x in src_entr.outputs]
            mg, msc = mlsag2.prove_rct_mg(
                self.full_message,
                mix_ring,
                [in_sk],
                self.output_sk,
                self.output_pk,
                kLRki,
                None,
                index,
                txn_fee_key,
            )

            if __debug__:
                self.assrt(
                    mlsag2.ver_rct_mg(
                        mg, mix_ring, self.output_pk, txn_fee_key, self.full_message
                    )
                )

        gc.collect()
        self._log_trace(5)

        # Encode
        from monero_glue.xmr.sub.recode import recode_msg

        mgs = recode_msg([mg])
        cout = None

        gc.collect()
        self._log_trace(6)

        # Multisig values returned encrypted, keys returned after finished successfully.
        if self.multi_sig:
            from monero_glue.xmr.enc import chacha_poly

            cout = chacha_poly.encrypt_pack(self.enc_key_cout(), crypto.encodeint(msc))

        # Final state transition
        if self.inp_idx + 1 == self.num_inputs():
            self.state.set_signature_done()
            await self.trezor.iface.transaction_signed()

        gc.collect()
        self._log_trace()

        from monero_glue.messages.MoneroTransactionSignInputAck import (
            MoneroTransactionSignInputAck
        )

        return MoneroTransactionSignInputAck(
            signature=await misc.dump_msg_gc(mgs[0], preallocate=488, del_msg=True),
            cout=cout,
        )
Пример #6
0
    async def all_out1_set(self):
        """
        All outputs were set in this phase. Computes additional public keys (if needed), tx.extra and
        transaction prefix hash.
        Adds additional public keys to the tx.extra

        :return: tx.extra, tx_prefix_hash
        """
        self._log_trace(0)
        self.state.set_output_done()
        await self.trezor.iface.transaction_step(self.STEP_ALL_OUT)
        self._log_trace(1)

        if self.out_idx + 1 != self.num_dests():
            raise ValueError("Invalid out num")

        # Test if \sum Alpha == \sum A
        if self.use_simple_rct:
            self.assrt(crypto.sc_eq(self.sumout, self.sumpouts_alphas))

        # Fee test
        if self.fee != (self.summary_inputs_money - self.summary_outs_money):
            raise ValueError(
                "Fee invalid %s vs %s, out: %s"
                % (
                    self.fee,
                    self.summary_inputs_money - self.summary_outs_money,
                    self.summary_outs_money,
                )
            )
        self._log_trace(2)

        # Set public key to the extra
        # Not needed to remove - extra is clean
        await self.all_out1_set_tx_extra()
        self.additional_tx_public_keys = None

        gc.collect()
        self._log_trace(3)

        if self.summary_outs_money > self.summary_inputs_money:
            raise ValueError(
                "Transaction inputs money (%s) less than outputs money (%s)"
                % (self.summary_inputs_money, self.summary_outs_money)
            )

        # Hashing transaction prefix
        await self.all_out1_set_tx_prefix()
        extra_b = self.tx.extra
        self.tx = None
        gc.collect()
        self._log_trace(4)

        # Txprefix match check for multisig
        if not common.is_empty(self.exp_tx_prefix_hash) and not common.ct_equal(
            self.exp_tx_prefix_hash, self.tx_prefix_hash
        ):
            self.state.set_fail()
            raise misc.TrezorTxPrefixHashNotMatchingError("Tx prefix invalid")

        gc.collect()
        self._log_trace(5)

        from monero_glue.messages.MoneroRingCtSig import MoneroRingCtSig
        from monero_glue.messages.MoneroTransactionAllOutSetAck import (
            MoneroTransactionAllOutSetAck
        )

        rv = self.init_rct_sig()
        rv_pb = MoneroRingCtSig(txn_fee=rv.txnFee, message=rv.message, rv_type=rv.type)
        return MoneroTransactionAllOutSetAck(
            extra=extra_b, tx_prefix_hash=self.tx_prefix_hash, rv=rv_pb
        )
Пример #7
0
    async def set_out1(self, dst_entr, dst_entr_hmac):
        """
        Set destination entry one by one.
        Computes destination stealth address, amount key, range proof + HMAC, out_pk, ecdh_info.

        :param dst_entr
        :type dst_entr: TxDestinationEntry
        :param dst_entr_hmac
        :return:
        """
        from monero_serialize import xmrserialize

        await self.trezor.iface.transaction_step(
            self.STEP_OUT, self.out_idx + 1, self.num_dests()
        )
        self._log_trace(1)

        if self.state.is_input_vins() and self.inp_idx + 1 != self.num_inputs():
            raise ValueError("Invalid number of inputs")

        self.state.set_output()
        self.out_idx += 1
        self._log_trace(2)

        if dst_entr.amount <= 0 and self.tx.version <= 1:
            raise ValueError("Destination with wrong amount: %s" % dst_entr.amount)

        # HMAC check of the destination
        dst_entr_hmac_computed = await self.gen_hmac_tsxdest(dst_entr, self.out_idx)
        if not common.ct_equal(dst_entr_hmac, dst_entr_hmac_computed):
            raise ValueError("HMAC invalid")
        gc.collect()
        self._log_trace(3)

        # First output - tx prefix hasher - size of the container
        self.tx_prefix_hasher.refresh(xser=xmrserialize)
        if self.out_idx == 0:
            await self._set_out1_prefix()
        gc.collect()

        self._log_trace(4)
        additional_txkey_priv = await self._set_out1_additional_keys(dst_entr)
        derivation = await self._set_out1_derivation(dst_entr, additional_txkey_priv)

        gc.collect()
        self._log_trace(5)

        amount_key = crypto.derivation_to_scalar(derivation, self.out_idx)
        tx_out_key = crypto.derive_public_key(
            derivation,
            self.out_idx,
            crypto.decodepoint(dst_entr.addr.m_spend_public_key),
        )

        from monero_serialize.xmrtypes import TxoutToKey
        from monero_serialize.xmrtypes import TxOut

        tk = TxoutToKey(key=crypto.encodepoint(tx_out_key))
        tx_out = TxOut(amount=0, target=tk)
        self.summary_outs_money += dst_entr.amount
        self._log_trace(6)

        # Tx header prefix hashing
        await self.tx_prefix_hasher.ar.field(tx_out, TxOut)
        gc.collect()

        # Hmac dest_entr.
        hmac_vouti = await self.gen_hmac_vouti(dst_entr, tx_out, self.out_idx)
        gc.collect()
        self._log_trace(7)

        # Range proof, out_pk, ecdh_info
        rsig, out_pk, ecdh_info = await self.range_proof(
            self.out_idx,
            dest_pub_key=tk.key,
            amount=dst_entr.amount,
            amount_key=amount_key,
        )
        gc.collect()
        self._log_trace(8)

        # Incremental hashing of the ECDH info.
        # RctSigBase allows to hash only one of the (ecdh, out_pk) as they are serialized
        # as whole vectors. Hashing ECDH info saves state space.
        await self.full_message_hasher.set_ecdh(ecdh_info)
        self._log_trace(9)

        # Output_pk is stored to the state as it is used during the signature and hashed to the
        # RctSigBase later.
        self.output_pk.append(out_pk)
        gc.collect()

        self._log_trace(10)
        from monero_glue.messages.MoneroTransactionSetOutputAck import (
            MoneroTransactionSetOutputAck
        )
        from monero_serialize.xmrtypes import CtKey

        return MoneroTransactionSetOutputAck(
            tx_out=await misc.dump_msg(tx_out, preallocate=34),
            vouti_hmac=hmac_vouti,
            rsig=rsig,  # rsig is already byte-encoded
            out_pk=await misc.dump_msg(out_pk, preallocate=64, msg_type=CtKey),
            ecdh_info=await misc.dump_msg(ecdh_info, preallocate=64),
        )
Пример #8
0
    async def sign_transaction_data(self,
                                    tx,
                                    multisig=False,
                                    exp_tx_prefix_hash=None,
                                    use_tx_keys=None):
        """
        Uses Trezor to sign the transaction
        :param tx:
        :type tx: xmrtypes.TxConstructionData
        :param multisig:
        :param exp_tx_prefix_hash:
        :param use_tx_keys:
        :return:
        """
        self.ct = TData()
        self.ct.tx_data = tx

        payment_id = []
        extras = await monero.parse_extra_fields(tx.extra)
        extra_nonce = monero.find_tx_extra_field_by_type(
            extras, xmrtypes.TxExtraNonce)
        if extra_nonce and monero.has_encrypted_payment_id(extra_nonce.nonce):
            payment_id = monero.get_encrypted_payment_id_from_tx_extra_nonce(
                extra_nonce.nonce)
        elif extra_nonce and monero.has_payment_id(extra_nonce.nonce):
            payment_id = monero.get_payment_id_from_tx_extra_nonce(
                extra_nonce.nonce)

        # Init transaction
        tsx_data = TsxData()
        tsx_data.version = 1
        tsx_data.payment_id = payment_id
        tsx_data.unlock_time = tx.unlock_time
        tsx_data.outputs = tx.splitted_dsts
        tsx_data.change_dts = tx.change_dts
        tsx_data.num_inputs = len(tx.sources)
        tsx_data.mixin = len(tx.sources[0].outputs)
        tsx_data.fee = sum([x.amount for x in tx.sources]) - sum(
            [x.amount for x in tx.splitted_dsts])
        tsx_data.account = tx.subaddr_account
        tsx_data.minor_indices = tx.subaddr_indices
        tsx_data.is_multisig = multisig
        tsx_data.is_bulletproof = False
        tsx_data.exp_tx_prefix_hash = common.defval(exp_tx_prefix_hash, b"")
        tsx_data.use_tx_keys = common.defval(use_tx_keys, [])
        self.ct.tx.unlock_time = tx.unlock_time

        self.ct.tsx_data = tsx_data
        tsx_data_pb = await tmisc.translate_tsx_data_pb(tsx_data)
        init_msg = MoneroTransactionInitRequest(
            version=0,
            address_n=self.address_n,
            network_type=self.network_type,
            tsx_data=tsx_data_pb,
        )

        t_res = await self.trezor.tsx_sign(
            MoneroTransactionSignRequest(init=init_msg)
        )  # type: MoneroTransactionInitAck
        self.handle_error(t_res)

        in_memory = t_res.in_memory
        self.ct.tx_out_entr_hmacs = t_res.hmacs

        # Set transaction inputs
        for idx, src in enumerate(tx.sources):
            src_bin = await tmisc.dump_msg(src)
            msg = MoneroTransactionSetInputRequest(src_entr=src_bin)

            t_res = await self.trezor.tsx_sign(
                MoneroTransactionSignRequest(set_input=msg)
            )  # type: MoneroTransactionSetInputAck
            self.handle_error(t_res)

            vini = await tmisc.parse_msg(t_res.vini, xmrtypes.TxinToKey())
            self.ct.tx.vin.append(vini)
            self.ct.tx_in_hmacs.append(t_res.vini_hmac)
            self.ct.pseudo_outs.append(
                (t_res.pseudo_out, t_res.pseudo_out_hmac))
            self.ct.alphas.append(t_res.alpha_enc)
            self.ct.spend_encs.append(t_res.spend_enc)

        # Sort key image
        self.ct.source_permutation = list(range(len(tx.sources)))
        self.ct.source_permutation.sort(
            key=lambda x: self.ct.tx.vin[x].k_image, reverse=True)

        def swapper(x, y):
            self.ct.tx.vin[x], self.ct.tx.vin[y] = self.ct.tx.vin[
                y], self.ct.tx.vin[x]
            self.ct.tx_in_hmacs[x], self.ct.tx_in_hmacs[y] = (
                self.ct.tx_in_hmacs[y],
                self.ct.tx_in_hmacs[x],
            )
            self.ct.pseudo_outs[x], self.ct.pseudo_outs[y] = (
                self.ct.pseudo_outs[y],
                self.ct.pseudo_outs[x],
            )
            self.ct.alphas[x], self.ct.alphas[y] = self.ct.alphas[
                y], self.ct.alphas[x]
            self.ct.spend_encs[x], self.ct.spend_encs[y] = (
                self.ct.spend_encs[y],
                self.ct.spend_encs[x],
            )
            tx.sources[x], tx.sources[y] = tx.sources[y], tx.sources[x]

        common.apply_permutation(self.ct.source_permutation, swapper)

        if not in_memory:
            msg = MoneroTransactionInputsPermutationRequest(
                perm=self.ct.source_permutation)
            t_res = await self.trezor.tsx_sign(
                MoneroTransactionSignRequest(input_permutation=msg))
            self.handle_error(t_res)

        # Set vin_i back - tx prefix hashing
        # Done only if not in-memory.
        if not in_memory:
            for idx in range(len(self.ct.tx.vin)):
                msg = MoneroTransactionInputViniRequest(
                    src_entr=await tmisc.dump_msg(tx.sources[idx]),
                    vini=await tmisc.dump_msg(self.ct.tx.vin[idx]),
                    vini_hmac=self.ct.tx_in_hmacs[idx],
                    pseudo_out=self.ct.pseudo_outs[idx][0]
                    if not in_memory else None,
                    pseudo_out_hmac=self.ct.pseudo_outs[idx][1]
                    if not in_memory else None,
                )
                t_res = await self.trezor.tsx_sign(
                    MoneroTransactionSignRequest(input_vini=msg))
                self.handle_error(t_res)

        # Set transaction outputs
        for idx, dst in enumerate(tx.splitted_dsts):
            msg = MoneroTransactionSetOutputRequest(
                dst_entr=await tmisc.dump_msg(dst),
                dst_entr_hmac=self.ct.tx_out_entr_hmacs[idx],
            )
            t_res = await self.trezor.tsx_sign(
                MoneroTransactionSignRequest(set_output=msg)
            )  # type: MoneroTransactionSetOutputAck
            self.handle_error(t_res)

            self.ct.tx.vout.append(await
                                   tmisc.parse_msg(t_res.tx_out,
                                                   xmrtypes.TxOut()))
            self.ct.tx_out_hmacs.append(t_res.vouti_hmac)
            self.ct.tx_out_rsigs.append(await tmisc.parse_msg(
                t_res.rsig, xmrtypes.RangeSig()))
            self.ct.tx_out_pk.append(await
                                     tmisc.parse_msg(t_res.out_pk,
                                                     xmrtypes.CtKey()))
            self.ct.tx_out_ecdh.append(await
                                       tmisc.parse_msg(t_res.ecdh_info,
                                                       xmrtypes.EcdhTuple()))

            # Rsig verification
            try:
                rsig = self.ct.tx_out_rsigs[-1]
                if not ring_ct.ver_range(C=None, rsig=rsig):
                    logger.warning("Rsing not valid")

            except Exception as e:
                logger.error("Exception rsig: %s" % e)
                traceback.print_exc()

        t_res = await self.trezor.tsx_sign(
            MoneroTransactionSignRequest(
                all_out_set=MoneroTransactionAllOutSetRequest())
        )  # type: MoneroTransactionAllOutSetAck
        self.handle_error(t_res)

        rv = xmrtypes.RctSig()
        rv.p = xmrtypes.RctSigPrunable()
        rv.txnFee = t_res.rv.txn_fee
        rv.message = t_res.rv.message
        rv.type = t_res.rv.rv_type
        self.ct.tx.extra = list(bytearray(t_res.extra))

        # Verify transaction prefix hash correctness, tx hash in one pass
        self.ct.tx_prefix_hash = await monero.get_transaction_prefix_hash(
            self.ct.tx)
        if not common.ct_equal(t_res.tx_prefix_hash, self.ct.tx_prefix_hash):
            raise ValueError("Transaction prefix has does not match")

        # RctSig
        if self.is_simple(rv):
            if self.is_bulletproof(rv):
                rv.p.pseudoOuts = [x[0] for x in self.ct.pseudo_outs]
            else:
                rv.pseudoOuts = [x[0] for x in self.ct.pseudo_outs]

        # Range proof
        rv.p.rangeSigs = []
        rv.outPk = []
        rv.ecdhInfo = []
        for idx in range(len(self.ct.tx_out_rsigs)):
            rv.p.rangeSigs.append(self.ct.tx_out_rsigs[idx])
            rv.outPk.append(self.ct.tx_out_pk[idx])
            rv.ecdhInfo.append(self.ct.tx_out_ecdh[idx])

        # MLSAG message check
        t_res = await self.trezor.tsx_sign(
            MoneroTransactionSignRequest(
                mlsag_done=MoneroTransactionMlsagDoneRequest())
        )  # type: MoneroTransactionMlsagDoneAck
        self.handle_error(t_res)

        mlsag_hash = t_res.full_message_hash
        mlsag_hash_computed = await monero.get_pre_mlsag_hash(rv)
        if not common.ct_equal(mlsag_hash, mlsag_hash_computed):
            raise ValueError("Pre MLSAG hash has does not match")

        # Sign each input
        couts = []
        rv.p.MGs = []
        for idx, src in enumerate(tx.sources):
            msg = MoneroTransactionSignInputRequest(
                await tmisc.dump_msg(src),
                await tmisc.dump_msg(self.ct.tx.vin[idx]),
                self.ct.tx_in_hmacs[idx],
                self.ct.pseudo_outs[idx][0] if not in_memory else None,
                self.ct.pseudo_outs[idx][1] if not in_memory else None,
                self.ct.alphas[idx],
                self.ct.spend_encs[idx],
            )
            t_res = await self.trezor.tsx_sign(
                MoneroTransactionSignRequest(sign_input=msg)
            )  # type: MoneroTransactionSignInputAck
            self.handle_error(t_res)

            mg = await tmisc.parse_msg(t_res.signature, xmrtypes.MgSig())
            rv.p.MGs.append(mg)
            couts.append(t_res.cout)

        self.ct.tx.signatures = []
        self.ct.tx.rct_signatures = rv

        t_res = await self.trezor.tsx_sign(
            MoneroTransactionSignRequest(
                final_msg=MoneroTransactionFinalRequest())
        )  # type: MoneroTransactionFinalAck
        self.handle_error(t_res)

        if multisig:
            cout_key = t_res.cout_key
            for ccout in couts:
                self.ct.couts.append(
                    chacha_poly.decrypt(cout_key, ccout[0], ccout[1],
                                        ccout[2]))

        self.ct.enc_salt1, self.ct.enc_salt2 = t_res.salt, t_res.rand_mult
        self.ct.enc_keys = t_res.tx_enc_keys
        return self.ct.tx
Пример #9
0
    async def sign_transaction_data(
        self,
        tx,
        multisig=False,
        exp_tx_prefix_hash=None,
        use_tx_keys=None,
        aux_data=None,
    ):
        """
        Uses Trezor to sign the transaction
        :param tx:
        :type tx: xmrtypes.TxConstructionData
        :param multisig:
        :param exp_tx_prefix_hash:
        :param use_tx_keys:
        :param aux_data:
        :return:
        """
        self.ct = TData()
        self.ct.tx_data = tx

        payment_id = []
        extras = await monero.parse_extra_fields(tx.extra)
        extra_nonce = monero.find_tx_extra_field_by_type(
            extras, xmrtypes.TxExtraNonce)
        if extra_nonce and monero.has_encrypted_payment_id(extra_nonce.nonce):
            payment_id = monero.get_encrypted_payment_id_from_tx_extra_nonce(
                extra_nonce.nonce)
        elif extra_nonce and monero.has_payment_id(extra_nonce.nonce):
            payment_id = monero.get_payment_id_from_tx_extra_nonce(
                extra_nonce.nonce)

        # Init transaction
        tsx_data = MoneroTransactionData()
        tsx_data.version = 1
        tsx_data.client_version = self.client_version
        tsx_data.payment_id = payment_id
        tsx_data.unlock_time = tx.unlock_time
        tsx_data.outputs = [
            tmisc.translate_monero_dest_entry_pb(x) for x in tx.splitted_dsts
        ]
        tsx_data.change_dts = tmisc.translate_monero_dest_entry_pb(
            tx.change_dts)
        tsx_data.num_inputs = len(tx.sources)
        tsx_data.mixin = len(tx.sources[0].outputs)
        tsx_data.fee = sum([x.amount for x in tx.sources]) - sum(
            [x.amount for x in tx.splitted_dsts])
        tsx_data.account = tx.subaddr_account
        tsx_data.minor_indices = tx.subaddr_indices
        tsx_data.integrated_indices = self._get_integrated_idx(
            tsx_data.outputs, aux_data)
        self.ct.tx.unlock_time = tx.unlock_time

        # Rsig
        num_outputs = len(tsx_data.outputs)
        use_bp = common.defattr(tx, "use_bulletproofs", False)
        self.ct.rsig_type = get_rsig_type(use_bp, num_outputs)
        self.ct.rsig_batches = generate_rsig_batch_sizes(
            self.ct.rsig_type, num_outputs)
        rsig_data = MoneroTransactionRsigData(rsig_type=self.ct.rsig_type,
                                              grouping=self.ct.rsig_batches)
        tsx_data.rsig_data = rsig_data

        if self.hf >= 10:
            tsx_data.rsig_data.bp_version = 2

        self.ct.tsx_data = tsx_data
        init_msg = MoneroTransactionInitRequest(
            version=0,
            address_n=self.address_n,
            network_type=self.network_type,
            tsx_data=tsx_data,
        )

        t_res = await self.trezor.tsx_sign(init_msg
                                           )  # type: MoneroTransactionInitAck
        self.handle_error(t_res)

        self.ct.tx_out_entr_hmacs = t_res.hmacs
        self.ct.rsig_param = t_res.rsig_data

        # Set transaction inputs
        for idx, src in enumerate(tx.sources):
            src_pb = tmisc.translate_monero_src_entry_pb(src)
            msg = MoneroTransactionSetInputRequest(src_entr=src_pb)

            t_res = await self.trezor.tsx_sign(
                msg)  # type: MoneroTransactionSetInputAck
            self.handle_error(t_res)

            vini_p = await tmisc.parse_msg(t_res.vini, xmrtypes.TxInV())
            vini = vini_p.txin_to_key
            self.ct.tx.vin.append(vini)
            self.ct.tx_in_hmacs.append(t_res.vini_hmac)
            self.ct.pseudo_outs.append(
                (t_res.pseudo_out, t_res.pseudo_out_hmac))
            self.ct.alphas.append(t_res.pseudo_out_alpha)
            self.ct.spend_encs.append(t_res.spend_key)

        # Sort key image
        self.ct.source_permutation = list(range(len(tx.sources)))
        self.ct.source_permutation.sort(
            key=lambda x: self.ct.tx.vin[x].k_image, reverse=True)

        def swapper(x, y):
            self.ct.tx.vin[x], self.ct.tx.vin[y] = self.ct.tx.vin[
                y], self.ct.tx.vin[x]
            self.ct.tx_in_hmacs[x], self.ct.tx_in_hmacs[y] = (
                self.ct.tx_in_hmacs[y],
                self.ct.tx_in_hmacs[x],
            )
            self.ct.pseudo_outs[x], self.ct.pseudo_outs[y] = (
                self.ct.pseudo_outs[y],
                self.ct.pseudo_outs[x],
            )
            self.ct.alphas[x], self.ct.alphas[y] = self.ct.alphas[
                y], self.ct.alphas[x]
            self.ct.spend_encs[x], self.ct.spend_encs[y] = (
                self.ct.spend_encs[y],
                self.ct.spend_encs[x],
            )
            tx.sources[x], tx.sources[y] = tx.sources[y], tx.sources[x]

        common.apply_permutation(self.ct.source_permutation, swapper)

        msg = MoneroTransactionInputsPermutationRequest(
            perm=self.ct.source_permutation)
        t_res = await self.trezor.tsx_sign(msg)
        self.handle_error(t_res)

        # Set vin_i back - tx prefix hashing
        for idx in range(len(self.ct.tx.vin)):
            src_pb = tmisc.translate_monero_src_entry_pb(tx.sources[idx])
            msg = MoneroTransactionInputViniRequest(
                src_entr=src_pb,
                vini=await tmisc.dump_msg(self.ct.tx.vin[idx], prefix=b"\x02"),
                vini_hmac=self.ct.tx_in_hmacs[idx],
                orig_idx=self.ct.source_permutation[idx],
            )
            t_res = await self.trezor.tsx_sign(msg)
            self.handle_error(t_res)

        # All inputs set
        t_res = await self.trezor.tsx_sign(
            MoneroTransactionAllInputsSetRequest()
        )  # type: MoneroTransactionAllInputsSetAck
        self.handle_error(t_res)
        await self._on_all_input_set(t_res)

        # Set transaction outputs
        for idx, dst in enumerate(tx.splitted_dsts):
            msg = await self._step_set_outputs(idx, dst)

            t_res = await self.trezor.tsx_sign(
                msg)  # type: MoneroTransactionSetOutputAck
            self.handle_error(t_res)

            await self._on_set_outputs_ack(idx, dst, t_res)

        t_res = await self.trezor.tsx_sign(
            MoneroTransactionAllOutSetRequest()
        )  # type: MoneroTransactionAllOutSetAck
        self.handle_error(t_res)

        rv = xmrtypes.RctSig()
        rv.p = xmrtypes.RctSigPrunable()
        rv.txnFee = t_res.rv.txn_fee
        rv.message = t_res.rv.message
        rv.type = t_res.rv.rv_type
        self.ct.tx.extra = list(bytearray(t_res.extra))

        # Verify transaction prefix hash correctness, tx hash in one pass
        self.ct.tx_prefix_hash = await monero.get_transaction_prefix_hash(
            self.ct.tx)
        if not common.ct_equal(t_res.tx_prefix_hash, self.ct.tx_prefix_hash):
            raise ValueError("Transaction prefix has does not match")

        # RctSig
        if self.is_simple(rv):
            if self.is_bulletproof(rv):
                rv.p.pseudoOuts = [x[0] for x in self.ct.pseudo_outs]
            else:
                rv.pseudoOuts = [x[0] for x in self.ct.pseudo_outs]

        # Range proof
        rv.p.rangeSigs = []
        rv.p.bulletproofs = []
        rv.outPk = []
        rv.ecdhInfo = []
        for idx in range(len(self.ct.tx_out_pk)):
            rv.outPk.append(self.ct.tx_out_pk[idx])
            rv.ecdhInfo.append(self.ct.tx_out_ecdh[idx])

        for idx in range(len(self.ct.tx_out_rsigs)):
            if self.is_bulletproof(rv):
                rv.p.bulletproofs.append(self.ct.tx_out_rsigs[idx])
            else:
                rv.p.rangeSigs.append(self.ct.tx_out_rsigs[idx])

        mlsag_hash = t_res.full_message_hash
        mlsag_hash_computed = await monero.get_pre_mlsag_hash(rv)
        if not common.ct_equal(mlsag_hash, mlsag_hash_computed):
            raise ValueError("Pre MLSAG hash has does not match")

        # Sign each input
        couts = []
        rv.p.MGs = []
        for idx, src in enumerate(tx.sources):
            msg = MoneroTransactionSignInputRequest(
                src_entr=tmisc.translate_monero_src_entry_pb(src),
                vini=await tmisc.dump_msg(self.ct.tx.vin[idx], prefix=b"\x02"),
                vini_hmac=self.ct.tx_in_hmacs[idx],
                pseudo_out=self.ct.pseudo_outs[idx][0],
                pseudo_out_hmac=self.ct.pseudo_outs[idx][1],
                pseudo_out_alpha=self.ct.alphas[idx],
                spend_key=self.ct.spend_encs[idx],
                orig_idx=self.ct.source_permutation[idx],
            )
            t_res = await self.trezor.tsx_sign(
                msg)  # type: MoneroTransactionSignInputAck
            self.handle_error(t_res)

            if self.client_version > 0 and t_res.pseudo_out:
                self.ct.pseudo_outs[
                    idx] = t_res.pseudo_out, self.ct.pseudo_outs[idx][1]
                if self.is_bulletproof(rv):
                    rv.p.pseudoOuts[idx] = t_res.pseudo_out
                else:
                    rv.pseudoOuts[idx] = t_res.pseudo_out

            mg = await tmisc.parse_msg(t_res.signature, xmrtypes.MgSig())
            rv.p.MGs.append(mg)
            couts.append(None)

        self.ct.tx.signatures = []
        self.ct.tx.rct_signatures = rv

        t_res = await self.trezor.tsx_sign(MoneroTransactionFinalRequest()
                                           )  # type: MoneroTransactionFinalAck
        self.handle_error(t_res)

        if multisig:
            cout_key = t_res.cout_key
            for ccout in couts:
                self.ct.couts.append(chacha_poly.decrypt_pack(cout_key, ccout))

        self.ct.enc_salt1, self.ct.enc_salt2 = t_res.salt, t_res.rand_mult
        self.ct.enc_keys = t_res.tx_enc_keys
        return self.ct.tx