Exemplo n.º 1
0
    async def _on_set_outputs_ack(self, idx, dst, 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_pk.append(await tmisc.parse_msg(t_res.out_pk,
                                                       xmrtypes.CtKey()))

        # hf10 encodes only 8B amount
        if self.hf == 9:
            self.ct.tx_out_ecdh.append(await
                                       tmisc.parse_msg(t_res.ecdh_info,
                                                       xmrtypes.EcdhTuple()))

        else:
            cur_ecdh = xmrtypes.EcdhTuple(mask=crypto.NULL_KEY_ENC,
                                          amount=bytearray(
                                              crypto.NULL_KEY_ENC))
            cur_ecdh.amount[0:8] = bytearray(t_res.ecdh_info)
            self.ct.tx_out_ecdh.append(cur_ecdh)

        rsig_buff = None
        if t_res.rsig_data:
            tsig_data = t_res.rsig_data

            if tsig_data.rsig and len(tsig_data.rsig) > 0:
                rsig_buff = tsig_data.rsig
            elif tsig_data.rsig_parts and len(tsig_data.rsig_parts) > 0:
                rsig_buff = b"".join(tsig_data.rsig_parts)

            if self.client_version >= 1 and tsig_data.mask:
                logger.debug("MASK received")
                self.ct.rsig_gamma.append(tsig_data.mask)

        # Client1: send generated BP if offloading
        if self.client_version >= 1 and self._is_offloading():
            proc = await self._v1_offloading(idx, dst, t_res)
            if not proc:
                return  # no state advance

        else:
            rsig = None
            if rsig_buff and not self._is_req_bulletproof():
                rsig = await tmisc.parse_msg(rsig_buff, xmrtypes.RangeSig())
            elif rsig_buff:
                rsig = await tmisc.parse_msg(rsig_buff, xmrtypes.Bulletproof())
            else:
                return  # no state change

            await self._out_proc_range_proof(rsig)

        # batch state advance
        self.ct.cur_batch_idx += 1
        self.ct.cur_output_in_batch_idx = 0
Exemplo n.º 2
0
def ecdh_encdec(masked, receiver_sk=None, derivation=None, v2=False, enc=True, dest=None):
    """
    Elliptic Curve Diffie-Helman: encodes and decodes the amount b and mask a
    where C= aG + bH
    """
    rv = xmrtypes.EcdhTuple() if dest is None else dest
    if derivation is None:
        derivation = crypto.scalarmult(masked.senderPk, receiver_sk)

    if v2:
        amnt = masked.amount
        rv.mask = monero.commitment_mask(derivation)
        rv.amount = bytearray(32)
        crypto.encodeint_into(rv.amount, amnt)
        crypto.xor8(rv.amount, monero.ecdh_hash(derivation))
        rv.amount = crypto.decodeint(rv.amount)
        return rv

    else:
        amount_key_hash_single = crypto.hash_to_scalar(derivation)
        amount_key_hash_double = crypto.hash_to_scalar(
            crypto.encodeint(amount_key_hash_single)
        )

        sc_fnc = crypto.sc_add if enc else crypto.sc_sub
        rv.mask = sc_fnc(masked.mask, amount_key_hash_single)
        rv.amount = sc_fnc(masked.amount, amount_key_hash_double)
        return rv
Exemplo n.º 3
0
    async def _on_set_outputs_ack(self, 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_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_buff = None
        if t_res.rsig_data:
            tsig_data = t_res.rsig_data

            if tsig_data.rsig and len(tsig_data.rsig) > 0:
                rsig_buff = tsig_data.rsig
            elif tsig_data.rsig_parts and len(tsig_data.rsig_parts) > 0:
                rsig_buff = b"".join(tsig_data.rsig_parts)

        if rsig_buff and not self._is_req_bulletproof():
            rsig = await tmisc.parse_msg(rsig_buff, xmrtypes.RangeSig())
        elif rsig_buff:
            rsig = await tmisc.parse_msg(rsig_buff, xmrtypes.Bulletproof())
        else:
            return

        if self._is_req_bulletproof():
            rsig.V = []
            batch_size = self.ct.rsig_batches[self.ct.cur_batch_idx]
            for i in range(batch_size):
                commitment = self.ct.tx_out_pk[1 + self.ct.cur_output_idx -
                                               batch_size + i].mask
                commitment = crypto.scalarmult(crypto.decodepoint(commitment),
                                               crypto.sc_inv_eight())
                rsig.V.append(crypto.encodepoint(commitment))

        self.ct.tx_out_rsigs.append(rsig)

        # Rsig verification
        try:
            if not ring_ct.ver_range(
                    C=crypto.decodepoint(self.ct.tx_out_pk[-1].mask),
                    rsig=rsig,
                    use_bulletproof=self._is_req_bulletproof(),
            ):
                logger.warning("Rsing not valid")

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

        self.ct.cur_batch_idx += 1
        self.ct.cur_output_in_batch_idx = 0
Exemplo n.º 4
0
def ecdh_decode(masked, receiver_sk=None, derivation=None):
    """
    Elliptic Curve Diffie-Helman: encodes and decodes the amount b and mask a
    where C= aG + bH
    """
    rv = xmrtypes.EcdhTuple()

    if derivation is None:
        derivation = crypto.scalarmult(masked.senderPk, receiver_sk)

    sharedSec1 = crypto.hash_to_scalar(derivation)
    sharedSec2 = crypto.hash_to_scalar(crypto.encodeint(sharedSec1))

    rv.mask = crypto.sc_sub(masked.mask, sharedSec1)
    rv.amount = crypto.sc_sub(masked.amount, sharedSec2)
    return rv
Exemplo n.º 5
0
def ecdh_encode(unmasked, receiver_pk=None, derivation=None, v2=False, dest=None):
    """
    Elliptic Curve Diffie-Helman: encodes and decodes the amount b and mask a
    where C= aG + bH
    :param unmasked:
    :param receiver_pk:
    :param derivation:
    :return:
    """
    rv = xmrtypes.EcdhTuple() if dest is None else dest
    if derivation is None:
        esk = crypto.random_scalar()
        rv.senderPk = crypto.scalarmult_base(esk)
        derivation = crypto.encodepoint(crypto.scalarmult(receiver_pk, esk))

    return ecdh_encdec(unmasked, None, derivation, v2=v2, enc=True, dest=rv)
Exemplo n.º 6
0
def ecdh_encode(unmasked, receiver_pk=None, derivation=None):
    """
    Elliptic Curve Diffie-Helman: encodes and decodes the amount b and mask a
    where C= aG + bH
    :param unmasked:
    :param receiver_pk:
    :param derivation:
    :return:
    """
    rv = xmrtypes.EcdhTuple()
    if derivation is None:
        esk = crypto.random_scalar()
        rv.senderPk = crypto.scalarmult_base(esk)
        derivation = crypto.encodepoint(crypto.scalarmult(receiver_pk, esk))

    sec1 = crypto.hash_to_scalar(derivation)
    sec2 = crypto.hash_to_scalar(crypto.encodeint(sec1))

    rv.mask = crypto.sc_add(unmasked.mask, sec1)
    rv.amount = crypto.sc_add(unmasked.amount, sec2)
    return rv
Exemplo n.º 7
0
    async def gen_rct_header(self, destinations, outamounts):
        """
        Initializes RV RctSig structure, processes outputs, computes range proofs, ecdh info masking.
        Common to gen_rct and gen_rct_simple.

        :param destinations:
        :param outamounts:
        :return:
        """
        rv = xmrtypes.RctSig()
        rv.p = xmrtypes.RctSigPrunable()

        rv.message = self.tx_prefix_hash
        rv.outPk = [None] * len(destinations)

        if self.use_bulletproof:
            rv.p.bulletproofs = [None] * len(destinations)
        else:
            rv.p.rangeSigs = [None] * len(destinations)
        rv.ecdhInfo = [None] * len(destinations)

        # Output processing
        sumout = crypto.sc_0()
        out_sk = [None] * len(destinations)
        for idx in range(len(destinations)):
            rv.outPk[idx] = xmrtypes.CtKey(dest=crypto.encodepoint(destinations[idx]))
            C, mask, rsig = None, 0, None

            # Rangeproof
            if self.use_bulletproof:
                raise ValueError("Bulletproof not yet supported")

            else:
                C, mask, rsig = ring_ct.prove_range(outamounts[idx])
                rv.p.rangeSigs[idx] = rsig
                if __debug__:
                    assert ring_ct.ver_range(C, rsig)
                    assert crypto.point_eq(
                        C,
                        crypto.point_add(
                            crypto.scalarmult_base(mask),
                            crypto.scalarmult_h(outamounts[idx]),
                        ),
                    )

            # Mask sum
            rv.outPk[idx].mask = crypto.encodepoint(C)
            sumout = crypto.sc_add(sumout, mask)
            out_sk[idx] = xmrtypes.CtKey(mask=mask)

            # ECDH masking
            amount_key = crypto.encodeint(self.output_secrets[idx][0])
            rv.ecdhInfo[idx] = xmrtypes.EcdhTuple(
                mask=mask, amount=crypto.sc_init(outamounts[idx])
            )
            rv.ecdhInfo[idx] = ring_ct.ecdh_encode(
                rv.ecdhInfo[idx], derivation=amount_key
            )
            monero.recode_ecdh(rv.ecdhInfo[idx], encode=True)

        return rv, sumout, out_sk
Exemplo n.º 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