Пример #1
0
    async def live_refresh(self, view_key_private, out_key, recv_deriv,
                           real_out_idx, major, minor):
        msg = MoneroLiveRefreshStepRequest(
            out_key=out_key,
            recv_deriv=recv_deriv,
            real_out_idx=real_out_idx,
            sub_addr_major=major,
            sub_addr_minor=minor,
        )
        t_res = await self.trezor.live_refresh(
            msg)  # type: MoneroLiveRefreshStepAck
        self.handle_error(t_res)

        enc_key = self._compute_ki_enc_key_host(view_key_private, out_key,
                                                t_res.salt)
        decr = chacha_poly.decrypt_pack(enc_key, t_res.key_image)

        ki_bin = decr[:32]
        ki = crypto.decodepoint(ki_bin)
        sig = [[crypto.decodeint(decr[32:64]), crypto.decodeint(decr[64:])]]

        if not ring_ct.check_ring_singature(
                ki_bin, ki, [crypto.decodepoint(out_key)], sig):
            raise ValueError("Invalid ring sig on KI")

        return ki
Пример #2
0
    async def _get_tx_key_intern(
        self,
        salt1,
        salt2,
        tx_enc_keys,
        tx_prefix_hash,
        view_key_priv,
        view_public_key=None,
        reason=0,
    ):
        msg = MoneroGetTxKeyRequest(address_n=self.address_n,
                                    network_type=self.network_type)
        msg.salt1 = salt1
        msg.salt2 = salt2
        msg.tx_enc_keys = tx_enc_keys
        msg.tx_prefix_hash = tx_prefix_hash
        msg.reason = reason
        msg.view_public_key = view_public_key
        t_res = await self.trezor.get_tx_key(msg)  # type: MoneroGetTxKeyAck
        self.handle_error(t_res)

        enc_key = self._compute_tx_key_host(view_key_priv, tx_prefix_hash,
                                            t_res.salt)
        decr = chacha_poly.decrypt_pack(
            enc_key, t_res.tx_keys if reason == 0 else t_res.tx_derivations)
        if len(decr) % 32 != 0:
            raise ValueError("Invalid length")
        return bytearray(decr)
Пример #3
0
    async def open_account_file(self, file):
        """
        Opens account file
        :param file:
        :return:
        """
        with open(file) as fh:
            js = json.load(fh)

        # Wallet key encryption
        self.wallet_password = await self.prompt_password()
        self.wallet_salt = common.defvalkey(js, "wallet_salt")
        if self.wallet_salt is None:
            self.wallet_salt = crypto.random_bytes(32)
        else:
            self.wallet_salt = binascii.unhexlify(self.wallet_salt)

        # Wallet view key dec.
        if "view_key" in js:
            self.priv_view = crypto.b16_to_scalar(
                js["view_key"].encode("utf8"))

        elif "view_key_enc" in js:
            wallet_enc_key = misc.wallet_enc_key(self.wallet_salt,
                                                 self.wallet_password)
            plain = chacha_poly.decrypt_pack(
                wallet_enc_key, binascii.unhexlify(js["view_key_enc"]))
            self.priv_view = crypto.decodeint(plain)

        self.address = js["address"].encode("utf8")
        self.wallet_file = js["wallet_file"]
        self.monero_bin = js["monero_bin"]
        self.set_network_type(js["network_type"])
        self.rpc_addr = js["rpc_addr"]

        await self.open_with_keys(self.priv_view, self.address)
