Exemplo n.º 1
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)
Exemplo n.º 2
0
def compute_tx_key(spend_key_private,
                   tx_prefix_hash,
                   salt=None,
                   rand_mult=None):
    """

    :param spend_key_private:
    :param tx_prefix_hash:
    :param salt:
    :param rand_mult:
    :return:
    """
    if not salt:
        salt = crypto.random_bytes(32)

    if not rand_mult:
        rand_mult_num = crypto.random_scalar()
        rand_mult = crypto.encodeint(rand_mult_num)
    else:
        rand_mult_num = crypto.decodeint(rand_mult)

    rand_inp = crypto.sc_add(spend_key_private, rand_mult_num)
    passwd = crypto.keccak_2hash(crypto.encodeint(rand_inp) + tx_prefix_hash)
    tx_key = crypto.compute_hmac(salt, passwd)
    # tx_key = crypto.pbkdf2(passwd, salt, count=100)
    return tx_key, salt, rand_mult
Exemplo n.º 3
0
    async def set_input(self, src_entr):
        """
        :param src_entr:
        :type src_entr: xmrtypes.TxSourceEntry
        :return:
        """
        self.inp_idx += 1
        if src_entr.real_output >= len(src_entr.outputs):
            raise ValueError(
                "real_output index %s bigger than output_keys.size()"
                % (src_entr.real_output, len(src_entr.outputs))
            )
        self.summary_inputs_money += src_entr.amount

        out_key = crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].dest)
        tx_key = crypto.decodepoint(src_entr.real_out_tx_key)
        additional_keys = [
            crypto.decodepoint(x) for x in src_entr.real_out_additional_tx_keys
        ]

        secs = monero.generate_key_image_helper(
            self.trezor.creds,
            self.subaddresses,
            out_key,
            tx_key,
            additional_keys,
            src_entr.real_output_in_tx_index,
        )
        xi, ki, di = secs
        self.input_secrets.append((xi,))
        self.input_rcts.append(src_entr.rct)

        # Construct tx.vin
        vini = xmrtypes.TxinToKey(
            amount=src_entr.amount, k_image=crypto.encodepoint(ki)
        )
        vini.key_offsets = [x[0] for x in src_entr.outputs]
        vini.key_offsets = monero.absolute_output_offsets_to_relative(vini.key_offsets)
        self.tx.vin.append(vini)

        # HMAC(T_in,i || vin_i)
        kwriter = monero.get_keccak_writer()
        ar = xmrserialize.Archive(kwriter, True)
        await ar.message(src_entr, xmrtypes.TxSourceEntry)
        await ar.message(vini, xmrtypes.TxinToKey)

        hmac_key_vini = crypto.keccak_hash(
            self.key_hmac + b"txin" + xmrserialize.dump_uvarint_b(self.inp_idx)
        )
        hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest())

        return vini, hmac_vini
Exemplo n.º 4
0
    async def gen_hmac_tsxdest(self, dst_entr, idx):
        """
        Generates HMAC for TxDestinationEntry[i]
        :param dst_entr:
        :param idx:
        :return:
        """
        from monero_glue.xmr.sub.keccak_hasher import get_keccak_writer
        from monero_serialize import xmrserialize
        from monero_serialize.xmrtypes import TxDestinationEntry

        kwriter = get_keccak_writer()
        ar = xmrserialize.Archive(kwriter, True)
        await ar.message(dst_entr, TxDestinationEntry)

        hmac_key = self.hmac_key_txdst(idx)
        hmac_tsxdest = crypto.compute_hmac(hmac_key, kwriter.get_digest())
        return hmac_tsxdest
Exemplo n.º 5
0
    async def gen_hmac_vouti(self, dst_entr, tx_out, idx):
        """
        Generates HMAC for (TxDestinationEntry[i] || tx.vout[i])
        :param dst_entr:
        :param tx_out:
        :param idx:
        :return:
        """
        from monero_glue.xmr.sub.keccak_hasher import get_keccak_writer
        from monero_serialize import xmrserialize
        from monero_serialize.xmrtypes import TxDestinationEntry
        from monero_serialize.xmrtypes import TxOut

        kwriter = get_keccak_writer()
        ar = xmrserialize.Archive(kwriter, True)
        await ar.message(dst_entr, TxDestinationEntry)
        await ar.message(tx_out, TxOut)

        hmac_key_vouti = self.hmac_key_txout(idx)
        hmac_vouti = crypto.compute_hmac(hmac_key_vouti, kwriter.get_digest())
        return hmac_vouti