Пример #4
0
    async def verify(self, tx, con_data=None, creds=None):
        """
        Transaction verification
        :param tx:
        :param con_data:
        :param creds:
        :return:
        """

        # Unserialize the transaction
        tx_obj = xmrtypes.Transaction()
        reader = xmrserialize.MemoryReaderWriter(bytearray(tx))
        ar1 = xmrserialize.Archive(reader, False)

        await ar1.message(tx_obj, msg_type=xmrtypes.Transaction)
        extras = await monero.parse_extra_fields(tx_obj.extra)
        monero.expand_transaction(tx_obj)

        tx_pub = crypto.decodepoint(monero.find_tx_extra_field_by_type(
            extras, xmrtypes.TxExtraPubKey
        ).pub_key)

        additional_pub_keys = monero.find_tx_extra_field_by_type(
            extras, xmrtypes.TxExtraAdditionalPubKeys
        )
        additional_pub_keys = [crypto.decodepoint(x) for x in additional_pub_keys.data] if additional_pub_keys is not None else None

        # Verify range proofs
        out_idx = 0
        is_bp = tx_obj.rct_signatures.type in [RctType.SimpleBulletproof, RctType.FullBulletproof]
        if not is_bp:
            for idx, rsig in enumerate(tx_obj.rct_signatures.p.rangeSigs):
                out_pk = tx_obj.rct_signatures.outPk[idx]
                C = crypto.decodepoint(out_pk.mask)
                rsig = tx_obj.rct_signatures.p.rangeSigs[idx]
                res = ring_ct.ver_range(C, rsig, use_bulletproof=is_bp)
                self.assertTrue(res)

        else:
            for idx, rsig in enumerate(tx_obj.rct_signatures.p.bulletproofs):
                rsig_num_outs = min(len(tx_obj.rct_signatures.outPk), 1 << (len(rsig.L) - 6))
                outs = tx_obj.rct_signatures.outPk[out_idx : out_idx + rsig_num_outs]
                rsig.V = [crypto.encodepoint(ring_ct.bp_comm_to_v(crypto.decodepoint(xx.mask))) for xx in outs]
                res = ring_ct.ver_range(None, rsig, use_bulletproof=is_bp)
                self.assertTrue(res)

        # Prefix hash
        prefix_hash = await monero.get_transaction_prefix_hash(tx_obj)
        is_simple = len(tx_obj.vin) > 1 or is_bp

        self.assertEqual(prefix_hash, con_data.tx_prefix_hash)
        tx_obj.rct_signatures.message = prefix_hash

        # MLSAG hash
        mlsag_hash = await monero.get_pre_mlsag_hash(tx_obj.rct_signatures)

        # Decrypt transaction key
        tx_key = misc.compute_tx_key(creds.spend_key_private, prefix_hash, salt=con_data.enc_salt1, rand_mult=con_data.enc_salt2)[0]
        key_buff = chacha_poly.decrypt_pack(tx_key, con_data.enc_keys)

        tx_priv_keys = [crypto.decodeint(x) for x in common.chunk(key_buff, 32) if x]
        tx_priv = tx_priv_keys[0]
        tx_additional_priv = tx_priv_keys[1:]

        # Verify mlsag signature
        monero.recode_msg(tx_obj.rct_signatures.p.MGs, encode=False)
        for idx in range(len(tx_obj.vin)):
            if is_simple:
                mix_ring = [MoneroRctKeyPublic(dest=x[1].dest, commitment=x[1].mask) for x in con_data.tx_data.sources[idx].outputs]
                if is_bp:
                    pseudo_out = crypto.decodepoint(bytes(tx_obj.rct_signatures.p.pseudoOuts[idx]))
                else:
                    pseudo_out = crypto.decodepoint(bytes(tx_obj.rct_signatures.pseudoOuts[idx]))
                self.assertTrue(mlsag2.ver_rct_mg_simple(
                    mlsag_hash, tx_obj.rct_signatures.p.MGs[idx], mix_ring, pseudo_out
                ))

            else:
                txn_fee_key = crypto.scalarmult_h(tx_obj.rct_signatures.txnFee)
                mix_ring = [[MoneroRctKeyPublic(dest=x[1].dest, commitment=x[1].mask)] for x in con_data.tx_data.sources[idx].outputs]
                self.assertTrue(mlsag2.ver_rct_mg(
                    tx_obj.rct_signatures.p.MGs[idx], mix_ring, tx_obj.rct_signatures.outPk, txn_fee_key, mlsag_hash
                ))
Пример #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 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