Exemplo n.º 6
0
    async def gen_hmac_vini(self, src_entr, vini, idx):
        """
        Computes hmac (TxSourceEntry[i] || tx.vin[i])
        :param src_entr:
        :param vini:
        :param idx:
        :return:
        """
        from monero_glue.xmr.sub.keccak_hasher import get_keccak_writer
        from monero_serialize import xmrserialize
        from monero_serialize.xmrtypes import TxSourceEntry
        from monero_serialize.xmrtypes import TxinToKey

        kwriter = get_keccak_writer()
        ar = xmrserialize.Archive(kwriter, True)
        await ar.message(src_entr, TxSourceEntry)
        await ar.message(vini, TxinToKey)

        hmac_key_vini = self.hmac_key_txin(idx)
        hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest())
        return hmac_vini
Exemplo n.º 7
0
    async def store_cdata(self, cdata, signed_tx, tx, transfers):
        """
        Stores transaction data for later usage.
            - cdata.enc_salt1, cdata.enc_salt2, cdata.enc_keys
            - tx_keys are AEAD protected, key derived from spend key - only token can open.
            - construction data for further proofs.

        :param cdata:
        :param signed_tx:
        :param tx:
        :param transfers:
        :return:
        """
        hash = cdata.tx_prefix_hash
        prefix = binascii.hexlify(hash[:12])

        tx_key_salt = crypto.random_bytes(32)
        tx_key_inp = hash + crypto.encodeint(self.priv_view)
        tx_view_key = crypto.pbkdf2(tx_key_inp, tx_key_salt, 2048)

        unsigned_data = xmrtypes.UnsignedTxSet()
        unsigned_data.txes = [tx]
        unsigned_data.transfers = transfers if transfers is not None else []

        writer = xmrserialize.MemoryReaderWriter()
        ar = xmrboost.Archive(writer, True)
        await ar.root()
        await ar.message(unsigned_data)

        unsigned_key = crypto.keccak_2hash(b'unsigned;' + tx_view_key)
        ciphertext = chacha_poly.encrypt_pack(unsigned_key,
                                              bytes(writer.get_buffer()))

        # Serialize signed transaction
        writer = xmrserialize.MemoryReaderWriter()
        ar = xmrserialize.Archive(writer, True)
        await ar.root()
        await ar.message(signed_tx)
        signed_tx_bytes = writer.get_buffer()
        signed_tx_hmac_key = crypto.keccak_2hash(b'hmac;' + tx_view_key)
        signed_tx_hmac = crypto.compute_hmac(signed_tx_hmac_key,
                                             signed_tx_bytes)

        try:
            js = {
                "time": int(time.time()),
                "hash": binascii.hexlify(hash).decode("ascii"),
                "enc_salt1": binascii.hexlify(cdata.enc_salt1).decode("ascii"),
                "enc_salt2": binascii.hexlify(cdata.enc_salt2).decode("ascii"),
                "tx_keys": binascii.hexlify(cdata.enc_keys).decode("ascii"),
                "unsigned_data": binascii.hexlify(ciphertext).decode("ascii"),
                "tx_salt": binascii.hexlify(tx_key_salt).decode("ascii"),
                "tx_signed": binascii.hexlify(signed_tx_bytes).decode("ascii"),
                "tx_signed_hmac":
                binascii.hexlify(signed_tx_hmac).decode("ascii"),
            }

            with open("transaction_%s.json" % prefix.decode("ascii"),
                      "w") as fh:
                json.dump(js, fh, indent=2)
                fh.write("\n")

        except Exception as e:
            self.trace_logger.log(e)
            print("Unable to save transaction data for transaction %s" %
                  binascii.hexlify(hash).decode("ascii"))
Exemplo n.º 8
0
    async def set_input(self, src_entr):
        """
        Sets UTXO one by one.
        Computes spending secret key, key image. tx.vin[i] + HMAC, Pedersen commitment on amount.

        If number of inputs is small, in-memory mode is used = alpha, pseudo_outs are kept in the Trezor.
        Otherwise pseudo_outs are offloaded with HMAC, alpha is offloaded encrypted under Chacha20Poly1305()
        with key derived for exactly this purpose.

        :param src_entr:
        :type src_entr: monero_glue.xmr.serialize_messages.tx_construct.TxSourceEntry
        :return:
        """
        from monero_glue.messages.MoneroTransactionSetInputAck import (
            MoneroTransactionSetInputAck
        )
        from monero_glue.xmr.enc import chacha_poly
        from monero_glue.xmr.sub import tsx_helper
        from monero_serialize.xmrtypes import TxinToKey

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

        await self.trezor.iface.transaction_step(
            self.STEP_INP, self.inp_idx, self.num_inputs()
        )

        if self.inp_idx >= self.num_inputs():
            raise ValueError("Too many inputs")
        if src_entr.real_output >= len(src_entr.outputs):
            raise ValueError(
                "real_output index %s bigger than output_keys.size() %s"
                % (src_entr.real_output, len(src_entr.outputs))
            )
        self.summary_inputs_money += src_entr.amount

        # Secrets derivation
        out_key = crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].dest)
        tx_key = crypto.decodepoint(src_entr.real_out_tx_key)
        additional_keys = [
            crypto.decodepoint(x) for x in src_entr.real_out_additional_tx_keys
        ]

        secs = monero.generate_key_image_helper(
            self.creds,
            self.subaddresses,
            out_key,
            tx_key,
            additional_keys,
            src_entr.real_output_in_tx_index,
        )
        xi, ki, di = secs

        # Construct tx.vin
        ki_real = src_entr.multisig_kLRki.ki if self.multi_sig else ki
        vini = TxinToKey(amount=src_entr.amount, k_image=crypto.encodepoint(ki_real))
        vini.key_offsets = [x[0] for x in src_entr.outputs]
        vini.key_offsets = tsx_helper.absolute_output_offsets_to_relative(
            vini.key_offsets
        )

        if src_entr.rct:
            vini.amount = 0

        if self.in_memory():
            self.tx.vin.append(vini)

        # HMAC(T_in,i || vin_i)
        hmac_vini = await self.gen_hmac_vini(src_entr, vini, self.inp_idx)

        # PseudoOuts commitment, alphas stored to state
        pseudo_out = None
        pseudo_out_hmac = None
        alpha_enc = None
        spend_enc = None

        if self.use_simple_rct:
            alpha, pseudo_out = await self.commitment(src_entr.amount)
            pseudo_out = crypto.encodepoint(pseudo_out)

            # In full version the alpha is encrypted and passed back for storage
            if self.in_memory():
                self.input_alphas.append(alpha)
                self.input_pseudo_outs.append(pseudo_out)
            else:
                pseudo_out_hmac = crypto.compute_hmac(
                    self.hmac_key_txin_comm(self.inp_idx), pseudo_out
                )
                alpha_enc = chacha_poly.encrypt_pack(
                    self.enc_key_txin_alpha(self.inp_idx), crypto.encodeint(alpha)
                )

        if self.many_inputs():
            spend_enc = chacha_poly.encrypt_pack(
                self.enc_key_spend(self.inp_idx), crypto.encodeint(xi)
            )
        else:
            self.input_secrets.append(xi)

        # All inputs done?
        if self.inp_idx + 1 == self.num_inputs():
            await self.tsx_inputs_done()

        return MoneroTransactionSetInputAck(
            vini=await misc.dump_msg(vini, preallocate=64),
            vini_hmac=hmac_vini,
            pseudo_out=pseudo_out,
            pseudo_out_hmac=pseudo_out_hmac,
            alpha_enc=alpha_enc,
            spend_enc=spend_enc,
        )
Exemplo n.º 9
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,
        )
Exemplo n.º 10
0
 def _compute_ki_enc_key_host(view_key_private, prefix, salt):
     passwd = crypto.keccak_2hash(
         crypto.encodeint(view_key_private) + prefix)
     enc_key = crypto.compute_hmac(salt, passwd)
     return enc_key
Exemplo n.º 11
0
 def _compute_tx_key_host(view_key_private, tx_prefix_hash, salt):
     passwd = crypto.keccak_2hash(
         crypto.encodeint(view_key_private) + tx_prefix_hash)
     tx_key = crypto.compute_hmac(salt, passwd)
     return tx_key